[
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "content": "---\nname: Bug report(默认)\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: funnyAnt\n\n---\n\n**1、bug描述** \n 简单清晰描述下bug现象.\n\n**2、版本号(非常重要)**\n v 1.6.*.*\n\n**3、相关表的配置信息**\n  schema.xml (需包含表的配置信息，mysql的连接驱动是JDBC还是native方式)\n  rule.xml (涉及到的路由函数)\n  server.xml(可选)\n\n**4、操作步骤**\n1.  '...'\n2.  '....'\n3.  ...\n\n**5、期望结果**\nA clear and concise description of what you expected to happen.\n\n**6、实际结果**\nA clear and concise description of what  actually happened.\n\n**7、额外信息**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/new_feture.md",
    "content": "---\nname: 功能建议\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: funnyAnt, junwen12221\n\n---\n\n**问题描述**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**描述您想要的结果及解决方案**\nA clear and concise description of what you want to happen.\n\n**额外内容**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/others.md",
    "content": "---\nname: 其他（如使用方法，配置参数等）\nabout: Describe this issue template's purpose here.\ntitle: ''\nlabels: help wanted\nassignees: ''\n\n---\n\n**描述你使用过程中遇到的问题: **\n"
  },
  {
    "path": ".gitignore",
    "content": "### Eclipse template\n*.pydevproject\n.metadata\n.gradle\nbin/\ntmp/\n*.tmp\n*.bak\n*.swp\n*~.nib\nlocal.properties\n.settings/\n.loadpath\n\n# Eclipse Core\n.project\n\n# External tool builders\n.externalToolBuilders/\n\n# Locally stored \"Eclipse launch configurations\"\n*.launch\n\n# CDT-specific\n.cproject\n\n# JDT-specific (Eclipse Java Development Tools)\n.classpath\n\n# Java annotation processor (APT)\n.factorypath\n\n# PDT-specific\n.buildpath\n\n# sbteclipse plugin\n.target\n\n# TeXlipse plugin\n.texlipse\n\n\n### JetBrains template\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion\n\n*.iml\n\n## Directory-based project format:\n.idea/\n# if you remove the above rule, at least ignore the following:\n\n# User-specific stuff:\n# .idea/workspace.xml\n# .idea/tasks.xml\n# .idea/dictionaries\n\n# Sensitive or high-churn files:\n# .idea/dataSources.ids\n# .idea/dataSources.xml\n# .idea/sqlDataSources.xml\n# .idea/dynamic.xml\n# .idea/uiDesigner.xml\n\n# Gradle:\n# .idea/gradle.xml\n# .idea/libraries\n\n# Mongo Explorer plugin:\n# .idea/mongoSettings.xml\n\n## File-based project format:\n*.ipr\n*.iws\n\n## Plugin-specific files:\n\n# IntelliJ\n/out/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\n\n\n### Java template\n*.class\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.jar\n*.war\n*.ear\n\n# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n\n\n/project/project/\n/project/target/\n/project/activator-*\n/logs/\n/RUNNING_PID\n.DS_Store\n/target/\n/conf/"
  },
  {
    "path": ".travis.yml",
    "content": "language: java\njdk:\n  - openjdk7\n  - oraclejdk7\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n    // Use IntelliSense to learn about possible attributes.\n    // Hover to view descriptions of existing attributes.\n    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"type\": \"java\",\n            \"name\": \"Debug (Launch)-MycatStartup<Mycat-server>\",\n            \"request\": \"launch\",\n            \"mainClass\": \"io.mycat.MycatStartup\",\n            \"projectName\": \"Mycat-server\",\n            \"vmArgs\": \"-server -Xms2G -Xmx2G -XX:MaxMetaspaceSize=64M  -XX:+AggressiveOpts -XX:MaxDirectMemorySize=2G -DMYCAT_HOME=${workspaceFolder} \",\n            \"args\": \"$\"\n        },\n    ]\n}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"java.format.settings.url\": \"${workspaceFolder}/eclipse-java-google-style.xml\"\n}"
  },
  {
    "path": "Dockerfile",
    "content": "FROM docker.io/adoptopenjdk/openjdk8:latest\n\nADD http://dl.mycat.io/1.6.6.1/Mycat-server-1.6.6.1-release-20180908155252-linux.tar.gz /usr/local\nRUN cd /usr/local && tar -zxvf Mycat-server-1.6.6.1-release-20180908155252-linux.tar.gz && ls -lna\n\nVOLUME /usr/local/mycat/conf\nVOLUME /usr/local/mycat/logs\n\nEXPOSE 8066 9066\n\nCMD [\"/usr/local/mycat/bin/mycat\", \"console\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    {description}\n    Copyright (C) {year}  {fullname}\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  {signature of Ty Coon}, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "README.md",
    "content": "\n# Mycat1\n\n官网:  http://mycatone.top\n\n\n[gitee](https://gitee.com/MycatOne/Mycat-Server)\n[github](https://github.com/MyCATApache/Mycat-Server)\n\n提交代码,可以开issue,写清楚代码改动或者联系qq:1019100252\n\nmycat1.6权威指南\n[yuque](https://www.yuque.com/books/share/0576de75-ffc4-4c34-8586-952ae4636944)\n[pdf](http://mycat.org.cn/document/mycat-definitive-guide.pdf)\n\n\n客户端连接配置 \n[gitee](https://gitee.com/MycatOne/Mycat-Server/wiki/%E5%BC%80%E5%8F%91%E7%AF%87%EF%BC%9A1.0-%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%BF%9E%E6%8E%A5%E9%85%8D%E7%BD%AE)\n[github](https://github.com/MyCATApache/Mycat-Server/wiki/%E5%BC%80%E5%8F%91%E7%AF%87%EF%BC%9A1.0-%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%BF%9E%E6%8E%A5%E9%85%8D%E7%BD%AE)\n\n[![Stargazers over time](https://starchart.cc/MyCATApache/Mycat-Server.svg)](https://starchart.cc/MyCATApache/Mycat-Server)\n  \n\nMycat志愿者开发群:332702697,106088787\n\n[![GitHub issues](https://img.shields.io/github/issues/MyCATApache/Mycat-Server.svg)](https://github.com/MyCATApache/Mycat-Server/issues)\n[![GitHub forks](https://img.shields.io/github/forks/MyCATApache/Mycat-Server.svg)](https://github.com/MyCATApache/Mycat-Server/network)\n[![GitHub stars](https://img.shields.io/github/stars/MyCATApache/Mycat-Server.svg)](https://github.com/MyCATApache/Mycat-Server/stargazers)\n[![MyCAT](https://img.shields.io/badge/MyCAT-%E2%9D%A4%EF%B8%8F-%23ff69b4.svg)](http://mycat.io/)\n\nMyCAT 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.\n\nMycat’s 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.\n\n2020年1月1日合拼了一个PR,优化PartionByLong的分片算法,数据不均衡的问题,所以该分片算法与此前的PartionByLong的数据分布不一致,即1.675之后与之前的版本不兼容\nMyCAT1.6不支持一个SQL包含多个语句\n\n旧Mycat升级fastjson，把pom.xml中fastjson的版本更改即可\n\n1.6的bug:\n批处理插入,多语句,堆外合拼,请大家要避开这些功能\n\n全局序列号语法\n\n```sql\nINSERT INTO `travelrecord` (`id`,user_id) VALUES ('next value for MYCATSEQ_GLOBAL',\"xxx\");\n```\n\n更新Druid 1.1.10版本的分支独立维护在\n[gitee](https://gitee.com/MycatOne/Mycat-Server/tree/1.6.6-druid)\n[github](https://github.com/MyCATApache/Mycat-Server/tree/1.6.6-druid)\n\n\n\n* Getting Started \n* [gitee](https://gitee.com/mirrors_MyCATApache/Mycat-doc/tree/master/en)\n* [github](https://github.com/MyCATApache/Mycat-doc/tree/master/en)\n\n* 尝试 MyCAT \n* [gitee](https://gitee.com/mirrors_MyCATApache/Mycat-doc/tree/master/%E5%85%A5%E9%97%A8%E6%8C%87%E5%8D%97)\n* [github](https://github.com/MyCATApache/Mycat-doc/tree/master/%E5%85%A5%E9%97%A8%E6%8C%87%E5%8D%97)\n\n## Features\n\n* Supports SQL 92 standard\n* Supports MySQL cluster, used as a Proxy\n* Supports JDBC connection with ORACLE, DB2, SQL Server, simulated as normal MySQL Server connection\n* Supports MySQL cluster, percona cluster or mariadb cluster, providing high availability of data fragmentation clusters\n* Supports automatic failover and high availability\n* Supports separation of read and write, dual-master with multi-slave, single-master with multi-master of MySQL model\n* Supports global table, automatically fragment data into multiple nodes for efficient relational query\n* Supports the unique fragmentation strategy based on ER-relation for efficient relational query\n* Supports multiple platforms, easy deployment and implementation\n\n## Advantage\n\n* Based on Alibaba's open-source project Cobar [github](https://github.com/alibaba/cobar),[gitee](https://gitee.com/mirrors_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.\n* Extensively drawing on the best open-source projects and innovative ideas, which are integrated into the Mycat’s gene, make MyCAT be ahead of the other current similar open-source projects, even beyond some commercial products.\n* 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.\n* MyCAT does not rely on any commercial company. It’s unlike some open-source projects whose important features is enclosed in its commercial products and making open-source projects like a decoration.\n\n## Roadmap\n\n* On the basis of MySQL’s 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.\n* 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\n* Achieve a more comprehensive monitoring and management\n* Integrated with HDFS, provide SQL commands, load databases into HDFS for rapid analysis\n* Integrated excellent open-source reporting tools to make MyCAT have data analysis capability \n\n## Download\n\nThere are some compiled binary installation packages in Mycat-download project on github at  [Mycat-download](http://dl.mycat.org.cn/1.6.7.6/).\n\n## Document\n\nThere are some documents in Mycat-doc project on github at [Mycat-doc]\n\n\n[gitee](https://gitee.com/mirrors_MyCATApache/Mycat-doc)\n\n[github](https://github.com/MyCATApache/Mycat-doc)\n\n\nMycat 简单demo，具体参考Mycat权威指南\n\n\n# Mycat前世今生\n\n2013年阿里的Cobar在社区使用过程中发现存在一些比较严重的问题，及其使用限制，经过Mycat发起人第一次改良，第一代改良版——Mycat诞生。 Mycat开源以后，一些Cobar的用户参与了Mycat的开发，最终Mycat发展成为一个由众多软件公司的实力派架构师和资深开发人员维护的社区型开源软件。\n\n2014年Mycat首次在上海的《中华架构师》大会上对外宣讲，更多的人参与进来，随后越来越多的项目采用了Mycat。\n\n2015年5月，由核心参与者们一起编写的第一本官方权威指南《Mycat权威指南》电子版发布，累计超过500本，成为开源项目中的首创。\n\n2015年10月为止，Mycat项目总共有16个Committer。\n\n截至2015年11月，超过300个项目采用Mycat，涵盖银行、电信、电子商务、物流、移动应用、O2O的众多领域和公司。\n\n截至2015年12月，超过4000名用户加群或研究讨论或测试或使用Mycat。\n\nMycat是基于开源cobar演变而来，我们对cobar的代码进行了彻底的重构，使用NIO重构了网络模块，并且优化了Buffer内核，增强了聚合，Join等基本特性，同时兼容绝大多数数据库成为通用的数据库中间件。1.4 版本以后 完全的脱离基本cobar内核，结合Mycat集群管理、自动扩容、智能优化，成为高性能的中间件。我们致力于开发高性能数据库中间而努力。永不收费，永不闭源，持续推动开源社区的发展。\n\nMycat吸引和聚集了一大批业内大数据和云计算方面的资深工程师，Mycat的发展壮大基于开源社区志愿者的持续努力，感谢社区志愿者的努力让Mycat更加强大，同时我们也欢迎社区更多的志愿者，特别是公司能够参与进来，参与Mycat的开发，一起推动社区的发展，为社区提供更好的开源中间件。\n\nMycat还不够强大，Mycat还有很多不足，欢迎社区志愿者的持续优化改进。\n\n#  关键特性\n支持SQL92标准\n\n遵守Mysql原生协议，跨语言，跨平台，跨数据库的通用中间件代理。\n\n基于心跳的自动故障切换，支持读写分离，支持MySQL主从，以及galera cluster集群。\n\n支持Galera for MySQL集群，Percona Cluster或者MariaDB cluster\n\n基于Nio实现，有效管理线程，高并发问题。\n\n支持数据的多片自动路由与聚合，支持sum,count,max等常用的聚合函数。\n\n支持单库内部任意join，支持跨库2表join，甚至基于caltlet的多表join。\n\n支持通过全局表，ER关系的分片策略，实现了高效的多表join查询。\n\n支持多租户方案。\n\n支持分布式事务（弱xa）。\n\n支持全局序列号，解决分布式下的主键生成问题。\n\n分片规则丰富，插件化开发，易于扩展。\n\n强大的web，命令行监控。\n\n支持前端作为mysq通用代理，后端JDBC方式支持Oracle、DB2、SQL Server 、 mongodb 、巨杉。\n\n支持密码加密\n\n支持服务降级\n\n支持IP白名单\n\n支持SQL黑名单、sql注入攻击拦截\n\n支持分表（1.6）\n\n集群基于ZooKeeper管理，在线升级，扩容，智能优化，大数据处理（2.0开发版）。\n\n\n# Mycat安装与使用\n\n## 下载：\n具体下载哪个版本以发布为准，推荐1.67.\n\n## 安装：\n下载的文件直接解压即可。\n\n## 运行：\n### linux：\n   ./mycat start 启动\n\n   ./mycat stop 停止\n\n   ./mycat console 前台运行\n\n   ./mycat install 添加到系统自动启动（暂未实现）\n\n   ./mycat remove 取消随系统自动启动（暂未实现）\n\n   ./mycat restart 重启服务\n\n   ./mycat pause 暂停\n\n   ./mycat status 查看启动状态\n\n### win：\n直接运行startup_nowrap.bat，如果出现闪退，在cmd 命令行运行，查看出错原因。 \n\n## 内存配置：\n启动前，一般需要修改JVM配置参数，打开conf/wrapper.conf文件，如下行的内容为2G和2048，可根据本机配置情况修改为512M或其它值。\n以下配置跟jvm参数完全一致，可以根据自己的jvm参数调整。\n\nJava Additional Parameters\n\nwrapper.java.additional.1=\n\nwrapper.java.additional.1=-DMYCAT_HOME=.\n\nwrapper.java.additional.2=-server\n\n#wrapper.java.additional.3=-XX:MaxPermSize=64M\n\nwrapper.java.additional.4=-XX:+AggressiveOpts\n\nwrapper.java.additional.5=-XX:MaxDirectMemorySize=100m\n\nwrapper.java.additional.6=-Dcom.sun.management.jmxremote\n\nwrapper.java.additional.7=-Dcom.sun.management.jmxremote.port=1984\n\nwrapper.java.additional.8=-Dcom.sun.management.jmxremote.authenticate=false\n\nwrapper.java.additional.9=-Dcom.sun.management.jmxremote.ssl=false\n\nwrapper.java.additional.10=-Xmx100m\n\nwrapper.java.additional.11=-Xms100m\n\nwrapper.java.additional.12=-XX:+UseParNewGC\n\nwrapper.java.additional.13=-XX:+UseConcMarkSweepGC\n\nwrapper.java.additional.14=-XX:+UseCMSCompactAtFullCollection\n\nwrapper.java.additional.15=-XX:CMSFullGCsBeforeCompaction=0\n\nwrapper.java.additional.16=-XX:CMSInitiatingOccupancyFraction=70\n\n\n以下配置作废：\n\nwrapper.java.initmemory=3\n\nwrapper.java.maxmemory=64\n\n### Mycat连接测试：\n测试mycat与测试mysql完全一致，mysql怎么连接，mycat就怎么连接。\n\n推荐先采用命令行测试：\n\nmysql -uroot -proot -P8066 -h127.0.0.1\n\n如果采用工具连接，1.4,1.3目前部分工具无法连接，会提示database not selected，建议采用高版本，navicat测试。1.5已经修复了部分工具连接。\n\n\n# Mycat配置入门\n\n## 配置：\n--bin  启动目录\n\n--conf 配置目录存放配置文件：\n\n      --server.xml：是Mycat服务器参数调整和用户授权的配置文件。\n    \n      --schema.xml：是逻辑库定义和表以及分片定义的配置文件。\n    \n      --rule.xml：  是分片规则的配置文件，分片规则的具体一些参数信息单独存放为文件，也在这个目录下，配置文件修改需要重启MyCAT。\n    \n      --log4j.xml： 日志存放在logs/log中，每天一个文件，日志的配置是在conf/log4j.xml中，根据自己的需要可以调整输出级别为debug                           debug级别下，会输出更多的信息，方便排查问题。\n    \n      --autopartition-long.txt,partition-hash-int.txt,sequence_conf.properties， sequence_db_conf.properties 分片相关的id分片规则配置文件\n    \n      --lib\t    MyCAT自身的jar包或依赖的jar包的存放目录。\n    \n      --logs        MyCAT日志的存放目录。日志存放在logs/log中，每天一个文件\n\n下面图片描述了Mycat最重要的3大配置文件：\n<p>\n\t<img src=\"http://songwie.com/attached/image/20160205/20160205164558_154.png\" alt=\"\">\n</p>\n\n## 逻辑库配置：\n### 配置server.xml\n添加两个mycat逻辑库：user,pay  \nsystem 参数是所有的mycat参数配置，比如添加解析器：defaultSqlParser，其他类推  \nuser 是用户参数。\n\n\t<system>\n\t\n\t\t<property name=\"defaultSqlParser\">druidparser</property>\n\t\n\t</system>\n\t\n\t<user name=\"mycat\">\n\t\n\t\t<property name=\"password\">mycat</property>\n\t\n\t\t<property name=\"schemas\">user,pay</property>\n\t\n\t</user>\n\n### 编辑schema.xml\n修改dataHost和schema对应的连接信息，user,pay 垂直切分后的配置如下所示：\n\nschema 是实际逻辑库的配置，user，pay分别对应两个逻辑库，多个schema代表多个逻辑库。\n\ndataNode是逻辑库对应的分片，如果配置多个分片只需要多个dataNode即可。\n\ndataHost是实际的物理库配置地址，可以配置多主主从等其他配置，多个dataHost代表分片对应的物理库地址，下面的writeHost、readHost代表该分片是否配置多写，主从，读写分离等高级特性。\n\n以下例子配置了两个writeHost为主从。\n\n    <schema name=\"user\" checkSQLschema=\"false\" sqlMaxLimit=\"100\" dataNode=\"user\" />\n    <schema name=\"pay\"  checkSQLschema=\"false\" sqlMaxLimit=\"100\" dataNode=\"pay\" >\n       <table name=\"order\" dataNode=\"pay1,pay2\" rule=\"rule1\"/>\n    </schema>\n    \n    <dataNode name=\"user\" dataHost=\"host\" database=\"user\" />\n    <dataNode name=\"pay1\" dataHost=\"host\" database=\"pay1\" />\n    <dataNode name=\"pay2\" dataHost=\"host\" database=\"pay2\" />\n    \n    <dataHost name=\"host\" maxCon=\"1000\" minCon=\"10\" balance=\"0\"\n       writeType=\"0\" dbType=\"mysql\" dbDriver=\"native\">\n       <heartbeat>select 1</heartbeat>\n       <!-- can have multi write hosts -->\n       <writeHost host=\"hostM1\" url=\"192.168.0.2:3306\" user=\"root\" password=\"root\" />\n       <writeHost host=\"hostM2\" url=\"192.168.0.3:3306\" user=\"root\" password=\"root\" />\n    </dataHost>\n\n\n​    \n\n# Mycat逻辑库、系统参数配置\n\n## 配置Mycat环境参数\n    <?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n    <!DOCTYPE mycat:server SYSTEM \"server.dtd\">\n    <mycat:server xmlns:mycat=\"http://org.opencloudb/\">\n       <system>\n    \t  <property name=\"defaultSqlParser\">druidparser</property>\n        </system> \n     </mycat:server>\n\n如例子中配置的所有的Mycat参数变量都是配置在server.xml 文件中，system标签下配置所有的参数，如果需要配置某个变量添加相应的配置即可，例如添加启动端口8066，默认为8066：\n\n       <property name=\"serverPort\">8066</property>\n\n其他所有变量类似。\n\n## 配置Mycat逻辑库与用户\n\n    <?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n    <!DOCTYPE mycat:server SYSTEM \"server.dtd\">\n    <mycat:server xmlns:mycat=\"http://org.opencloudb/\">\n    <user name=\"mycat\">\n    \t<property name=\"password\">mycat</property>\n    \t<property name=\"schemas\">TESTDB</property>\n    </user>\n     </mycat:server>\n\n\n如例子中配置的所有的Mycat连接的用户与逻辑库映射都是配置在server.xml 文件中，user标签下配置所有的参数，例如例子中配置了一个mycat用户供应用连接到mycat，同时mycat 在schema.xml中配置后了一个逻辑库TESTDB，配置好逻辑库与用户的映射关系。\n\n\n# 逻辑库、表分片配置\n\n## 配置逻辑库（schema）\n\nMycat作为一个中间件，实现mysql协议，那么对前端应用连接来说就是一个数据库，也就有数据库的配置，mycat的数据库配置是在schema.xml中配置，配置好后映射到server.xml里面的用户就可以了。\n\n\t<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\t<!DOCTYPE mycat:schema SYSTEM \"schema.dtd\">\n\t\n\t<mycat:schema  xmlns:mycat=\"http://org.opencloudb/\">\n\t  <schema name=\"TESTDB\" checkSQLschema=\"true\" sqlMaxLimit=\"100\" dataNode=\"dn1\">\n\t      <table name=\"t_user\" dataNode=\"dn1,dn2\" rule=\"sharding-by-mod2\"/>\n\t      <table name=\"ht_jy_login_log\" primaryKey=\"ID\" dataNode=\"dn1,dn2\" rule=\"sharding-by-date_jylog\"/>\n\t  </schema>\n\t  <dataNode name=\"dn1\" dataHost=\"localhost1\" database=\"mycat_node1\"/>\n\t  <dataNode name=\"dn2\" dataHost=\"localhost1\" database=\"mycat_node2\"/>\n\t  \n\t  <dataHost name=\"localhost1\" writeType=\"0\" switchType=\"1\" slaveThreshold=\"100\" balance=\"1\" dbType=\"mysql\" maxCon=\"10\" minCon=\"1\" dbDriver=\"native\">\n\t    <heartbeat>show status like 'wsrep%'</heartbeat>\n\t    <writeHost host=\"hostM1\" url=\"127.0.0.1:3306\" user=\"root\" password=\"root\" >\n\t    </writeHost>  \n\t  </dataHost>\n\t</mycat:schema >\n\n上面例子配置了一个逻辑库TESTDB，同时配置了t_user，ht_jy_login_log两个分片表。\n\n### 逻辑表配置\n\t      <table name=\"t_user\" dataNode=\"dn1,dn2\" rule=\"sharding-by-mod2\"/>\n\ntable 标签 是逻辑表的配置 其中 \n\nname代表表名，\n\ndataNode代表表对应的分片，\n\nMycat默认采用分库方式，也就是一个表映射到不同的库上，\n\nrule代表表要采用的数据切分方式，名称对应到rule.xml中的对应配置，如果要分片必须配置。\n\n\n## 配置分片（dataNode）\n\n\t  <dataNode name=\"dn1\" dataHost=\"localhost1\" database=\"mycat_node1\"/>\n\t  <dataNode name=\"dn2\" dataHost=\"localhost1\" database=\"mycat_node2\"/>\n\n表切分后需要配置映射到哪几个数据库中，Mycat的分片实际上就是库的别名，例如上面例子配置了两个分片dn1，dn2 分别对应到物理机映射dataHost\nlocalhost1 的两个库上。\n\n## 配置物理库分片映射（dataHost）\n\n\t  <dataHost name=\"localhost1\" writeType=\"0\" switchType=\"1\" slaveThreshold=\"100\" balance=\"1\" dbType=\"mysql\" maxCon=\"10\" minCon=\"1\" dbDriver=\"native\">\n\t    <heartbeat>show status like 'wsrep%'</heartbeat>\n\t    <writeHost host=\"hostM1\" url=\"127.0.0.1:3306\" user=\"root\" password=\"root\" >\n\t    </writeHost>  \n\t  </dataHost>\n\nMycat作为数据库代理需要逻辑库，逻辑用户，表切分后需要配置分片，分片也就需要映射到真实的物理主机上，至于是映射到一台还是一台的多个实例上，Mycat并不关心，只需要配置好映射即可，例如例子中：\n\n配置了一个名为localhost1的物理主机（dataHost）映射。\n\nheartbeat 标签代表Mycat需要对物理库心跳检测的语句，正常情况下生产案例可能配置主从，或者多写 或者单库，无论哪种情况Mycat都需要维持到数据库的数据源连接，因此需要定时检查后端连接可以性，心跳语句就是来作为心跳检测。\n\nwriteHost 此标签代表 一个逻辑主机（dataHost）对应的后端的物理主机映射，例如例子中写库hostM1 映射到127.0.0.1:3306。如果后端需要做读写分离或者多写 或者主从则通过配置 多个writeHost 或者readHost即可。\n\ndataHost 标签中的 writeType balance 等标签则是不同的策略，具体参考指南。\n\n# Mycat 表切分规则配置\n\n## 表切分规则\n\n\t<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\t<!DOCTYPE mycat:rule SYSTEM \"rule.dtd\">\n\t\n\t<mycat:rule  xmlns:mycat=\"http://org.opencloudb/\">\n\t  <tableRule name=\"sharding-by-hour\">\n\t    <rule>\n\t      <columns>createTime</columns>\n\t      <algorithm>sharding-by-hour</algorithm>\n\t    </rule>\n\t  </tableRule>\n\t  \n\t  <function name=\"sharding-by-hour\" class=\"org.opencloudb.route.function.LatestMonthPartion\">\n\t    <property name=\"splitOneDay\">24</property>\n\t  </function>\n\t   \n\t</mycat:rule >\n\n数据切分中作为表切分规则中最重要的配置，表的切分方式决定了数据切分后的性能好坏，因此也是最重要的配置。\n\n如上面例子配置了一个切分规则，名为sharding-by-hour 对应的切分方式（function ）是按日期切分，该配置中：\n\n### tableRule\n\nname 为schema.xml 中table 标签中对应的 rule=\"sharding-by-hour\" ,也就是配置表的分片规则，\n\ncolumns 是表的切分字段： createTime 创建日期。\n\nalgorithm 是规则对应的切分规则：映射到function 的name。\n\n\n### function \n\nfunction 配置是分片规则的配置。\n\nname 为切分规则的名称，名字任意取，但是需要与tableRule 中匹配。\n\nclass 是切分规则对应的切分类，写死，需要哪种规则则配置哪种，例如本例子是按小时分片：org.opencloudb.route.function.LatestMonthPartion\n\nproperty 标签是切分规则对应的不同属性，不同的切分规则配置不同。\n\n\n"
  },
  {
    "path": "README_Chinese.md",
    "content": "### Mycat介绍\n\n### 官网:[http://www.mycat.org.cn](http://www.mycat.org.cn)\n\n### github:[https://github.com/MyCATApache](https://github.com/MyCATApache)\n##### 入门: [zh-CN: https://github.com/MyCATApache/Mycat-doc/blob/master/history/MyCat_In_Action_%E4%B8%AD%E6%96%87%E7%89%88.doc] [English:https://github.com/MyCATApache/Mycat-doc/tree/master/en]\n\n什么是Mycat？简单的说，Mycat就是：\n\n*\t一个彻底开源的，面向企业应用开发的“大数据库集群”\n*\t支持事务、ACID、可以替代MySQL的加强版数据库\n*\t一个可以视为“MySQL”集群的企业级数据库，用来替代昂贵的Oracle集群\n*\t一个融合内存缓存技术、Nosql技术、HDFS大数据的新型SQL Server\n*\t结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品\n*\t一个新颖的数据库中间件产品\n\n\n\n##### Mycat的目标是：\n\n低成本的将现有的单机数据库和应用平滑迁移到“云”端，解决数据存储和业务规模迅速增长情况下的数据瓶颈问题。\n\n\n##### Mycat的关键特性：\n\n*\t支持 SQL 92标准\n*\t支持MySQL集群，可以作为Proxy使用\n*\t支持JDBC连接ORACLE、DB2、SQL Server，将其模拟为MySQL  Server使用\n*\t支持galera for MySQL集群，percona-cluster或者mariadb cluster，提供高可用性数据分片集群\n*\t自动故障切换，高可用性\n*\t支持读写分离，支持MySQL双主多从，以及一主多从的模式\n*\t支持全局表，数据自动分片到多个节点，用于高效表关联查询\n*\t支持独有的基于E-R 关系的分片策略，实现了高效的表关联查询\n*\t多平台支持，部署和实施简单\n\n\n##### Mycat的优势：\n\n*\t基于阿里开源的Cobar产品而研发，Cobar的稳定性、可靠性、优秀的架构和性能，以及众多成熟的使用案例使得Mycat一开始就拥有一个很好的起点，站在巨人的肩膀上，我们能看到更远。\n*\t广泛吸取业界优秀的开源项目和创新思路，将其融入到Mycat的基因中，使得Mycat在很多方面都领先于目前其他一些同类的开源项目，甚至超越某些商业产品。\n*\tMycat背后有一只强大的技术团队，其参与者都是5年以上资深软件工程师、架构师、DBA等，优秀的技术团队保证了Mycat的产品质量。\n*\tMycat并不依托于任何一个商业公司，因此不像某些开源项目，将一些重要的特性封闭在其商业产品中，使得开源项目成了一个摆设。\n\n\n##### Mycat的长期路线规划：\n\n*\t在支持MySQL的基础上，后端增加更多的开源数据库和商业数据库的支持，包括原生支持PosteSQL、FireBird等开源数据库，以及通过JDBC等方式间接支持其他非开源的数据库如Oracle、DB2、SQL Server等\n*\t实现更为智能的自我调节特性，如自动统计分析SQL，自动创建和调整索引，根据数据表的读写频率，自动优化缓存和备份策略等\n*\t实现更全面的监控管理功能\n*\t与HDFS集成，提供SQL命令，将数据库装入HDFS中并能够快速分析\n*\t集成优秀的开源报表工具，使之具备一定的数据分析的能力\n\n##### 下载：\n\ngithub上面的Mycat-download项目是编译好的二进制安装包 [https://github.com/MyCATApache/Mycat-download](https://github.com/MyCATApache/Mycat-download)\n\n##### 文档：\n\ngithub上面的Mycat-doc项目是相关文档 [https://github.com/MyCATApache/Mycat-doc](https://github.com/MyCATApache/Mycat-doc)\n"
  },
  {
    "path": "catlet/readme.txt",
    "content": "put your customer Catlet class files in this dir"
  },
  {
    "path": "eclipse-java-google-style.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n<profiles version=\"13\">\r\n<profile kind=\"CodeFormatterProfile\" name=\"GoogleStyle\" version=\"13\">\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.disabling_tag\" value=\"@formatter:off\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration\" value=\"end_of_line\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_cascading_method_invocation_with_arguments.count_dependent\" value=\"16|-1|16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_field\" value=\"0\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.use_on_off_tags\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_prefer_two_fragments\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_ellipsis\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_comment_inline_tags\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_local_variable_declaration\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiple_fields\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter\" value=\"1040\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type.count_dependent\" value=\"1585|-1|1585\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_conditional_expression\" value=\"80\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiple_fields.count_dependent\" value=\"16|-1|16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_binary_operator\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_array_initializer\" value=\"end_of_line\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_package\" value=\"1\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression.count_dependent\" value=\"16|4|80\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration.count_dependent\" value=\"16|4|48\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation\" value=\"2\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration.count_dependent\" value=\"16|4|49\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk\" value=\"1\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_binary_operator\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_package\" value=\"0\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_cascading_method_invocation_with_arguments\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.compiler.source\" value=\"1.7\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration.count_dependent\" value=\"16|4|48\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_line_comments\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.join_wrapped_lines\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_non_simple_local_variable_annotation\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.align_type_members_on_columns\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_member_type\" value=\"0\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_enum_constants.count_dependent\" value=\"16|5|48\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_unary_operator\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.indent_parameter_description\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.lineSplit\" value=\"100\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation.count_dependent\" value=\"16|4|48\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration\" value=\"0\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.indentation.size\" value=\"4\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.enabling_tag\" value=\"@formatter:on\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package\" value=\"1585\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_assignment\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.compiler.problem.assertIdentifier\" value=\"error\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.tabulation.char\" value=\"space\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_body\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_method\" value=\"1\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_non_simple_type_annotation\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_field_declaration\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration\" value=\"end_of_line\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_method_declaration\" value=\"0\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_switch\" value=\"end_of_line\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.compiler.problem.enumIdentifier\" value=\"error\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_generic_type_arguments\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment_new_line_at_start_of_html_paragraph\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_ellipsis\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block\" value=\"end_of_line\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comment_prefix\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_method_declaration\" value=\"end_of_line\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.compact_else_if\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_non_simple_parameter_annotation\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method\" value=\"1585\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.indent_root_tags\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_constant\" value=\"end_of_line\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.tabulation.size\" value=\"2\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation.count_dependent\" value=\"16|5|80\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter.count_dependent\" value=\"1040|-1|1040\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package.count_dependent\" value=\"1585|-1|1585\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.indent_empty_lines\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.force_if_else_statement_brace\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block_in_case\" value=\"end_of_line\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve\" value=\"3\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_non_simple_package_annotation\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation.count_dependent\" value=\"16|-1|16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type\" value=\"1585\"/>\r\n<setting id=\"org.eclipse.jdt.core.compiler.compliance\" value=\"1.7\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer\" value=\"2\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_unary_operator\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_new_anonymous_class\" value=\"20\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable.count_dependent\" value=\"1585|-1|1585\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field.count_dependent\" value=\"1585|-1|1585\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration.count_dependent\" value=\"16|5|80\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration\" value=\"end_of_line\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_binary_expression\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode\" value=\"enabled\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_label\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_javadoc_comments\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant.count_dependent\" value=\"16|-1|16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.line_length\" value=\"100\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_import_groups\" value=\"1\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration\" value=\"end_of_line\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body\" value=\"0\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_before_binary_operator\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations\" value=\"2\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_block\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.join_lines_in_comments\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_compact_if\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_imports\" value=\"0\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field\" value=\"1585\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_html\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer.count_dependent\" value=\"16|5|80\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_source_code\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.compiler.codegen.targetPlatform\" value=\"1.7\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_resources_in_try\" value=\"80\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_header\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_block_comments\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_enum_constants\" value=\"0\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration.count_dependent\" value=\"16|4|48\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method.count_dependent\" value=\"1585|-1|1585\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_type_declaration\" value=\"end_of_line\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_binary_expression.count_dependent\" value=\"16|-1|16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_non_simple_member_annotation\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable\" value=\"1585\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call.count_dependent\" value=\"16|5|80\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_generic_type_arguments.count_dependent\" value=\"16|-1|16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression.count_dependent\" value=\"16|5|80\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration.count_dependent\" value=\"16|5|80\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_imports\" value=\"1\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header\" value=\"true\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for\" value=\"insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_for_statement\" value=\"16\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column\" value=\"false\"/>\r\n<setting id=\"org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line\" value=\"false\"/>\r\n</profile>\r\n</profiles>\r\n"
  },
  {
    "path": "intellij-java-google-style.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<code_scheme name=\"GoogleStyle\">\r\n  <option name=\"OTHER_INDENT_OPTIONS\">\r\n    <value>\r\n      <option name=\"INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n      <option name=\"USE_TAB_CHARACTER\" value=\"false\" />\r\n      <option name=\"SMART_TABS\" value=\"false\" />\r\n      <option name=\"LABEL_INDENT_SIZE\" value=\"0\" />\r\n      <option name=\"LABEL_INDENT_ABSOLUTE\" value=\"false\" />\r\n      <option name=\"USE_RELATIVE_INDENTS\" value=\"false\" />\r\n    </value>\r\n  </option>\r\n  <option name=\"INSERT_INNER_CLASS_IMPORTS\" value=\"true\" />\r\n  <option name=\"CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND\" value=\"999\" />\r\n  <option name=\"NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND\" value=\"999\" />\r\n  <option name=\"PACKAGES_TO_USE_IMPORT_ON_DEMAND\">\r\n    <value />\r\n  </option>\r\n  <option name=\"IMPORT_LAYOUT_TABLE\">\r\n    <value>\r\n      <package name=\"\" withSubpackages=\"true\" static=\"true\" />\r\n      <emptyLine />\r\n      <package name=\"\" withSubpackages=\"true\" static=\"false\" />\r\n    </value>\r\n  </option>\r\n  <option name=\"RIGHT_MARGIN\" value=\"100\" />\r\n  <option name=\"JD_ALIGN_PARAM_COMMENTS\" value=\"false\" />\r\n  <option name=\"JD_ALIGN_EXCEPTION_COMMENTS\" value=\"false\" />\r\n  <option name=\"JD_P_AT_EMPTY_LINES\" value=\"false\" />\r\n  <option name=\"JD_KEEP_EMPTY_PARAMETER\" value=\"false\" />\r\n  <option name=\"JD_KEEP_EMPTY_EXCEPTION\" value=\"false\" />\r\n  <option name=\"JD_KEEP_EMPTY_RETURN\" value=\"false\" />\r\n  <option name=\"KEEP_CONTROL_STATEMENT_IN_ONE_LINE\" value=\"false\" />\r\n  <option name=\"KEEP_BLANK_LINES_IN_CODE\" value=\"1\" />\r\n  <option name=\"BLANK_LINES_AFTER_CLASS_HEADER\" value=\"1\" />\r\n  <option name=\"ALIGN_MULTILINE_PARAMETERS\" value=\"false\" />\r\n  <option name=\"ALIGN_MULTILINE_FOR\" value=\"false\" />\r\n  <option name=\"CALL_PARAMETERS_WRAP\" value=\"1\" />\r\n  <option name=\"METHOD_PARAMETERS_WRAP\" value=\"1\" />\r\n  <option name=\"EXTENDS_LIST_WRAP\" value=\"1\" />\r\n  <option name=\"THROWS_KEYWORD_WRAP\" value=\"1\" />\r\n  <option name=\"METHOD_CALL_CHAIN_WRAP\" value=\"1\" />\r\n  <option name=\"BINARY_OPERATION_WRAP\" value=\"1\" />\r\n  <option name=\"BINARY_OPERATION_SIGN_ON_NEXT_LINE\" value=\"true\" />\r\n  <option name=\"TERNARY_OPERATION_WRAP\" value=\"1\" />\r\n  <option name=\"TERNARY_OPERATION_SIGNS_ON_NEXT_LINE\" value=\"true\" />\r\n  <option name=\"FOR_STATEMENT_WRAP\" value=\"1\" />\r\n  <option name=\"ARRAY_INITIALIZER_WRAP\" value=\"1\" />\r\n  <option name=\"WRAP_COMMENTS\" value=\"true\" />\r\n  <option name=\"IF_BRACE_FORCE\" value=\"3\" />\r\n  <option name=\"DOWHILE_BRACE_FORCE\" value=\"3\" />\r\n  <option name=\"WHILE_BRACE_FORCE\" value=\"3\" />\r\n  <option name=\"FOR_BRACE_FORCE\" value=\"3\" />\r\n  <AndroidXmlCodeStyleSettings>\r\n    <option name=\"USE_CUSTOM_SETTINGS\" value=\"true\" />\r\n    <option name=\"LAYOUT_SETTINGS\">\r\n      <value>\r\n        <option name=\"INSERT_BLANK_LINE_BEFORE_TAG\" value=\"false\" />\r\n      </value>\r\n    </option>\r\n  </AndroidXmlCodeStyleSettings>\r\n  <JSCodeStyleSettings>\r\n    <option name=\"INDENT_CHAINED_CALLS\" value=\"false\" />\r\n  </JSCodeStyleSettings>\r\n  <Python>\r\n    <option name=\"USE_CONTINUATION_INDENT_FOR_ARGUMENTS\" value=\"true\" />\r\n  </Python>\r\n  <TypeScriptCodeStyleSettings>\r\n    <option name=\"INDENT_CHAINED_CALLS\" value=\"false\" />\r\n  </TypeScriptCodeStyleSettings>\r\n  <XML>\r\n    <option name=\"XML_ALIGN_ATTRIBUTES\" value=\"false\" />\r\n    <option name=\"XML_LEGACY_SETTINGS_IMPORTED\" value=\"true\" />\r\n  </XML>\r\n  <codeStyleSettings language=\"CSS\">\r\n    <indentOptions>\r\n      <option name=\"INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n    </indentOptions>\r\n  </codeStyleSettings>\r\n  <codeStyleSettings language=\"ECMA Script Level 4\">\r\n    <option name=\"KEEP_BLANK_LINES_IN_CODE\" value=\"1\" />\r\n    <option name=\"ALIGN_MULTILINE_PARAMETERS\" value=\"false\" />\r\n    <option name=\"ALIGN_MULTILINE_FOR\" value=\"false\" />\r\n    <option name=\"CALL_PARAMETERS_WRAP\" value=\"1\" />\r\n    <option name=\"METHOD_PARAMETERS_WRAP\" value=\"1\" />\r\n    <option name=\"EXTENDS_LIST_WRAP\" value=\"1\" />\r\n    <option name=\"BINARY_OPERATION_WRAP\" value=\"1\" />\r\n    <option name=\"BINARY_OPERATION_SIGN_ON_NEXT_LINE\" value=\"true\" />\r\n    <option name=\"TERNARY_OPERATION_WRAP\" value=\"1\" />\r\n    <option name=\"TERNARY_OPERATION_SIGNS_ON_NEXT_LINE\" value=\"true\" />\r\n    <option name=\"FOR_STATEMENT_WRAP\" value=\"1\" />\r\n    <option name=\"ARRAY_INITIALIZER_WRAP\" value=\"1\" />\r\n    <option name=\"IF_BRACE_FORCE\" value=\"3\" />\r\n    <option name=\"DOWHILE_BRACE_FORCE\" value=\"3\" />\r\n    <option name=\"WHILE_BRACE_FORCE\" value=\"3\" />\r\n    <option name=\"FOR_BRACE_FORCE\" value=\"3\" />\r\n    <option name=\"PARENT_SETTINGS_INSTALLED\" value=\"true\" />\r\n  </codeStyleSettings>\r\n  <codeStyleSettings language=\"HTML\">\r\n    <indentOptions>\r\n      <option name=\"INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n    </indentOptions>\r\n  </codeStyleSettings>\r\n  <codeStyleSettings language=\"JAVA\">\r\n    <option name=\"KEEP_CONTROL_STATEMENT_IN_ONE_LINE\" value=\"false\" />\r\n    <option name=\"KEEP_BLANK_LINES_IN_CODE\" value=\"1\" />\r\n    <option name=\"BLANK_LINES_AFTER_CLASS_HEADER\" value=\"1\" />\r\n    <option name=\"ALIGN_MULTILINE_PARAMETERS\" value=\"false\" />\r\n    <option name=\"ALIGN_MULTILINE_RESOURCES\" value=\"false\" />\r\n    <option name=\"ALIGN_MULTILINE_FOR\" value=\"false\" />\r\n    <option name=\"CALL_PARAMETERS_WRAP\" value=\"1\" />\r\n    <option name=\"METHOD_PARAMETERS_WRAP\" value=\"1\" />\r\n    <option name=\"EXTENDS_LIST_WRAP\" value=\"1\" />\r\n    <option name=\"THROWS_KEYWORD_WRAP\" value=\"1\" />\r\n    <option name=\"METHOD_CALL_CHAIN_WRAP\" value=\"1\" />\r\n    <option name=\"BINARY_OPERATION_WRAP\" value=\"1\" />\r\n    <option name=\"BINARY_OPERATION_SIGN_ON_NEXT_LINE\" value=\"true\" />\r\n    <option name=\"TERNARY_OPERATION_WRAP\" value=\"1\" />\r\n    <option name=\"TERNARY_OPERATION_SIGNS_ON_NEXT_LINE\" value=\"true\" />\r\n    <option name=\"FOR_STATEMENT_WRAP\" value=\"1\" />\r\n    <option name=\"ARRAY_INITIALIZER_WRAP\" value=\"1\" />\r\n    <option name=\"WRAP_COMMENTS\" value=\"true\" />\r\n    <option name=\"IF_BRACE_FORCE\" value=\"3\" />\r\n    <option name=\"DOWHILE_BRACE_FORCE\" value=\"3\" />\r\n    <option name=\"WHILE_BRACE_FORCE\" value=\"3\" />\r\n    <option name=\"FOR_BRACE_FORCE\" value=\"3\" />\r\n    <option name=\"PARENT_SETTINGS_INSTALLED\" value=\"true\" />\r\n    <indentOptions>\r\n      <option name=\"INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n    </indentOptions>\r\n  </codeStyleSettings>\r\n  <codeStyleSettings language=\"JSON\">\r\n    <indentOptions>\r\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n    </indentOptions>\r\n  </codeStyleSettings>\r\n  <codeStyleSettings language=\"JavaScript\">\r\n    <option name=\"RIGHT_MARGIN\" value=\"80\" />\r\n    <option name=\"KEEP_BLANK_LINES_IN_CODE\" value=\"1\" />\r\n    <option name=\"ALIGN_MULTILINE_PARAMETERS\" value=\"false\" />\r\n    <option name=\"ALIGN_MULTILINE_FOR\" value=\"false\" />\r\n    <option name=\"CALL_PARAMETERS_WRAP\" value=\"1\" />\r\n    <option name=\"METHOD_PARAMETERS_WRAP\" value=\"1\" />\r\n    <option name=\"BINARY_OPERATION_WRAP\" value=\"1\" />\r\n    <option name=\"BINARY_OPERATION_SIGN_ON_NEXT_LINE\" value=\"true\" />\r\n    <option name=\"TERNARY_OPERATION_WRAP\" value=\"1\" />\r\n    <option name=\"TERNARY_OPERATION_SIGNS_ON_NEXT_LINE\" value=\"true\" />\r\n    <option name=\"FOR_STATEMENT_WRAP\" value=\"1\" />\r\n    <option name=\"ARRAY_INITIALIZER_WRAP\" value=\"1\" />\r\n    <option name=\"IF_BRACE_FORCE\" value=\"3\" />\r\n    <option name=\"DOWHILE_BRACE_FORCE\" value=\"3\" />\r\n    <option name=\"WHILE_BRACE_FORCE\" value=\"3\" />\r\n    <option name=\"FOR_BRACE_FORCE\" value=\"3\" />\r\n    <option name=\"PARENT_SETTINGS_INSTALLED\" value=\"true\" />\r\n    <indentOptions>\r\n      <option name=\"INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n    </indentOptions>\r\n  </codeStyleSettings>\r\n  <codeStyleSettings language=\"PROTO\">\r\n    <option name=\"RIGHT_MARGIN\" value=\"80\" />\r\n    <indentOptions>\r\n      <option name=\"INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n    </indentOptions>\r\n  </codeStyleSettings>\r\n  <codeStyleSettings language=\"protobuf\">\r\n    <option name=\"RIGHT_MARGIN\" value=\"80\" />\r\n    <indentOptions>\r\n      <option name=\"INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n    </indentOptions>\r\n  </codeStyleSettings>\r\n  <codeStyleSettings language=\"Python\">\r\n    <option name=\"KEEP_BLANK_LINES_IN_CODE\" value=\"1\" />\r\n    <option name=\"RIGHT_MARGIN\" value=\"80\" />\r\n    <option name=\"ALIGN_MULTILINE_PARAMETERS\" value=\"false\" />\r\n    <option name=\"PARENT_SETTINGS_INSTALLED\" value=\"true\" />\r\n    <indentOptions>\r\n      <option name=\"INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n    </indentOptions>\r\n  </codeStyleSettings>\r\n  <codeStyleSettings language=\"SASS\">\r\n    <indentOptions>\r\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n    </indentOptions>\r\n  </codeStyleSettings>\r\n  <codeStyleSettings language=\"SCSS\">\r\n    <indentOptions>\r\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n    </indentOptions>\r\n  </codeStyleSettings>\r\n  <codeStyleSettings language=\"TypeScript\">\r\n    <indentOptions>\r\n      <option name=\"INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n    </indentOptions>\r\n  </codeStyleSettings>\r\n  <codeStyleSettings language=\"XML\">\r\n    <indentOptions>\r\n      <option name=\"INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"TAB_SIZE\" value=\"2\" />\r\n    </indentOptions>\r\n    <arrangement>\r\n      <rules>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>xmlns:android</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>^$</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>xmlns:.*</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>^$</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n            <order>BY_NAME</order>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:id</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>style</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>^$</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>^$</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n            <order>BY_NAME</order>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:.*Style</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n            <order>BY_NAME</order>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:layout_width</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:layout_height</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:layout_weight</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:layout_margin</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:layout_marginTop</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:layout_marginBottom</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:layout_marginStart</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:layout_marginEnd</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:layout_marginLeft</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:layout_marginRight</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:layout_.*</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n            <order>BY_NAME</order>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:padding</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:paddingTop</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:paddingBottom</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:paddingStart</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:paddingEnd</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:paddingLeft</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*:paddingRight</NAME>\r\n                <XML_ATTRIBUTE />\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*</NAME>\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n            <order>BY_NAME</order>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*</NAME>\r\n                <XML_NAMESPACE>http://schemas.android.com/apk/res-auto</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n            <order>BY_NAME</order>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*</NAME>\r\n                <XML_NAMESPACE>http://schemas.android.com/tools</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n            <order>BY_NAME</order>\r\n          </rule>\r\n        </section>\r\n        <section>\r\n          <rule>\r\n            <match>\r\n              <AND>\r\n                <NAME>.*</NAME>\r\n                <XML_NAMESPACE>.*</XML_NAMESPACE>\r\n              </AND>\r\n            </match>\r\n            <order>BY_NAME</order>\r\n          </rule>\r\n        </section>\r\n      </rules>\r\n    </arrangement>\r\n  </codeStyleSettings>\r\n  <Objective-C>\r\n    <option name=\"INDENT_NAMESPACE_MEMBERS\" value=\"0\" />\r\n    <option name=\"INDENT_C_STRUCT_MEMBERS\" value=\"2\" />\r\n    <option name=\"INDENT_CLASS_MEMBERS\" value=\"2\" />\r\n    <option name=\"INDENT_VISIBILITY_KEYWORDS\" value=\"1\" />\r\n    <option name=\"INDENT_INSIDE_CODE_BLOCK\" value=\"2\" />\r\n    <option name=\"KEEP_STRUCTURES_IN_ONE_LINE\" value=\"true\" />\r\n    <option name=\"FUNCTION_PARAMETERS_WRAP\" value=\"5\" />\r\n    <option name=\"FUNCTION_CALL_ARGUMENTS_WRAP\" value=\"5\" />\r\n    <option name=\"TEMPLATE_CALL_ARGUMENTS_WRAP\" value=\"5\" />\r\n    <option name=\"TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE\" value=\"true\" />\r\n    <option name=\"ALIGN_INIT_LIST_IN_COLUMNS\" value=\"false\" />\r\n    <option name=\"SPACE_BEFORE_SUPERCLASS_COLON\" value=\"false\" />\r\n  </Objective-C>\r\n  <Objective-C-extensions>\r\n    <option name=\"GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES\" value=\"ASK\" />\r\n    <option name=\"RELEASE_STYLE\" value=\"IVAR\" />\r\n    <option name=\"TYPE_QUALIFIERS_PLACEMENT\" value=\"BEFORE\" />\r\n    <file>\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Import\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Macro\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Typedef\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Enum\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Constant\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Global\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Struct\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"FunctionPredecl\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Function\" />\r\n    </file>\r\n    <class>\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Property\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Synthesize\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"InitMethod\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"StaticMethod\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"InstanceMethod\" />\r\n      <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"DeallocMethod\" />\r\n    </class>\r\n    <extensions>\r\n      <pair source=\"cc\" header=\"h\" />\r\n      <pair source=\"c\" header=\"h\" />\r\n    </extensions>\r\n  </Objective-C-extensions>\r\n  <codeStyleSettings language=\"ObjectiveC\">\r\n    <option name=\"RIGHT_MARGIN\" value=\"80\" />\r\n    <option name=\"KEEP_BLANK_LINES_BEFORE_RBRACE\" value=\"1\" />\r\n    <option name=\"BLANK_LINES_BEFORE_IMPORTS\" value=\"0\" />\r\n    <option name=\"BLANK_LINES_AFTER_IMPORTS\" value=\"0\" />\r\n    <option name=\"BLANK_LINES_AROUND_CLASS\" value=\"0\" />\r\n    <option name=\"BLANK_LINES_AROUND_METHOD\" value=\"0\" />\r\n    <option name=\"BLANK_LINES_AROUND_METHOD_IN_INTERFACE\" value=\"0\" />\r\n    <option name=\"ALIGN_MULTILINE_BINARY_OPERATION\" value=\"false\" />\r\n    <option name=\"BINARY_OPERATION_SIGN_ON_NEXT_LINE\" value=\"true\" />\r\n    <option name=\"FOR_STATEMENT_WRAP\" value=\"1\" />\r\n    <option name=\"ASSIGNMENT_WRAP\" value=\"1\" />\r\n    <indentOptions>\r\n      <option name=\"INDENT_SIZE\" value=\"2\" />\r\n      <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\r\n    </indentOptions>\r\n  </codeStyleSettings>\r\n</code_scheme>\r\n"
  },
  {
    "path": "pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n\t<modelVersion>4.0.0</modelVersion>\n\t<groupId>io.mycat</groupId>\n\t<artifactId>Mycat-server</artifactId>\n\t<version>1.6.7.6-release</version>\n\t<packaging>jar</packaging>\n\t<name>Mycat-server</name>\n\t<description>The project of Mycat-server</description>\n\t<url>http://io.mycat</url>\n\n\t<properties>\n\t\t<app.encoding>UTF-8</app.encoding>\n\t\t<!-- maven.build.timestamp.format>yyyyMMdd</maven.build.timestamp.format>\n\t\t\t<buildNumber>${maven.build.timestamp}</buildNumber      -->\n\t\t<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>\n\t\t<version.template.file>version.txt.template</version.template.file>\n\t\t<version.file>version.txt</version.file>\n\t</properties>\n\t<scm>\n\t\t<connection>scm:git:ssh://apachemycat@github.com/MyCATApache/Mycat-Server.git</connection>\n\t\t<developerConnection>scm:git:ssh://apachemycat@github.com/MyCATApache/Mycat-Server.git</developerConnection>\n\t\t<url>scm:git:ssh://apachemycat@github.com/MyCATApache/Mycat-Server.git</url>\n\t</scm>\n\t<!-- repositories>\n\t\t<repository>\n\t\t\t<id>nexus</id>\n\t\t\t<name>local private nexus</name>\n\t\t\t<url>http://nexus.mycat.io/content/groups/public</url>\n\t\t</repository>\n\t</repositories>\n\t<distributionManagement>\n\t\t<repository>\n\t\t\t<id>releases</id>\n\t\t\t<name>Internal Releases</name>\n\t\t\t<url>http://nexus.mycat.io/content/repositories/releases</url>\n\t\t</repository>\n\t\t<snapshotRepository>\n\t\t\t<id>snapshots</id>\n\t\t\t<name>Internal Snapshots</name>\n\t\t\t<url>http://nexus.mycat.io/content/repositories/snapshots</url>\n\t\t</snapshotRepository>\n\t</distributionManagement -->\n\t<dependencies>\n\t\t<!-- <dependency> <groupId>com.google.guava</groupId> <artifactId>guava-parent</artifactId>\n\t\t\t<version>18.0</version> </dependency> -->\n\t\t<dependency>\n\t\t\t<groupId>org.mongodb</groupId>\n\t\t\t<artifactId>mongo-java-driver</artifactId>\n\t\t\t<version>3.11.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.iq80.leveldb</groupId>\n\t\t\t<artifactId>leveldb</artifactId>\n\t\t\t<version>0.7</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.iq80.leveldb</groupId>\n\t\t\t<artifactId>leveldb-api</artifactId>\n\t\t\t<version>0.7</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.google.guava</groupId>\n\t\t\t<artifactId>guava</artifactId>\n\t\t\t<version>28.2-jre</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>druid</artifactId>\n\t\t\t<version>1.2.6</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>mysql</groupId>\n\t\t\t<artifactId>mysql-connector-java</artifactId>\n\t\t\t<version>5.1.35</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>net.sf.ehcache</groupId>\n\t\t\t<artifactId>ehcache-core</artifactId>\n\t\t\t<version>2.6.11</version>\n\t\t\t<scope>compile</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.mapdb</groupId>\n\t\t\t<artifactId>mapdb</artifactId>\n\t\t\t<version>1.0.7</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<version>4.4</version>\n\t\t\t<scope>provided</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.velocity</groupId>\n\t\t\t<artifactId>velocity</artifactId>\n\t\t\t<version>1.7</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.codehaus.jsr166-mirror</groupId>\n\t\t\t<artifactId>jsr166y</artifactId>\n\t\t\t<version>1.7.0</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.lmax</groupId>\n\t\t\t<artifactId>disruptor</artifactId>\n\t\t\t<version>3.3.4</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.logging.log4j</groupId>\n\t\t\t<artifactId>log4j-slf4j-impl</artifactId>\n\t\t\t<version>2.17.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.logging.log4j</groupId>\n\t\t\t<artifactId>log4j-core</artifactId>\n\t\t\t<version>2.17.1</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.logging.log4j</groupId>\n\t\t\t<artifactId>log4j-1.2-api</artifactId>\n\t\t\t<version>2.17.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.univocity</groupId>\n\t\t\t<artifactId>univocity-parsers</artifactId>\n\t\t\t<version>2.8.4</version>\n\t\t\t<type>jar</type>\n\t\t</dependency>\n\n\n\t\t<dependency>\n\t\t\t<groupId>com.sequoiadb</groupId>\n\t\t\t<artifactId>sequoiadb-driver</artifactId>\n\t\t\t<version>1.12</version>\n\t\t</dependency>\n\n\n\t\t<!--DOM4J FOR XML -->\n\t\t<dependency>\n\t\t\t<groupId>dom4j</groupId>\n\t\t\t<artifactId>dom4j</artifactId>\n\t\t\t<version>1.6.1</version>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<groupId>xml-apis</groupId>\n\t\t\t\t\t<artifactId>xml-apis</artifactId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\n\t\t<!-- zookeeper -->\n\t\t<dependency>\n\t\t\t<groupId>org.apache.curator</groupId>\n\t\t\t<artifactId>curator-framework</artifactId>\n\t\t\t<version>4.0.1</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.curator</groupId>\n\t\t\t<artifactId>curator-recipes</artifactId>\n\t\t\t<version>4.0.1</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.curator</groupId>\n\t\t\t<artifactId>curator-test</artifactId>\n\t\t\t<version>4.0.1</version>\n\t\t\t<scope>test</scope>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<groupId>log4j</groupId>\n\t\t\t\t\t<artifactId>log4j</artifactId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>fastjson</artifactId>\n\t\t\t<version>1.2.68</version>\n\t\t</dependency>\n\t\t<!-- joda日期处理工具 -->\n\t\t<dependency>\n\t\t\t<groupId>joda-time</groupId>\n\t\t\t<artifactId>joda-time</artifactId>\n\t\t\t<version>2.9.3</version>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>com.github.shyiko</groupId>\n\t\t\t<artifactId>mysql-binlog-connector-java</artifactId>\n\t\t\t<version>0.16.1</version>\n\t\t</dependency>\n\n\n\t\t<dependency>\n\t\t\t<groupId>org.mockito</groupId>\n\t\t\t<artifactId>mockito-all</artifactId>\n\t\t\t<version>1.8.5</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>com.google.code.findbugs</groupId>\n\t\t\t<artifactId>jsr305</artifactId>\n\t\t\t<version>2.0.3</version>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>com.esotericsoftware.kryo</groupId>\n\t\t\t<artifactId>kryo</artifactId>\n\t\t\t<version>2.10</version>\n\t\t</dependency>\n\t\t<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-csv -->\n\t\t<dependency>\n\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t<artifactId>commons-csv</artifactId>\n\t\t\t<version>1.8</version>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.hamcrest</groupId>\n\t\t\t<artifactId>hamcrest-library</artifactId>\n\t\t\t<version>1.3</version>\n\t\t</dependency>\n\n\n\t\t<!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->\n\t\t<dependency>\n\t\t\t<groupId>commons-lang</groupId>\n\t\t\t<artifactId>commons-lang</artifactId>\n\t\t\t<version>2.6</version>\n\t\t</dependency>\n\t\t<!-- https://mvnrepository.com/artifact/io.netty/netty-buffer -->\n\t\t<dependency>\n\t\t\t<groupId>io.netty</groupId>\n\t\t\t<artifactId>netty-buffer</artifactId>\n\t\t\t<version>4.1.9.Final</version>\n\t\t</dependency>\n\t\t<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->\n\t\t<dependency>\n\t\t\t<groupId>com.squareup.okhttp3</groupId>\n\t\t\t<artifactId>okhttp</artifactId>\n\t\t\t<version>4.2.2</version>\n\t\t</dependency>\n\n\t</dependencies>\n\n\n\n\t<issueManagement>\n\t\t<system>JIRA</system>\n\t\t<url>http://io.mycat</url>\n\t</issueManagement>\n\n\t<build>\n\t\t<!-- finalName>${artifactId}-${version}-${buildNumber}</finalName -->\n\t\t<resources>\n\t\t\t<resource>\n\t\t\t\t<directory>src/main/resources</directory>\n\t\t\t\t<excludes>\n\t\t\t\t\t<exclude>**/.svn/**</exclude>\n\t\t\t\t</excludes>\n\t\t\t</resource>\n\t\t\t<resource>\n\t\t\t\t<directory>${basedir}</directory>\n\t\t\t\t<includes>\n\t\t\t\t\t<include>${version.file}</include>\n\t\t\t\t</includes>\n\t\t\t</resource>\n\t\t</resources>\n\t\t<testResources>\n\t\t\t<testResource>\n\t\t\t\t<directory>src/test/resources</directory>\n\t\t\t\t<excludes>\n\t\t\t\t\t<exclude>**/.svn/**</exclude>\n\t\t\t\t</excludes>\n\t\t\t</testResource>\n\t\t</testResources>\n\t\t<pluginManagement>\n\t\t\t<plugins>\n\t\t\t\t<plugin>\n\t\t\t\t\t<groupId>org.eclipse.m2e</groupId>\n\t\t\t\t\t<artifactId>lifecycle-mapping</artifactId>\n\t\t\t\t\t<version>1.0.0</version>\n\t\t\t\t\t<configuration>\n\t\t\t\t\t\t<lifecycleMappingMetadata>\n\t\t\t\t\t\t\t<pluginExecutions>\n\t\t\t\t\t\t\t\t<pluginExecution>\n\t\t\t\t\t\t\t\t\t<pluginExecutionFilter>\n\t\t\t\t\t\t\t\t\t\t<groupId>com.google.code.maven-replacer-plugin</groupId>\n\t\t\t\t\t\t\t\t\t\t<artifactId>replacer</artifactId>\n\t\t\t\t\t\t\t\t\t\t<versionRange>[1.0.0,)</versionRange>\n\t\t\t\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t\t\t\t<goal>replace</goal>\n\t\t\t\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t\t\t\t</pluginExecutionFilter>\n\t\t\t\t\t\t\t\t\t<action>\n\t\t\t\t\t\t\t\t\t\t<ignore />\n\t\t\t\t\t\t\t\t\t</action>\n\t\t\t\t\t\t\t\t</pluginExecution>\n\t\t\t\t\t\t\t</pluginExecutions>\n\t\t\t\t\t\t</lifecycleMappingMetadata>\n\t\t\t\t\t</configuration>\n\t\t\t\t</plugin>\n\t\t\t</plugins>\n\t\t</pluginManagement>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>com.google.code.maven-replacer-plugin</groupId>\n\t\t\t\t<artifactId>replacer</artifactId>\n\t\t\t\t<version>1.5.3</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>version</id>\n\t\t\t\t\t\t<phase>process-sources</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>replace</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<file>${project.basedir}/${version.template.file}</file>\n\t\t\t\t\t\t\t<outputFile>${project.basedir}/${version.file}</outputFile>\n\t\t\t\t\t\t\t<replacements>\n\t\t\t\t\t\t\t\t<replacement>\n\t\t\t\t\t\t\t\t\t<token>@buildnumber@</token>\n\t\t\t\t\t\t\t\t\t<value>${buildNumber}</value>\n\t\t\t\t\t\t\t\t</replacement>\n\t\t\t\t\t\t\t\t<replacement>\n\t\t\t\t\t\t\t\t\t<token>@buildtime@</token>\n\t\t\t\t\t\t\t\t\t<value>${maven.build.timestamp}</value>\n\t\t\t\t\t\t\t\t</replacement>\n\t\t\t\t\t\t\t\t<replacement>\n\t\t\t\t\t\t\t\t\t<token>@pomversion@</token>\n\t\t\t\t\t\t\t\t\t<value>${project.version}</value>\n\t\t\t\t\t\t\t\t</replacement>\n\t\t\t\t\t\t\t\t<replacement>\n\t\t\t\t\t\t\t\t\t<token>@giturl@</token>\n\t\t\t\t\t\t\t\t\t<value>https://github.com/MyCATApache/Mycat-Server.git</value>\n\t\t\t\t\t\t\t\t</replacement>\n\t\t\t\t\t\t\t\t<replacement>\n\t\t\t\t\t\t\t\t\t<token>@mycatsite@</token>\n\t\t\t\t\t\t\t\t\t<value>http://www.mycat.org.cn</value>\n\t\t\t\t\t\t\t\t</replacement>\n\t\t\t\t\t\t\t\t<replacement>\n\t\t\t\t\t\t\t\t\t<token>@qqgroup@</token>\n\t\t\t\t\t\t\t\t\t<value>106088787</value>\n\t\t\t\t\t\t\t\t</replacement>\n\t\t\t\t\t\t\t</replacements>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</execution>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>version2</id>\n\n\t\t\t\t\t\t<phase>process-sources</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>replace</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<file>${project.basedir}/src/main/java/io/mycat/config/Versions.template</file>\n\t\t\t\t\t\t\t<outputFile>${project.basedir}/src/main/java/io/mycat/config/Versions.java</outputFile>\n\t\t\t\t\t\t\t<replacements>\n\t\t\t\t\t\t\t\t<replacement>\n\t\t\t\t\t\t\t\t\t<token>@server-version@</token>\n\t\t\t\t\t\t\t\t\t<value>5.6.29-mycat-${project.version}-${timestamp}</value>\n\t\t\t\t\t\t\t\t</replacement>\n\t\t\t\t\t\t\t</replacements>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\n\t\t\t</plugin>\n\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>\n\t\t\t\t<configuration>\n\t\t\t\t\t<source>1.8</source>\n\t\t\t\t\t<target>1.8</target>\n\t\t\t\t\t<encoding>${app.encoding}</encoding>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-source-plugin</artifactId>\n\t\t\t\t<version>2.1.2</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<encoding>${app.encoding}</encoding>\n\t\t\t\t\t<attach>true</attach>\n\t\t\t\t</configuration>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>attach-sources</id>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>jar-no-fork</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-jar-plugin</artifactId>\n\t\t\t\t<configuration>\n\t\t\t\t\t<excludes>\n\t\t\t\t\t\t<exclude>**/.svn/**</exclude>\n\t\t\t\t\t</excludes>\n\t\t\t\t</configuration>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>test-jar</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t<!-- configuration> <finalName>${project.build.finalName}-${buildNumber}</finalName>\n\t\t\t\t\t\t\t</configuration -->\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-eclipse-plugin</artifactId>\n\t\t\t\t<configuration>\n\t\t\t\t\t<sourceExcludes>\n\t\t\t\t\t\t<sourceExclude>**/.svn/**</sourceExclude>\n\t\t\t\t\t</sourceExcludes>\n\t\t\t\t\t<downloadSources>true</downloadSources>\n\t\t\t\t\t<outputDirectory>classes</outputDirectory>\n\t\t\t\t\t<additionalConfig>\n\t\t\t\t\t\t<file>\n\t\t\t\t\t\t\t<name>.settings/org.eclipse.core.resources.prefs</name>\n\t\t\t\t\t\t\t<content>\n\t\t\t\t\t\t\t\t<![CDATA[eclipse.preferences.version=1${line.separator}encoding/<project>=${app.encoding}${line.separator}]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</additionalConfig>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\n\t\t\t<!-- -->\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.codehaus.mojo</groupId>\n\t\t\t\t<artifactId>appassembler-maven-plugin</artifactId>\n\t\t\t\t<version>1.8</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<configurationDirectory>conf</configurationDirectory>\n\t\t\t\t\t<includeConfigurationDirectoryInClasspath>true</includeConfigurationDirectoryInClasspath>\n\t\t\t\t\t<repositoryLayout>flat</repositoryLayout>\n\t\t\t\t\t<useWildcardClassPath>true</useWildcardClassPath>\n\t\t\t\t\t<daemons>\n\t\t\t\t\t\t<daemon>\n\t\t\t\t\t\t\t<id>mycat</id>\n\t\t\t\t\t\t\t<mainClass>io.mycat.MycatStartup</mainClass>\n\t\t\t\t\t\t\t<commandLineArguments>\n\t\t\t\t\t\t\t\t<commandLineArgument>start</commandLineArgument>\n\t\t\t\t\t\t\t</commandLineArguments>\n\t\t\t\t\t\t\t<platforms>\n\t\t\t\t\t\t\t\t<platform>jsw</platform>\n\t\t\t\t\t\t\t</platforms>\n\t\t\t\t\t\t\t<jvmSettings>\n\t\t\t\t\t\t\t\t<!-- 启动内存配置 -->\n\t\t\t\t\t\t\t\t<maxStackSize>128</maxStackSize>\n\t\t\t\t\t\t\t\t<systemProperties>\n\t\t\t\t\t\t\t\t\t<systemProperty>MYCAT_HOME=.</systemProperty>\n\t\t\t\t\t\t\t\t</systemProperties>\n\t\t\t\t\t\t\t\t<extraArguments>\n\t\t\t\t\t\t\t\t\t<extraArgument>-server </extraArgument>\n\t\t\t\t\t\t\t\t\t<!--<extraArgument>-XX:MaxPermSize=64M</extraArgument>-->\n\t\t\t\t\t\t\t\t\t<extraArgument>-XX:+AggressiveOpts</extraArgument>\n\t\t\t\t\t\t\t\t\t<extraArgument>-XX:MaxDirectMemorySize=2G</extraArgument>\n\t\t\t\t\t\t\t\t\t<!-- 远程JMX -->\n\t\t\t\t\t\t\t\t\t<extraArgument>-Dcom.sun.management.jmxremote </extraArgument>\n\t\t\t\t\t\t\t\t\t<extraArgument>-Dcom.sun.management.jmxremote.port=1984</extraArgument>\n\t\t\t\t\t\t\t\t\t<extraArgument>-Dcom.sun.management.jmxremote.authenticate=false </extraArgument>\n\t\t\t\t\t\t\t\t\t<extraArgument>-Dcom.sun.management.jmxremote.ssl=false </extraArgument>\n\t\t\t\t\t\t\t\t\t<extraArgument>-Xmx4G</extraArgument>\n\t\t\t\t\t\t\t\t\t<extraArgument>-Xms1G</extraArgument>\n\t\t\t\t\t\t\t\t</extraArguments>\n\t\t\t\t\t\t\t</jvmSettings>\n\t\t\t\t\t\t\t<generatorConfigurations>\n\t\t\t\t\t\t\t\t<generatorConfiguration>\n\t\t\t\t\t\t\t\t\t<generator>jsw</generator>\n\t\t\t\t\t\t\t\t\t<includes>\n\t\t\t\t\t\t\t\t\t\t<include>aix-ppc-32</include>\n\t\t\t\t\t\t\t\t\t\t<include>aix-ppc-64</include>\n\t\t\t\t\t\t\t\t\t\t<include>hpux-parisc-64</include>\n\t\t\t\t\t\t\t\t\t\t<include>linux-x86-32</include>\n\t\t\t\t\t\t\t\t\t\t<include>linux-x86-64</include>\n\t\t\t\t\t\t\t\t\t\t<include>linux-ppc-64</include>\n\t\t\t\t\t\t\t\t\t\t<include>macosx-ppc-32</include>\n\t\t\t\t\t\t\t\t\t\t<include>macosx-x86-universal-32</include>\n\t\t\t\t\t\t\t\t\t\t<include>macosx-universal-32</include>\n\t\t\t\t\t\t\t\t\t\t<include>macosx-universal-64</include>\n\t\t\t\t\t\t\t\t\t\t<include>solaris-sparc-32</include>\n\t\t\t\t\t\t\t\t\t\t<include>solaris-sparc-64</include>\n\t\t\t\t\t\t\t\t\t\t<include>solaris-x86-32</include>\n\t\t\t\t\t\t\t\t\t\t<include>windows-x86-32</include>\n\t\t\t\t\t\t\t\t\t\t<include>windows-x86-64</include>\n\t\t\t\t\t\t\t\t\t</includes>\n\n\t\t\t\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t\t\t\t<property>\n\t\t\t\t\t\t\t\t\t\t\t<name>configuration.directory.in.classpath.first</name>\n\t\t\t\t\t\t\t\t\t\t\t<value>conf</value>\n\t\t\t\t\t\t\t\t\t\t</property>\n\t\t\t\t\t\t\t\t\t\t<property>\n\t\t\t\t\t\t\t\t\t\t\t<name>wrapper.ping.timeout</name>\n\t\t\t\t\t\t\t\t\t\t\t<value>120</value>\n\t\t\t\t\t\t\t\t\t\t</property>\n\t\t\t\t\t\t\t\t\t\t<property>\n\t\t\t\t\t\t\t\t\t\t\t<name>set.default.REPO_DIR</name>\n\t\t\t\t\t\t\t\t\t\t\t<value>lib</value>\n\t\t\t\t\t\t\t\t\t\t</property>\n\t\t\t\t\t\t\t\t\t\t<property>\n\t\t\t\t\t\t\t\t\t\t\t<name>wrapper.logfile.maxsize</name>\n\t\t\t\t\t\t\t\t\t\t\t<value>512m</value>\n\t\t\t\t\t\t\t\t\t\t</property>\n\t\t\t\t\t\t\t\t\t\t<property>\n\t\t\t\t\t\t\t\t\t\t\t<name>wrapper.logfile.maxfiles</name>\n\t\t\t\t\t\t\t\t\t\t\t<value>30</value>\n\t\t\t\t\t\t\t\t\t\t</property>\n\t\t\t\t\t\t\t\t\t\t<property>\n\t\t\t\t\t\t\t\t\t\t\t<name>wrapper.logfile</name>\n\t\t\t\t\t\t\t\t\t\t\t<value>logs/wrapper.log</value>\n\t\t\t\t\t\t\t\t\t\t</property>\n\t\t\t\t\t\t\t\t\t</configuration>\n\n\t\t\t\t\t\t\t\t</generatorConfiguration>\n\t\t\t\t\t\t\t</generatorConfigurations>\n\t\t\t\t\t\t</daemon>\n\n\t\t\t\t\t</daemons>\n\t\t\t\t</configuration>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>generate-jsw</id>\n\t\t\t\t\t\t<phase>package</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>generate-daemons</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-assembly-plugin</artifactId>\n\t\t\t\t<configuration>\n\t\t\t\t\t<descriptors>\n\t\t\t\t\t\t<descriptor>src/main/assembly/assembly-win.xml</descriptor>\n\t\t\t\t\t\t<descriptor>src/main/assembly/assembly-linux.xml</descriptor>\n\t\t\t\t\t\t<descriptor>src/main/assembly/assembly-mac.xml</descriptor>\n\t\t\t\t\t\t<descriptor>src/main/assembly/assembly-solaris.xml</descriptor>\n\t\t\t\t\t\t<descriptor>src/main/assembly/assembly-unix.xml</descriptor>\n\t\t\t\t\t\t<descriptor>src/main/assembly/assembly-testtool.xml</descriptor>\n\t\t\t\t\t</descriptors>\n\t\t\t\t</configuration>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>make-assembly</id>\n\t\t\t\t\t\t<phase>package</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>single</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-scm-plugin</artifactId>\n\t\t\t\t<version>1.11.2</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<providerImplementations>\n\t\t\t\t\t\t<git>jgit</git>\n\t\t\t\t\t</providerImplementations>\n\t\t\t\t</configuration>\n\t\t\t\t<dependencies>\n\t\t\t\t\t<dependency>\n\t\t\t\t\t\t<groupId>org.apache.maven.scm</groupId>\n\t\t\t\t\t\t<artifactId>maven-scm-provider-jgit</artifactId>\n\t\t\t\t\t\t<version>1.11.2</version>\n\t\t\t\t\t</dependency>\n\t\t\t\t</dependencies>\n\t\t\t</plugin>\n\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.codehaus.mojo</groupId>\n\t\t\t\t<artifactId>buildnumber-maven-plugin</artifactId>\n\t\t\t\t<version>1.4</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<phase>validate</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>create</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t\t<configuration>\n\t\t\t\t\t<format>{1}</format>\n\t\t\t\t\t<items>\n\t\t\t\t\t\t<item>timestamp</item>\n\t\t\t\t\t\t<item>scmVersion</item>\n\t\t\t\t\t</items>\n\t\t\t\t\t<doCheck>false</doCheck>\n\t\t\t\t\t<doUpdate>false</doUpdate>\n\t\t\t\t\t<timestampFormat>{0,date,yyyyMMddHHmmss}</timestampFormat>\n\t\t\t\t\t<providerImplementations>\n\t\t\t\t\t\t<git>git</git>\n\t\t\t\t\t</providerImplementations>\n\n\t\t\t\t</configuration>\n\t\t\t\t<!-- dependencies>\n\t\t\t\t\t<dependency>\n\t\t\t\t\t\t<groupId>org.apache.maven.scm</groupId>\n\t\t\t\t\t\t<artifactId>maven-scm-provider-gitexe</artifactId>\n\t\t\t\t\t\t<version>RELEASE</version>\n\t\t\t\t\t</dependency>\n\t\t\t\t\t<dependency>\n\t\t\t\t\t\t<groupId>org.tmatesoft.svnkit</groupId>\n\t\t\t\t\t\t<artifactId>svnkit</artifactId>\n\t\t\t\t\t\t<version>RELEASE</version>\n\t\t\t\t\t</dependency>\n\t\t\t\t</dependencies -->\n\n\t\t\t</plugin>\n\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "src/main/assembly/assembly-linux.xml",
    "content": "<assembly\n\txmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n\t<id>${timestamp}-linux</id>\n\t<includeBaseDirectory>false</includeBaseDirectory>\n\t<formats>\n\t\t<format>tar.gz</format>\n\t</formats>\n\t<fileSets>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/bin</directory>\n\t\t\t<outputDirectory>mycat/bin</outputDirectory>\n            <fileMode>0755</fileMode>\n            <includes>\n\t\t\t\t<include>mycat</include>\n\t\t\t\t<include>wrapper-linux*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/lib</directory>\n\t\t\t<outputDirectory>mycat/lib</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>*.jar</include>\n\t\t\t\t<include>libwrapper-linux*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/conf</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/resources</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t\t<excludes>\n\t\t\t\t<exclude>*.dtd</exclude>\n\t\t\t\t<exclude>log4j*</exclude>\n\t\t\t</excludes>\n\t\t</fileSet>\n        <fileSet>\n            <directory>${basedir}</directory>\n            <outputDirectory>mycat/</outputDirectory>\n            <includes>\n                <include>version.txt</include>\n            </includes>\n        </fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/assembly/conf</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/assembly/bin</directory>\n\t\t\t<outputDirectory>mycat/bin</outputDirectory>\n\t\t\t<fileMode>0755</fileMode>\n\t\t\t<includes>\n\t\t\t\t<include>*.sh</include>\n            </includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>${basedir}/logs</directory>\n\t\t\t<outputDirectory>mycat/logs</outputDirectory>\n\t\t\t<excludes>\n\t\t\t\t<exclude>**/*</exclude>\n\t\t\t</excludes>\n\t\t</fileSet>\n        <fileSet>\n            <directory>${basedir}/catlet</directory>\n            <outputDirectory>mycat/catlet</outputDirectory>\n            <excludes>\n                <exclude>**/*</exclude>\n            </excludes>\n        </fileSet>\n\t</fileSets>\n\n</assembly>\n"
  },
  {
    "path": "src/main/assembly/assembly-mac.xml",
    "content": "<assembly\n\txmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n\t<id>${timestamp}-mac</id>\n\t<includeBaseDirectory>false</includeBaseDirectory>\n\t<formats>\n\t\t<format>tar.gz</format>\n\t</formats>\n\t<fileSets>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/bin</directory>\n\t\t\t<outputDirectory>mycat/bin</outputDirectory>\n            <fileMode>0755</fileMode>\n            <includes>\n\t\t\t\t<include>mycat</include>\n\t\t\t\t<include>wrapper-macosx*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/lib</directory>\n\t\t\t<outputDirectory>mycat/lib</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>*.jar</include>\n\t\t\t\t<include>libwrapper-macosx*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/conf</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/resources</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t\t<excludes>\n\t\t\t\t<exclude>*.dtd</exclude>\n\t\t\t\t<exclude>log4j*</exclude>\n\t\t\t</excludes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/assembly/conf</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t</fileSet>\n        <fileSet>\n            <directory>${basedir}</directory>\n            <outputDirectory>mycat/</outputDirectory>\n            <includes>\n                <include>version.txt</include>\n            </includes>\n        </fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/assembly/bin</directory>\n\t\t\t<outputDirectory>mycat/bin</outputDirectory>\n\t\t\t<fileMode>0755</fileMode>\n\t\t\t<includes>\n\t\t\t\t<include>*.sh</include>\n\t\t\t</includes>\n\t\t</fileSet>\n        <fileSet>\n            <directory>${basedir}/logs</directory>\n            <outputDirectory>mycat/logs</outputDirectory>\n            <excludes>\n                <exclude>**/*</exclude>\n            </excludes>\n        </fileSet>\n        <fileSet>\n            <directory>${basedir}/catlet</directory>\n            <outputDirectory>mycat/catlet</outputDirectory>\n            <excludes>\n                <exclude>**/*</exclude>\n            </excludes>\n        </fileSet>\n\t</fileSets>\n\n</assembly>\n"
  },
  {
    "path": "src/main/assembly/assembly-solaris.xml",
    "content": "<assembly\n\txmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n\t<id>${timestamp}-solaris</id>\n\t<includeBaseDirectory>false</includeBaseDirectory>\n\t<formats>\n\t\t<format>tar.gz</format>\n\t</formats>\n\t<fileSets>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/bin</directory>\n\t\t\t<outputDirectory>mycat/bin</outputDirectory>\n            <fileMode>0755</fileMode>\n            <includes>\n\t\t\t\t<include>mycat</include>\n\t\t\t\t<include>wrapper-solaris*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/lib</directory>\n\t\t\t<outputDirectory>mycat/lib</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>*.jar</include>\n\t\t\t\t<include>libwrapper-solaris*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/conf</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/resources</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t\t<excludes>\n\t\t\t\t<exclude>*.dtd</exclude>\n\t\t\t\t<exclude>log4j*</exclude>\n\t\t\t</excludes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/assembly/conf</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t</fileSet>\n        <fileSet>\n            <directory>${basedir}</directory>\n            <outputDirectory>mycat/</outputDirectory>\n            <includes>\n                <include>version.txt</include>\n            </includes>\n        </fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/assembly/bin</directory>\n\t\t\t<outputDirectory>mycat/bin</outputDirectory>\n\t\t\t<fileMode>0755</fileMode>\n\t\t\t<includes>\n\t\t\t\t<include>*.sh</include>\n\t\t\t</includes>\n\t\t</fileSet>\n        <fileSet>\n            <directory>${basedir}/logs</directory>\n            <outputDirectory>mycat/logs</outputDirectory>\n            <excludes>\n                <exclude>**/*</exclude>\n            </excludes>\n        </fileSet>\n        <fileSet>\n            <directory>${basedir}/catlet</directory>\n            <outputDirectory>mycat/catlet</outputDirectory>\n            <excludes>\n                <exclude>**/*</exclude>\n            </excludes>\n        </fileSet>\n\t</fileSets>\n\n</assembly>\n"
  },
  {
    "path": "src/main/assembly/assembly-testtool.xml",
    "content": "<assembly\n\txmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\"\n\t>\n\t<includeBaseDirectory>false</includeBaseDirectory>\n\t<id>${timestamp}-testtool</id>\n\t<formats>\n\t\t<format>tar.gz</format>\n\t</formats>\n\t<fileSets>\n\t\t<fileSet>\n\t\t<directory>src/main/assembly/testtool</directory>\n\t\t\t<outputDirectory>mycat/bin</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>**/*</include>\n\t\t\t</includes>\n\t\t\t<fileMode>0755</fileMode>\n\t\t</fileSet>\n        <fileSet>\n            <directory>${basedir}</directory>\n            <outputDirectory>mycat/</outputDirectory>\n            <includes>\n                <include>version.txt</include>\n            </includes>\n        </fileSet>\n\t\t<fileSet>\n\t\t\t<directory>target</directory>\n\t\t\t<outputDirectory>mycat/lib</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>**tests.jar</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t</fileSets>\n\t<dependencySets>\n\t\t<dependencySet>\n\t\t\t<scope>test</scope>\n\t\t\t<outputDirectory>mycat/lib</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>mysql-con*</include>\n\t\t\t\t<include>junit*</include>\t\t\t\t\n\t\t\t</includes>\n\t\t</dependencySet>\n\t\t\n\t</dependencySets>\n</assembly>\n"
  },
  {
    "path": "src/main/assembly/assembly-unix.xml",
    "content": "<assembly\n\txmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n\t<id>${timestamp}-unix</id>\n\t<includeBaseDirectory>false</includeBaseDirectory>\n\t<formats>\n\t\t<format>tar.gz</format>\n\t</formats>\n\t<fileSets>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/bin</directory>\n\t\t\t<outputDirectory>mycat/bin</outputDirectory>\n            <fileMode>0755</fileMode>\n\t\t\t<includes>\n\t\t\t\t<include>mycat</include>\n\t\t\t\t<include>wrapper-aix*</include>\n\t\t\t\t<include>wrapper-hpux*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/lib</directory>\n\t\t\t<outputDirectory>mycat/lib</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>*.jar</include>\n\t\t\t\t<include>libwrapper-linux*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/conf</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/resources</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t\t<excludes>\n\t\t\t\t<exclude>*.dtd</exclude>\n\t\t\t\t<exclude>log4j*</exclude>\n\t\t\t</excludes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/assembly/conf</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t</fileSet>\n        <fileSet>\n            <directory>${basedir}</directory>\n            <outputDirectory>mycat/</outputDirectory>\n            <includes>\n                <include>version.txt</include>\n            </includes>\n        </fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/assembly/bin</directory>\n\t\t\t<outputDirectory>mycat/bin</outputDirectory>\n\t\t\t<fileMode>0755</fileMode>\n\t\t\t<includes>\n\t\t\t\t<include>*.sh</include>\n\t\t\t</includes>\n\t\t</fileSet>\n        <fileSet>\n            <directory>${basedir}/logs</directory>\n            <outputDirectory>mycat/logs</outputDirectory>\n            <excludes>\n                <exclude>**/*</exclude>\n            </excludes>\n        </fileSet>\n        <fileSet>\n            <directory>${basedir}/catlet</directory>\n            <outputDirectory>mycat/catlet</outputDirectory>\n            <excludes>\n                <exclude>**/*</exclude>\n            </excludes>\n        </fileSet>\n\t</fileSets>\n\n</assembly>\n"
  },
  {
    "path": "src/main/assembly/assembly-win.xml",
    "content": "<assembly\n\txmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n\t<id>${timestamp}-win</id>\n\t<includeBaseDirectory>false</includeBaseDirectory>\n\t<formats>\n\t\t<format>tar.gz</format>\n\t</formats>\n\t<fileSets>\n        <fileSet>\n            <directory>target/generated-resources/appassembler/jsw/mycat/bin</directory>\n            <outputDirectory>mycat/bin</outputDirectory>\n            <fileMode>0755</fileMode>\n            <includes>\n                <include>mycat.bat</include>\n                <include>wrapper-windows*</include>\n            </includes>\n        </fileSet>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/lib</directory>\n\t\t\t<outputDirectory>mycat/lib</outputDirectory>\n\t\t\t<includes>\n\t\t\t<include>*.jar</include>\n\t\t\t<include>wrapper-windows*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>target/generated-resources/appassembler/jsw/mycat/conf</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t\t<includes>\n\t\t\t<include>*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/resources</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t\t<excludes>\n\t\t\t\t<exclude>*.dtd</exclude>\n\t\t\t\t<exclude>log4j*</exclude>\n\t\t\t</excludes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/assembly/conf</directory>\n\t\t\t<outputDirectory>mycat/conf</outputDirectory>\n\t\t</fileSet>\n        <fileSet>\n            <directory>${basedir}</directory>\n            <outputDirectory>mycat/</outputDirectory>\n            <includes>\n                <include>version.txt</include>\n            </includes>\n        </fileSet>\n\t\t<fileSet>\n\t\t\t<directory>src/main/assembly/bin</directory>\n\t\t\t<outputDirectory>mycat/bin</outputDirectory>\n\t\t\t<fileMode>0755</fileMode>\n\t\t\t<includes>\n\t\t\t<include>*.bat</include>\n\t\t\t</includes>\n\t\t</fileSet>\n        <fileSet>\n            <directory>${basedir}/logs</directory>\n            <outputDirectory>mycat/logs</outputDirectory>\n            <excludes>\n                <exclude>**/*</exclude>\n            </excludes>\n        </fileSet>\n        <fileSet>\n            <directory>${basedir}/catlet</directory>\n            <outputDirectory>mycat/catlet</outputDirectory>\n            <excludes>\n                <exclude>**/*</exclude>\n            </excludes>\n        </fileSet>\n\t</fileSets>\n</assembly>\n"
  },
  {
    "path": "src/main/assembly/conf/log4j2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"WARN\">\n    <Appenders>\n        <Console name=\"Console\" target=\"SYSTEM_OUT\">\n            <PatternLayout pattern=\"%d [%-5p][%t] %m %throwable{full} (%C:%F:%L) %n\"/>\n        </Console>\n\n        <RollingFile name=\"RollingFile\" fileName=\"${sys:MYCAT_HOME}/logs/mycat.log\"\n                     filePattern=\"${sys:MYCAT_HOME}/logs/$${date:yyyy-MM}/mycat-%d{MM-dd}-%i.log.gz\">\n        <PatternLayout>\n                <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%t] (%l) - %m%n</Pattern>\n            </PatternLayout>\n            <Policies>\n                <OnStartupTriggeringPolicy/>\n                <SizeBasedTriggeringPolicy size=\"250 MB\"/>\n                <TimeBasedTriggeringPolicy/>\n            </Policies>\n        </RollingFile>\n    </Appenders>\n    <Loggers>\n        <!--<AsyncLogger name=\"io.mycat\" level=\"info\" includeLocation=\"true\" additivity=\"false\">-->\n            <!--<AppenderRef ref=\"Console\"/>-->\n            <!--<AppenderRef ref=\"RollingFile\"/>-->\n        <!--</AsyncLogger>-->\n        <asyncRoot level=\"info\" includeLocation=\"true\">\n\n            <!--<AppenderRef ref=\"Console\" />-->\n            <AppenderRef ref=\"RollingFile\"/>\n\n        </asyncRoot>\n    </Loggers>\n</Configuration>\n"
  },
  {
    "path": "src/main/assembly/testtool/test_globalseq_insert_perf.bat",
    "content": "\nREM check JAVA_HOME & java\nset \"JAVA_CMD=\"%JAVA_HOME%/bin/java\"\"\nif \"%JAVA_HOME%\" == \"\" goto noJavaHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto mainEntry\n:noJavaHome\necho ---------------------------------------------------\necho WARN: JAVA_HOME environment variable is not set. \necho ---------------------------------------------------\nset \"JAVA_CMD=java\"\n:mainEntry\nREM set HOME_DIR\nset \"CURR_DIR=%cd%\"\ncd ..\nset \"MYCAT_HOME=%cd%\"\ncd %CURR_DIR%\n\"%JAVA_CMD%\" -Xms256M -Xmx1G -XX:MaxPermSize=64M  -DMYCAT_HOME=%MYCAT_HOME% -cp \"..\\conf;..\\lib\\*\" io.mycat.performance.TestInsertPerf %1 %2 %3 %4 %5"
  },
  {
    "path": "src/main/assembly/testtool/test_globaltable_insert_perf.bat",
    "content": "\nREM check JAVA_HOME & java\nset \"JAVA_CMD=%JAVA_HOME%/bin/java\"\nif \"%JAVA_HOME%\" == \"\" goto noJavaHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto mainEntry\n:noJavaHome\necho ---------------------------------------------------\necho WARN: JAVA_HOME environment variable is not set. \necho ---------------------------------------------------\nset \"JAVA_CMD=java\"\n:mainEntry\nREM set HOME_DIR\nset \"CURR_DIR=%cd%\"\ncd ..\nset \"MYCAT_HOME=%cd%\"\ncd %CURR_DIR%\n\"%JAVA_CMD%\" -Xms256M -Xmx1G -XX:MaxPermSize=64M  -DMYCAT_HOME=%MYCAT_HOME% -cp \"..\\conf;..\\lib\\*\" io.mycat.performance.TestGlobalTableInsertPerf %1 %2 %3 %4 %5"
  },
  {
    "path": "src/main/assembly/testtool/test_globaltable_insert_perf.sh",
    "content": "#!/bin/bash\n\necho \"check JAVA_HOME & java\"\nJAVA_CMD=$JAVA_HOME/bin/java\nMAIN_CLASS=io.mycat.performance.TestGlobalTableInsertPerf\nif [ ! -d \"$JAVA_HOME\" ]; then\n    echo ---------------------------------------------------\n    echo WARN: JAVA_HOME environment variable is not set. \n    echo ---------------------------------------------------\n    JAVA_CMD=java\nfi\n\necho \"---------set HOME_DIR------------\"\nCURR_DIR=`pwd`\ncd ..\nMYCAT_HOME=`pwd`\ncd $CURR_DIR\n$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\n"
  },
  {
    "path": "src/main/assembly/testtool/test_stand_insert_perf.bat",
    "content": "\nREM check JAVA_HOME & java\nset \"JAVA_CMD=%JAVA_HOME%/bin/java\"\nif \"%JAVA_HOME%\" == \"\" goto noJavaHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto mainEntry\n:noJavaHome\necho ---------------------------------------------------\necho WARN: JAVA_HOME environment variable is not set. \necho ---------------------------------------------------\nset \"JAVA_CMD=java\"\n:mainEntry\nREM set HOME_DIR\nset \"CURR_DIR=%cd%\"\ncd ..\nset \"MYCAT_HOME=%cd%\"\ncd %CURR_DIR%\n\"%JAVA_CMD%\" -Xms256M -Xmx1G -XX:MaxPermSize=64M  -DMYCAT_HOME=%MYCAT_HOME% -cp \"..\\conf;..\\lib\\*\" io.mycat.performance.TestInsertPerf %1 %2 %3 %4 %5"
  },
  {
    "path": "src/main/assembly/testtool/test_stand_insert_perf.sh",
    "content": "#!/bin/bash\n\necho \"check JAVA_HOME & java\"\nJAVA_CMD=$JAVA_HOME/bin/java\nMAIN_CLASS=io.mycat.performance.TestInsertPerf\nif [ ! -d \"$JAVA_HOME\" ]; then\n    echo ---------------------------------------------------\n    echo WARN: JAVA_HOME environment variable is not set. \n    echo ---------------------------------------------------\n    JAVA_CMD=java\nfi\n\necho \"---------set HOME_DIR------------\"\nCURR_DIR=`pwd`\ncd ..\nMYCAT_HOME=`pwd`\ncd $CURR_DIR\n$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\n"
  },
  {
    "path": "src/main/assembly/testtool/test_stand_merge_sel_perf.bat",
    "content": "\nREM check JAVA_HOME & java\nset \"JAVA_CMD=%JAVA_HOME%/bin/java\"\nif \"%JAVA_HOME%\" == \"\" goto noJavaHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto mainEntry\n:noJavaHome\necho ---------------------------------------------------\necho WARN: JAVA_HOME environment variable is not set. \necho ---------------------------------------------------\nset \"JAVA_CMD=java\"\n:mainEntry\nREM set HOME_DIR\nset \"CURR_DIR=%cd%\"\ncd ..\nset \"MYCAT_HOME=%cd%\"\ncd %CURR_DIR%\n\"%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"
  },
  {
    "path": "src/main/assembly/testtool/test_stand_merge_sel_perf.sh",
    "content": "#!/bin/bash\n\necho \"check JAVA_HOME & java\"\nJAVA_CMD=$JAVA_HOME/bin/java\nMAIN_CLASS=io.mycat.performance.TestMergeSelectPerf\nif [ ! -d \"$JAVA_HOME\" ]; then\n    echo ---------------------------------------------------\n    echo WARN: JAVA_HOME environment variable is not set. \n    echo ---------------------------------------------------\n    JAVA_CMD=java\nfi\n\necho \"---------set HOME_DIR------------\"\nCURR_DIR=`pwd`\ncd ..\nMYCAT_HOME=`pwd`\ncd $CURR_DIR\n$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\n"
  },
  {
    "path": "src/main/assembly/testtool/test_stand_select_perf.bat",
    "content": "\nREM check JAVA_HOME & java\nset \"JAVA_CMD=%JAVA_HOME%/bin/java\"\nif \"%JAVA_HOME%\" == \"\" goto noJavaHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto mainEntry\n:noJavaHome\necho ---------------------------------------------------\necho WARN: JAVA_HOME environment variable is not set. \necho ---------------------------------------------------\nset \"JAVA_CMD=java\"\n:mainEntry\nREM set HOME_DIR\nset \"CURR_DIR=%cd%\"\ncd ..\nset \"MYCAT_HOME=%cd%\"\ncd %CURR_DIR%\n\"%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 "
  },
  {
    "path": "src/main/assembly/testtool/test_stand_select_perf.sh",
    "content": "#!/bin/bash\n\necho \"check JAVA_HOME & java\"\nJAVA_CMD=$JAVA_HOME/bin/java\nMAIN_CLASS=io.mycat.performance.TestSelectPerf\nif [ ! -d \"$JAVA_HOME\" ]; then\n    echo ---------------------------------------------------\n    echo WARN: JAVA_HOME environment variable is not set. \n    echo ---------------------------------------------------\n    JAVA_CMD=java\nfi\n\necho \"---------set HOME_DIR------------\"\nCURR_DIR=`pwd`\ncd ..\nMYCAT_HOME=`pwd`\ncd $CURR_DIR\n$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\n"
  },
  {
    "path": "src/main/assembly/testtool/test_stand_update_perf.bat",
    "content": "\nREM check JAVA_HOME & java\nset \"JAVA_CMD=%JAVA_HOME%/bin/java\"\nif \"%JAVA_HOME%\" == \"\" goto noJavaHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto mainEntry\n:noJavaHome\necho ---------------------------------------------------\necho WARN: JAVA_HOME environment variable is not set. \necho ---------------------------------------------------\nset \"JAVA_CMD=java\"\n:mainEntry\nREM set HOME_DIR\nset \"CURR_DIR=%cd%\"\ncd ..\nset \"MYCAT_HOME=%cd%\"\ncd %CURR_DIR%\n\"%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"
  },
  {
    "path": "src/main/assembly/testtool/test_stand_update_perf.sh",
    "content": "#!/bin/bash\n\necho \"check JAVA_HOME & java\"\nJAVA_CMD=$JAVA_HOME/bin/java\nMAIN_CLASS=io.mycat.performance.TestUpdatePerf\nif [ ! -d \"$JAVA_HOME\" ]; then\n    echo ---------------------------------------------------\n    echo WARN: JAVA_HOME environment variable is not set. \n    echo ---------------------------------------------------\n    JAVA_CMD=java\nfi\n\necho \"---------set HOME_DIR------------\"\nCURR_DIR=`pwd`\ncd ..\nMYCAT_HOME=`pwd`\ncd $CURR_DIR\n$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\n"
  },
  {
    "path": "src/main/java/io/mycat/MycatServer.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.nio.channels.AsynchronousChannelGroup;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.recipes.locks.InterProcessMutex;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.io.Files;\nimport com.google.common.util.concurrent.ListeningExecutorService;\nimport com.google.common.util.concurrent.MoreExecutors;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.heartbeat.zkprocess.MycatLeaderLatch;\nimport io.mycat.backend.mysql.nio.handler.MultiNodeCoordinator;\nimport io.mycat.backend.mysql.xa.CoordinatorLogEntry;\nimport io.mycat.backend.mysql.xa.ParticipantLogEntry;\nimport io.mycat.backend.mysql.xa.TxState;\nimport io.mycat.backend.mysql.xa.XACommitCallback;\nimport io.mycat.backend.mysql.xa.XARollbackCallback;\nimport io.mycat.backend.mysql.xa.recovery.Repository;\nimport io.mycat.backend.mysql.xa.recovery.impl.FileSystemRepository;\nimport io.mycat.buffer.BufferPool;\nimport io.mycat.buffer.DirectByteBufferPool;\nimport io.mycat.buffer.NettyBufferPool;\nimport io.mycat.cache.CacheService;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.classloader.DynaClassLoader;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.config.table.structure.MySQLTableStructureDetector;\nimport io.mycat.manager.ManagerConnectionFactory;\nimport io.mycat.memory.MyCatMemory;\nimport io.mycat.net.AIOAcceptor;\nimport io.mycat.net.AIOConnector;\nimport io.mycat.net.NIOAcceptor;\nimport io.mycat.net.NIOConnector;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.NIOReactorPool;\nimport io.mycat.net.SocketAcceptor;\nimport io.mycat.net.SocketConnector;\nimport io.mycat.route.MyCATSequnceProcessor;\nimport io.mycat.route.RouteService;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.route.sequence.handler.SequenceHandler;\nimport io.mycat.server.ServerConnectionFactory;\nimport io.mycat.server.interceptor.SQLInterceptor;\nimport io.mycat.server.interceptor.impl.GlobalTableUtil;\nimport io.mycat.sqlengine.OneRawSQLQueryResultHandler;\nimport io.mycat.sqlengine.SQLJob;\nimport io.mycat.statistic.SQLRecorder;\nimport io.mycat.statistic.stat.SqlResultSizeRecorder;\nimport io.mycat.statistic.stat.UserStat;\nimport io.mycat.statistic.stat.UserStatAnalyzer;\nimport io.mycat.util.ExecutorUtil;\nimport io.mycat.util.NameableExecutor;\nimport io.mycat.util.TimeUtil;\nimport io.mycat.util.ZKUtils;\n\n/**\n * @author mycat\n */\npublic class MycatServer {\n\n    public static final String NAME = \"MyCat\";\n    private static final long LOG_WATCH_DELAY = 60000L;\n    private static final long TIME_UPDATE_PERIOD = 20L;\n    private static final long DEFAULT_SQL_STAT_RECYCLE_PERIOD = 5 * 1000L;\n    private static final long DEFAULT_OLD_CONNECTION_CLEAR_PERIOD = 5 * 1000L;\n    private static final long DEFAULT_DATANODE_CALC_ACTIVECOUNT = 1000L;\n\n    private static final MycatServer INSTANCE = new MycatServer();\n    private static final Logger LOGGER = LoggerFactory.getLogger(\"MycatServer\");\n    private static final Repository fileRepository = new FileSystemRepository();\n    private final RouteService routerService;\n    private final CacheService cacheService;\n    private Properties dnIndexProperties;\n\n    //AIO连接群组\n    private AsynchronousChannelGroup[] asyncChannelGroups;\n    private volatile int channelIndex = 0;\n\n    //全局序列号\n//\tprivate final MyCATSequnceProcessor sequnceProcessor = new MyCATSequnceProcessor();\n    private final DynaClassLoader catletClassLoader;\n    private final SQLInterceptor sqlInterceptor;\n    private volatile int nextProcessor;\n\n    // System Buffer Pool Instance\n    private BufferPool bufferPool;\n    private boolean aio = false;\n\n    //XA事务全局ID生成\n    private final AtomicLong xaIDInc = new AtomicLong();\n    //sequence处理对象\n    private SequenceHandler sequenceHandler;\n\n    /**\n     * Mycat 内存管理类\n     */\n    private MyCatMemory myCatMemory = null;\n\n    public static final MycatServer getInstance() {\n        return INSTANCE;\n    }\n\n    private final MycatConfig config;\n    private final ScheduledExecutorService scheduler;\n    private final ScheduledExecutorService heartbeatScheduler;\n    private final SQLRecorder sqlRecorder;\n    private final AtomicBoolean isOnline;\n    private final long startupTime;\n    private NIOProcessor[] processors;\n    private SocketConnector connector;\n    private NameableExecutor businessExecutor;\n    private NameableExecutor sequenceExecutor;\n    private NameableExecutor timerExecutor;\n    private ListeningExecutorService listeningExecutorService;\n    private InterProcessMutex dnindexLock;\n    private long totalNetWorkBufferSize = 0;\n\n    private volatile MycatLeaderLatch leaderLatch;\n\n    private final AtomicBoolean startup = new AtomicBoolean(false);\n\n    private ScheduledFuture<?> recycleSqlStatFuture = null;\n\n    private MycatServer() {\n\n        //读取文件配置\n        this.config = new MycatConfig();\n\n        //定时线程池，单线程线程池\n        scheduler = Executors.newSingleThreadScheduledExecutor();\n\n        //心跳调度独立出来，避免被其他任务影响\n        heartbeatScheduler = Executors.newSingleThreadScheduledExecutor();\n\n        //SQL记录器\n        this.sqlRecorder = new SQLRecorder(config.getSystem().getSqlRecordCount());\n\n        /**\n         * 是否在线，MyCat manager中有命令控制\n         * | offline | Change MyCat status to OFF |\n         * | online | Change MyCat status to ON |\n         */\n        this.isOnline = new AtomicBoolean(true);\n\n        //缓存服务初始化\n        cacheService = new CacheService();\n\n        //路由计算初始化\n        routerService = new RouteService(cacheService);\n\n        // load datanode active index from properties\n        dnIndexProperties = loadDnIndexProps();\n        try {\n            //SQL解析器\n            sqlInterceptor = (SQLInterceptor) Class.forName(\n                    config.getSystem().getSqlInterceptor()).newInstance();\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n\n        //catlet加载器\n        catletClassLoader = new DynaClassLoader(SystemConfig.getHomePath()\n                + File.separator + \"catlet\", config.getSystem().getCatletClassCheckSeconds());\n\n        //记录启动时间\n        this.startupTime = TimeUtil.currentTimeMillis();\n        if (isUseZkSwitch()) {\n            String path = ZKUtils.getZKBasePath() + \"lock/dnindex.lock\";\n            dnindexLock = new InterProcessMutex(ZKUtils.getConnection(), path);\n\n        }\n\n    }\n\n    public AtomicBoolean getStartup() {\n        return startup;\n    }\n\n    public long getTotalNetWorkBufferSize() {\n        return totalNetWorkBufferSize;\n    }\n\n    public BufferPool getBufferPool() {\n        return bufferPool;\n    }\n\n    public NameableExecutor getTimerExecutor() {\n        return timerExecutor;\n    }\n\n    public DynaClassLoader getCatletClassLoader() {\n        return catletClassLoader;\n    }\n\n    public MyCATSequnceProcessor getSequnceProcessor() {\n        return MyCATSequnceProcessor.getInstance();\n    }\n\n    public SQLInterceptor getSqlInterceptor() {\n        return sqlInterceptor;\n    }\n\n    public ScheduledExecutorService getScheduler() {\n        return scheduler;\n    }\n\n    public String genXATXID() {\n        long seq = this.xaIDInc.incrementAndGet();\n        if (seq < 0) {\n            synchronized (xaIDInc) {\n                if (xaIDInc.get() < 0) {\n                    xaIDInc.set(0);\n                }\n                seq = xaIDInc.incrementAndGet();\n            }\n        }\n        return \"'Mycat.\" + this.getConfig().getSystem().getMycatNodeId() + \".\" + seq + \"'\";\n    }\n\n    public String getXATXIDGLOBAL() {\n        return \"'\" + getUUID() + \"'\";\n    }\n\n    public static String getUUID() {\n        String s = UUID.randomUUID().toString();\n        //去掉“-”符号\n        return s.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24);\n    }\n\n    public MyCatMemory getMyCatMemory() {\n        return myCatMemory;\n    }\n\n    /**\n     * get next AsynchronousChannel ,first is exclude if multi\n     * AsynchronousChannelGroups\n     *\n     * @return\n     */\n    public AsynchronousChannelGroup getNextAsyncChannelGroup() {\n        if (asyncChannelGroups.length == 1) {\n            return asyncChannelGroups[0];\n        } else {\n            int index = (++channelIndex) % asyncChannelGroups.length;\n            if (index == 0) {\n                ++channelIndex;\n                return asyncChannelGroups[1];\n            } else {\n                return asyncChannelGroups[index];\n            }\n\n        }\n    }\n\n    public MycatConfig getConfig() {\n        return config;\n    }\n\n    public void beforeStart() {\n        String home = SystemConfig.getHomePath();\n\n\n        //ZkConfig.instance().initZk();\n    }\n\n    public void startup() throws IOException {\n\n        SystemConfig system = config.getSystem();\n        int processorCount = system.getProcessors();\n\n        //init RouteStrategyFactory first\n        RouteStrategyFactory.init();\n\n        // server startup\n        LOGGER.info(NAME + \" is ready to startup ...\");\n        String inf = \"Startup processors ...,total processors:\"\n                + system.getProcessors() + \",aio thread pool size:\"\n                + system.getProcessorExecutor()\n                + \"    \\r\\n each process allocated socket buffer pool \"\n                + \" bytes ,a page size:\"\n                + system.getBufferPoolPageSize()\n                + \"  a page's chunk number(PageSize/ChunkSize) is:\"\n                + (system.getBufferPoolPageSize()\n                / system.getBufferPoolChunkSize())\n                + \"  buffer page's number is:\"\n                + system.getBufferPoolPageNumber();\n        LOGGER.info(inf);\n        LOGGER.info(\"sysconfig params:\" + system.toString());\n\n        // startup manager\n        ManagerConnectionFactory mf = new ManagerConnectionFactory();\n        ServerConnectionFactory sf = new ServerConnectionFactory();\n        SocketAcceptor manager = null;\n        SocketAcceptor server = null;\n        aio = (system.getUsingAIO() == 1);\n\n        // startup processors\n        int threadPoolSize = system.getProcessorExecutor();\n        processors = new NIOProcessor[processorCount];\n        // a page size\n        int bufferPoolPageSize = system.getBufferPoolPageSize();\n        // total page number\n        short bufferPoolPageNumber = system.getBufferPoolPageNumber();\n        //minimum allocation unit\n        short bufferPoolChunkSize = system.getBufferPoolChunkSize();\n\n        int socketBufferLocalPercent = system.getProcessorBufferLocalPercent();\n        int bufferPoolType = system.getProcessorBufferPoolType();\n\n        switch (bufferPoolType) {\n            case 0:\n                bufferPool = new DirectByteBufferPool(bufferPoolPageSize, bufferPoolChunkSize,\n                        bufferPoolPageNumber, system.getFrontSocketSoRcvbuf());\n\n\n                totalNetWorkBufferSize = bufferPoolPageSize * bufferPoolPageNumber;\n                break;\n            case 1:\n                /**\n                 * todo 对应权威指南修改：\n                 *\n                 * bytebufferarena由6个bytebufferlist组成，这六个list有减少内存碎片的机制\n                 * 每个bytebufferlist由多个bytebufferchunk组成，每个list也有减少内存碎片的机制\n                 * 每个bytebufferchunk由多个page组成，平衡二叉树管理内存使用状态，计算灵活\n                 * 设置的pagesize对应bytebufferarena里面的每个bytebufferlist的每个bytebufferchunk的buffer长度\n                 * bufferPoolChunkSize对应每个bytebufferchunk的每个page的长度\n                 * bufferPoolPageNumber对应每个bytebufferlist有多少个bytebufferchunk\n                 */\n\n                totalNetWorkBufferSize = 6 * bufferPoolPageSize * bufferPoolPageNumber;\n                break;\n            case 2:\n                bufferPool = new NettyBufferPool(bufferPoolChunkSize);\n                LOGGER.info(\"Use Netty Buffer Pool\");\n\n                break;\n            default:\n                bufferPool = new DirectByteBufferPool(bufferPoolPageSize, bufferPoolChunkSize,\n                        bufferPoolPageNumber, system.getFrontSocketSoRcvbuf());\n                ;\n                totalNetWorkBufferSize = bufferPoolPageSize * bufferPoolPageNumber;\n        }\n\n        /**\n         * Off Heap For Merge/Order/Group/Limit 初始化\n         */\n        if (system.getUseOffHeapForMerge() == 1) {\n            try {\n                myCatMemory = new MyCatMemory(system, totalNetWorkBufferSize);\n            } catch (NoSuchFieldException e) {\n                LOGGER.error(\"NoSuchFieldException\", e);\n            } catch (IllegalAccessException e) {\n                LOGGER.error(\"Error\", e);\n            }\n        }\n        businessExecutor = ExecutorUtil.create(\"BusinessExecutor\",\n                threadPoolSize);\n        sequenceExecutor = ExecutorUtil.create(\"SequenceExecutor\", threadPoolSize);\n        timerExecutor = ExecutorUtil.create(\"Timer\", system.getTimerExecutor());\n        listeningExecutorService = MoreExecutors.listeningDecorator(businessExecutor);\n\n        for (int i = 0; i < processors.length; i++) {\n            processors[i] = new NIOProcessor(\"Processor\" + i, bufferPool,\n                    businessExecutor);\n        }\n\n        if (aio) {\n            LOGGER.info(\"using aio network handler \");\n            asyncChannelGroups = new AsynchronousChannelGroup[processorCount];\n            // startup connector\n            connector = new AIOConnector();\n            for (int i = 0; i < processors.length; i++) {\n                asyncChannelGroups[i] = AsynchronousChannelGroup.withFixedThreadPool(processorCount,\n                        new ThreadFactory() {\n                            private int inx = 1;\n\n                            @Override\n                            public Thread newThread(Runnable r) {\n                                Thread th = new Thread(r);\n                                //TODO\n                                th.setName(DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + \"AIO\" + (inx++));\n                                LOGGER.info(\"created new AIO thread \" + th.getName());\n                                return th;\n                            }\n                        }\n                );\n            }\n            manager = new AIOAcceptor(NAME + \"Manager\", system.getBindIp(),\n                    system.getManagerPort(), system.getServerBacklog(), mf, this.asyncChannelGroups[0]);\n\n            // startup server\n\n            server = new AIOAcceptor(NAME + \"Server\", system.getBindIp(),\n                    system.getServerPort(), system.getServerBacklog(), sf, this.asyncChannelGroups[0]);\n\n        } else {\n            LOGGER.info(\"using nio network handler \");\n\n            NIOReactorPool reactorPool = new NIOReactorPool(\n                    DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + \"NIOREACTOR\",\n                    processors.length);\n            connector = new NIOConnector(DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + \"NIOConnector\", reactorPool);\n            ((NIOConnector) connector).start();\n\n            manager = new NIOAcceptor(DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + NAME\n                    + \"Manager\", system.getBindIp(), system.getManagerPort(), system.getServerBacklog(), mf, reactorPool);\n\n            server = new NIOAcceptor(DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + NAME\n                    + \"Server\", system.getBindIp(), system.getServerPort(), system.getServerBacklog(), sf, reactorPool);\n        }\n        // manager start\n        manager.start();\n        LOGGER.info(manager.getName() + \" is started and listening on \" + manager.getPort());\n        server.start();\n\n        // server started\n        LOGGER.info(server.getName() + \" is started and listening on \" + server.getPort());\n\n        LOGGER.info(\"===============================================\");\n\n        // init datahost\n        Map<String, PhysicalDBPool> dataHosts = config.getDataHosts();\n        LOGGER.info(\"Initialize dataHost ...\");\n        for (PhysicalDBPool node : dataHosts.values()) {\n            String index = dnIndexProperties.getProperty(node.getHostName(), \"0\");\n            if (!\"0\".equals(index)) {\n                LOGGER.info(\"init datahost: \" + node.getHostName() + \"  to use datasource index:\" + index);\n            }\n            node.init(Integer.parseInt(index));\n            node.startHeartbeat();\n        }\n\n        long dataNodeIldeCheckPeriod = system.getDataNodeIdleCheckPeriod();\n\n        heartbeatScheduler.scheduleAtFixedRate(updateTime(), 0L, TIME_UPDATE_PERIOD, TimeUnit.MILLISECONDS);\n        heartbeatScheduler.scheduleAtFixedRate(processorCheck(), 0L, system.getProcessorCheckPeriod(), TimeUnit.MILLISECONDS);\n        heartbeatScheduler.scheduleAtFixedRate(dataNodeConHeartBeatCheck(dataNodeIldeCheckPeriod), 0L, dataNodeIldeCheckPeriod, TimeUnit.MILLISECONDS);\n        heartbeatScheduler.scheduleAtFixedRate(dataNodeHeartbeat(), 0L, system.getDataNodeHeartbeatPeriod(), TimeUnit.MILLISECONDS);\n        heartbeatScheduler.scheduleAtFixedRate(dataSourceOldConsClear(), 0L, DEFAULT_OLD_CONNECTION_CLEAR_PERIOD, TimeUnit.MILLISECONDS);\n        heartbeatScheduler.scheduleAtFixedRate(dataNodeCalcActiveCons(), 0L, DEFAULT_DATANODE_CALC_ACTIVECOUNT, TimeUnit.MILLISECONDS);\n        //\n        scheduler.schedule(catletClassClear(), 30000, TimeUnit.MILLISECONDS);\n\n        if (system.getCheckTableConsistency() == 1) {\n            scheduler.scheduleAtFixedRate(tableStructureCheck(), 0L, system.getCheckTableConsistencyPeriod(), TimeUnit.MILLISECONDS);\n        }\n\n        ensureSqlstatRecycleFuture();\n\n        if (system.getUseGlobleTableCheck() == 1) {    // 全局表一致性检测是否开启\n//\t\t\tscheduler.scheduleAtFixedRate(glableTableConsistencyCheck(), 0L, system.getGlableTableCheckPeriod(), TimeUnit.MILLISECONDS);\n        }\n\n        //定期清理结果集排行榜，控制拒绝策略\n        scheduler.scheduleAtFixedRate(resultSetMapClear(), 0L, system.getClearBigSqLResultSetMapMs(), TimeUnit.MILLISECONDS);\n\n        //xa 事务定时检查 是否全部提交 或者部分提交  部分回滚, 进行补充提交补充回滚.\n        scheduler.scheduleAtFixedRate(xaTaskCheck(), 0L, 10 * 1000, TimeUnit.MILLISECONDS);\n//        new Thread(tableStructureCheck()).start();\n\n        //XA Init recovery Log\n        LOGGER.info(\"===============================================\");\n        LOGGER.info(\"Perform XA recovery log ...\");\n        CoordinatorLogEntry[] coordinatorLogEntries = getCoordinatorLogEntries();\n        putXARecoveryLogToMemory(coordinatorLogEntries);\n        performXARecoveryLog(coordinatorLogEntries);\n        LOGGER.info(\"Perform XA recovery log end...\");\n\n        if (isUseZkSwitch()) {\n            //首次启动如果发现zk上dnindex为空，则将本地初始化上zk\n            initZkDnindex();\n\n            leaderLatch = new MycatLeaderLatch(\"heartbeat/leader\");\n            try {\n                leaderLatch.start();\n            } catch (Exception e) {\n                LOGGER.error(e.getMessage(), e);\n                e.printStackTrace();\n            }\n        }\n        initRuleData();\n\n        startup.set(true);\n    }\n\n    public void ensureSqlstatRecycleFuture() {\n        if (config.getSystem().getUseSqlStat() == 1) {\n            if(recycleSqlStatFuture == null){\n                recycleSqlStatFuture = scheduler\n                    .scheduleAtFixedRate(recycleSqlStat(), 0L, DEFAULT_SQL_STAT_RECYCLE_PERIOD, TimeUnit.MILLISECONDS);\n            }\n        } else {\n            if (recycleSqlStatFuture != null) {\n                recycleSqlStatFuture.cancel(false);\n                recycleSqlStatFuture = null;\n            }\n        }\n    }\n\n    public void initRuleData() {\n        if (!isUseZk()) return;\n        InterProcessMutex ruleDataLock = null;\n        try {\n            File file = new File(SystemConfig.getHomePath(), \"conf\" + File.separator + \"ruledata\");\n            if (!file.exists()) {\n                file.mkdir();\n            }\n            String path = ZKUtils.getZKBasePath() + \"lock/ruledata.lock\";\n            ruleDataLock = new InterProcessMutex(ZKUtils.getConnection(), path);\n            ruleDataLock.acquire(30, TimeUnit.SECONDS);\n            File[] childFiles = file.listFiles();\n            if (childFiles != null && childFiles.length > 0) {\n                String basePath = ZKUtils.getZKBasePath() + \"ruledata/\";\n                for (File childFile : childFiles) {\n                    CuratorFramework zk = ZKUtils.getConnection();\n                    if (zk.checkExists().forPath(basePath + childFile.getName()) == null) {\n                        zk.create().creatingParentsIfNeeded().forPath(basePath + childFile.getName(), Files.toByteArray(childFile));\n                    }\n                }\n            }\n\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        } finally {\n            try {\n                if (ruleDataLock != null)\n                    ruleDataLock.release();\n            } catch (Exception e) {\n                throw new RuntimeException(e);\n            }\n        }\n    }\n\n    private void initZkDnindex() {\n        try {\n            File file = new File(SystemConfig.getHomePath(), \"conf\" + File.separator + \"dnindex.properties\");\n            dnindexLock.acquire(30, TimeUnit.SECONDS);\n            String path = ZKUtils.getZKBasePath() + \"bindata/dnindex.properties\";\n            CuratorFramework zk = ZKUtils.getConnection();\n            if (zk.checkExists().forPath(path) == null) {\n                zk.create().creatingParentsIfNeeded().forPath(path, Files.toByteArray(file));\n            }\n\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        } finally {\n            try {\n                dnindexLock.release();\n            } catch (Exception e) {\n                throw new RuntimeException(e);\n            }\n        }\n    }\n\n    public void reloadDnIndex() {\n        if (MycatServer.getInstance().getProcessors() == null) return;\n        // load datanode active index from properties\n        dnIndexProperties = loadDnIndexProps();\n        // init datahost\n        Map<String, PhysicalDBPool> dataHosts = config.getDataHosts();\n        LOGGER.info(\"reInitialize dataHost ...\");\n        for (PhysicalDBPool node : dataHosts.values()) {\n            String index = dnIndexProperties.getProperty(node.getHostName(), \"0\");\n            if (!\"0\".equals(index)) {\n                LOGGER.info(\"reinit datahost: \" + node.getHostName() + \"  to use datasource index:\" + index);\n            }\n            node.switchSource(Integer.parseInt(index), true, \"reload dnindex\");\n\n        }\n    }\n\n    private Runnable catletClassClear() {\n        return new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    catletClassLoader.clearUnUsedClass();\n                } catch (Exception e) {\n                    LOGGER.warn(\"catletClassClear err \" + e);\n                }\n            }\n\n            ;\n        };\n    }\n\n\n    /**\n     * 清理 reload @@config_all 后，老的 connection 连接\n     *\n     * @return\n     */\n    private Runnable dataSourceOldConsClear() {\n        return new Runnable() {\n            @Override\n            public void run() {\n                timerExecutor.execute(new Runnable() {\n                    @Override\n                    public void run() {\n\n                        long sqlTimeout = MycatServer.getInstance().getConfig().getSystem().getSqlExecuteTimeout() * 1000L;\n\n                        //根据 lastTime 确认事务的执行， 超过 sqlExecuteTimeout 阀值 close connection\n                        long currentTime = TimeUtil.currentTimeMillis();\n                        Iterator<BackendConnection> iter = NIOProcessor.backends_old.iterator();\n                        while (iter.hasNext()) {\n                            BackendConnection con = iter.next();\n                            long lastTime = con.getLastTime();\n                            if (currentTime - lastTime > sqlTimeout) {\n                                con.close(\"clear old backend connection ...\");\n                                iter.remove();\n                            }\n                        }\n                    }\n                });\n            }\n\n            ;\n        };\n    }\n\n\n    /**\n     * 在bufferpool使用率大于使用率阈值时不清理\n     * 在bufferpool使用率小于使用率阈值时清理大结果集清单内容\n     */\n    private Runnable resultSetMapClear() {\n        return new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    BufferPool bufferPool = getBufferPool();\n                    long bufferSize = bufferPool.size();\n                    long bufferCapacity = bufferPool.capacity();\n                    long bufferUsagePercent = (bufferCapacity - bufferSize) * 100 / bufferCapacity;\n                    if (bufferUsagePercent < config.getSystem().getBufferUsagePercent()) {\n                        Map<String, UserStat> map = UserStatAnalyzer.getInstance().getUserStatMap();\n                        Set<String> userSet = config.getUsers().keySet();\n                        for (String user : userSet) {\n                            UserStat userStat = map.get(user);\n                            if (userStat != null) {\n                                SqlResultSizeRecorder recorder = userStat.getSqlResultSizeRecorder();\n                                //System.out.println(recorder.getSqlResultSet().size());\n                                recorder.clearSqlResultSet();\n                            }\n                        }\n                    }\n                } catch (Exception e) {\n                    LOGGER.warn(\"resultSetMapClear err \" + e);\n                }\n            }\n\n            ;\n        };\n    }\n\n    private Properties loadDnIndexProps() {\n        Properties prop = new Properties();\n        File file = new File(SystemConfig.getHomePath(), \"conf\" + File.separator + \"dnindex.properties\");\n        if (!file.exists()) {\n            return prop;\n        }\n        FileInputStream filein = null;\n        try {\n            filein = new FileInputStream(file);\n            prop.load(filein);\n        } catch (Exception e) {\n            LOGGER.warn(\"load DataNodeIndex err:\" + e);\n        } finally {\n            if (filein != null) {\n                try {\n                    filein.close();\n                } catch (IOException e) {\n                }\n            }\n        }\n        return prop;\n    }\n\n\n    public synchronized boolean saveDataHostIndexToZk(String dataHost, int curIndex) {\n        boolean result = false;\n        try {\n\n            try {\n                dnindexLock.acquire(30, TimeUnit.SECONDS);\n                String path = ZKUtils.getZKBasePath() + \"bindata/dnindex.properties\";\n\n                Map<String, String> propertyMap = new HashMap<>();\n                propertyMap.put(dataHost, String.valueOf(curIndex));\n                result = ZKUtils.writeProperty(path, propertyMap);\n            } finally {\n                dnindexLock.release();\n            }\n        } catch (Exception e) {\n            LOGGER.warn(\"saveDataHostIndexToZk err:\", e);\n        }\n        return result;\n    }\n\n    /**\n     * save cur datanode index to properties file\n     *\n     * @param\n     * @param curIndex\n     */\n    public synchronized void saveDataHostIndex(String dataHost, int curIndex) {\n        File file = new File(SystemConfig.getHomePath(), \"conf\" + File.separator + \"dnindex.properties\");\n        FileOutputStream fileOut = null;\n        try {\n            String oldIndex = dnIndexProperties.getProperty(dataHost);\n            String newIndex = String.valueOf(curIndex);\n            if (newIndex.equals(oldIndex)) {\n                return;\n            }\n\n            dnIndexProperties.setProperty(dataHost, newIndex);\n            LOGGER.info(\"save DataHost index  \" + dataHost + \" cur index \" + curIndex);\n\n            File parent = file.getParentFile();\n            if (parent != null && !parent.exists()) {\n                parent.mkdirs();\n            }\n\n            fileOut = new FileOutputStream(file);\n            dnIndexProperties.store(fileOut, \"update\");\n\n//\t\t\tif(isUseZkSwitch()) {\n//\t\t\t\t// save to  zk\n//\t\t\t\ttry {\n//\t\t\t\t\tdnindexLock.acquire(30,TimeUnit.SECONDS)   ;\n//\t\t\t\t\tString path = ZKUtils.getZKBasePath() + \"bindata/dnindex.properties\";\n//\t\t\t\t\tCuratorFramework zk = ZKUtils.getConnection();\n//\t\t\t\t\tif(zk.checkExists().forPath(path)==null) {\n//\t\t\t\t\t\tzk.create().creatingParentsIfNeeded().forPath(path, Files.toByteArray(file));\n//\t\t\t\t\t} else{\n//\t\t\t\t\t\tbyte[] data=\tzk.getData().forPath(path);\n//\t\t\t\t\t\tByteArrayOutputStream out=new ByteArrayOutputStream();\n//\t\t\t\t\t\tProperties properties=new Properties();\n//\t\t\t\t\t\tproperties.load(new ByteArrayInputStream(data));\n//\t\t\t\t\t\t if(!String.valueOf(curIndex).equals(properties.getProperty(dataHost))) {\n//\t\t\t\t\t\t\t properties.setProperty(dataHost, String.valueOf(curIndex));\n//\t\t\t\t\t\t\t properties.store(out, \"update\");\n//\t\t\t\t\t\t\t zk.setData().forPath(path, out.toByteArray());\n//\t\t\t\t\t\t }\n//\t\t\t\t\t}\n//\n//\t\t\t\t}finally {\n//\t\t\t\t dnindexLock.release();\n//\t\t\t\t}\n//\t\t\t}\n        } catch (Exception e) {\n            LOGGER.warn(\"saveDataNodeIndex err:\", e);\n        } finally {\n            if (fileOut != null) {\n                try {\n                    fileOut.close();\n                } catch (IOException e) {\n                }\n            }\n        }\n\n    }\n\n\n    private boolean isUseZk() {\n        String loadZk = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_FLAG);\n        return \"true\".equalsIgnoreCase(loadZk);\n    }\n\n    public boolean isUseZkSwitch() {\n        MycatConfig mycatConfig = config;\n        boolean isUseZkSwitch = mycatConfig.getSystem().isUseZKSwitch();\n        String loadZk = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_FLAG);\n        return (isUseZkSwitch && \"true\".equalsIgnoreCase(loadZk));\n    }\n\n    public RouteService getRouterService() {\n        return routerService;\n    }\n\n    public CacheService getCacheService() {\n        return cacheService;\n    }\n\n    public NameableExecutor getBusinessExecutor() {\n        return businessExecutor;\n    }\n\n    public RouteService getRouterservice() {\n        return routerService;\n    }\n\n    public NIOProcessor nextProcessor() {\n        int i = ++nextProcessor;\n        if (i >= processors.length) {\n            i = nextProcessor = 0;\n        }\n        return processors[i];\n    }\n\n    public NIOProcessor[] getProcessors() {\n        return processors;\n    }\n\n    public SocketConnector getConnector() {\n        return connector;\n    }\n\n    public SQLRecorder getSqlRecorder() {\n        return sqlRecorder;\n    }\n\n    public long getStartupTime() {\n        return startupTime;\n    }\n\n    public boolean isOnline() {\n        return isOnline.get();\n    }\n\n    public void offline() {\n        isOnline.set(false);\n    }\n\n    public void online() {\n        isOnline.set(true);\n    }\n\n    // 系统时间定时更新任务\n    private Runnable updateTime() {\n        return new Runnable() {\n            @Override\n            public void run() {\n                TimeUtil.update();\n            }\n        };\n    }\n\n    // 处理器定时检查任务\n    private Runnable processorCheck() {\n        return new Runnable() {\n            @Override\n            public void run() {\n                timerExecutor.execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        try {\n                            for (NIOProcessor p : processors) {\n                                p.checkBackendCons();\n                            }\n                        } catch (Exception e) {\n                            LOGGER.warn(\"checkBackendCons caught err:\" + e);\n                        }\n\n                    }\n                });\n                timerExecutor.execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        try {\n                            for (NIOProcessor p : processors) {\n                                p.checkFrontCons();\n                            }\n                        } catch (Exception e) {\n                            LOGGER.warn(\"checkFrontCons caught err:\" + e);\n                        }\n                    }\n                });\n            }\n        };\n    }\n\n    // 数据节点定时连接空闲超时检查任务\n    private Runnable dataNodeConHeartBeatCheck(final long heartPeriod) {\n        return new Runnable() {\n            @Override\n            public void run() {\n                timerExecutor.execute(new Runnable() {\n                    @Override\n                    public void run() {\n\n                        Map<String, PhysicalDBPool> nodes = config.getDataHosts();\n                        for (PhysicalDBPool node : nodes.values()) {\n                            node.heartbeatCheck(heartPeriod);\n                        }\n\t\t\t\t\t\t\n\t\t\t\t\t\t/*\n\t\t\t\t\t\tMap<String, PhysicalDBPool> _nodes = config.getBackupDataHosts();\n\t\t\t\t\t\tif (_nodes != null) {\n\t\t\t\t\t\t\tfor (PhysicalDBPool node : _nodes.values()) {\n\t\t\t\t\t\t\t\tnode.heartbeatCheck(heartPeriod);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}*/\n                    }\n                });\n            }\n        };\n    }\n\n    // 数据节点定时心跳任务\n    private Runnable dataNodeHeartbeat() {\n        return new Runnable() {\n            @Override\n            public void run() {\n                timerExecutor.execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        Map<String, PhysicalDBPool> nodes = config.getDataHosts();\n                        for (PhysicalDBPool node : nodes.values()) {\n                            node.doHeartbeat();\n                        }\n                    }\n                });\n            }\n        };\n    }\n\n    //by kaiz : 定时计算datanode active connection\n    private Runnable dataNodeCalcActiveCons() {\n        return new Runnable() {\n            @Override\n            public void run() {\n                timerExecutor.execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        Map<String, PhysicalDBPool> nodes = config.getDataHosts();\n                        for (PhysicalDBPool node : nodes.values()) {\n                            Collection<PhysicalDatasource> dataSources = node.getAllDataSources();\n                            for(PhysicalDatasource ds : dataSources) {\n                                ds.calcTotalCount();\n                            }\n                        }\n                    }\n                });\n            }\n        };\n    }\n\n    //定时清理保存SqlStat中的数据\n    private Runnable recycleSqlStat() {\n        return new Runnable() {\n            @Override\n            public void run() {\n                Map<String, UserStat> statMap = UserStatAnalyzer.getInstance().getUserStatMap();\n                for (UserStat userStat : statMap.values()) {\n                    userStat.getSqlLastStat().recycle();\n                    userStat.getSqlRecorder().recycle();\n                    userStat.getSqlHigh().recycle();\n                    userStat.getSqlLargeRowStat().recycle();\n                }\n            }\n        };\n    }\n\n    //定时清理xa任务 对超过阈值的xa任务回滚或者提交\n    private Runnable xaTaskCheck() {\n        return new Runnable() {\n            @Override\n            public void run() {\n                Collection<CoordinatorLogEntry> coordinatorLogEntries = MultiNodeCoordinator.inMemoryRepository.getAllCoordinatorLogEntries();\n                long sqlTimeout = MycatServer.getInstance().getConfig().getSystem().getSqlExecuteTimeout() * 1000L;\n\n                List<CoordinatorLogEntry> CoordinatorLogEntryList = null;\n                long currentTime = TimeUtil.currentTimeMillis();\n                for(CoordinatorLogEntry coordinatorLogEntry : coordinatorLogEntries) {\n                    //超过执行时间20秒 进行重试\n                    if(currentTime >  sqlTimeout + 20 * 1000 + coordinatorLogEntry.createTime){\n                        if(CoordinatorLogEntryList == null) {\n                            CoordinatorLogEntryList = new ArrayList<CoordinatorLogEntry>();\n                        }\n                        CoordinatorLogEntryList.add(coordinatorLogEntry);\n                    }\n                }\n                if(CoordinatorLogEntryList != null) {\n                    performXARecoveryLog((CoordinatorLogEntry[])CoordinatorLogEntryList.toArray());\n                }\n            }\n        };\n    }\n\n    //定时检查不同分片表结构一致性\n    private Runnable tableStructureCheck() {\n        return new MySQLTableStructureDetector();\n    }\n\n    //  全局表一致性检查任务\n    private Runnable glableTableConsistencyCheck() {\n        return new Runnable() {\n            @Override\n            public void run() {\n                timerExecutor.execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        GlobalTableUtil.consistencyCheck();\n                    }\n                });\n            }\n        };\n    }\n\n    private void putXARecoveryLogToMemory(CoordinatorLogEntry[] coordinatorLogEntries) {\n        //init into in memory cached\n        for (int i = 0; i < coordinatorLogEntries.length; i++) {\n            MultiNodeCoordinator.inMemoryRepository.put(coordinatorLogEntries[i].id, coordinatorLogEntries[i]);\n            //discard the recovery log\n            MultiNodeCoordinator.fileRepository.writeCheckpoint(coordinatorLogEntries[i].id, MultiNodeCoordinator.inMemoryRepository.getAllCoordinatorLogEntries());\n        }\n    }\n\n    //XA recovery log check\n    private void performXARecoveryLog(CoordinatorLogEntry[] coordinatorLogEntries) {\n        //fetch the recovery log\n        for (int i = 0; i < coordinatorLogEntries.length; i++) {\n            CoordinatorLogEntry coordinatorLogEntry = coordinatorLogEntries[i];\n            boolean needRollback = false;\n            boolean hasCommit = false;\n            //检查xa事务是否完成 ,处于部分commit 或者部分prepare中\n            for (int j = 0; j < coordinatorLogEntry.participants.length; j++) {\n                ParticipantLogEntry participantLogEntry = coordinatorLogEntry.participants[j];\n                if (participantLogEntry.txState == TxState.TX_PREPARED_STATE || participantLogEntry.txState == TxState.TX_STARTED_STATE) {\n                    needRollback = true;\n                }\n                if (participantLogEntry.txState == TxState.TX_COMMITED_STATE) {\n                \thasCommit = true;\n                }\n            }\n            //补充提交 prepare 状态的提交, xa commit or xa rollback\n            if (needRollback) {\n                //1 can rollback\n            \tif(!hasCommit) {\n                    for (int j = 0; j < coordinatorLogEntry.participants.length; j++) {\n                        ParticipantLogEntry participantLogEntry = coordinatorLogEntry.participants[j];\n                        if (participantLogEntry.txState == TxState.TX_COMMITED_STATE || participantLogEntry.txState == TxState.TX_ROLLBACKED_STATE) {\n                            continue;\n                        }                         //XA rollback\n                        String xacmd = \"XA ROLLBACK \" + coordinatorLogEntry.id  +\",'\"+ participantLogEntry.resourceName+\"'\" + ';';\n                        LOGGER.debug(\"send xaCmd : {}\", xacmd);\n                        OneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler(new String[0], new XARollbackCallback(coordinatorLogEntry.id,\n                                participantLogEntry\n                        ));\n                        //xa cmd send\n                        sendXaCmd(participantLogEntry, xacmd, resultHandler);\n                    }\n            \t}  else {\n                    LOGGER.debug( \"some has commit in {}\",coordinatorLogEntry);\n                    for (int j = 0; j < coordinatorLogEntry.participants.length; j++) {\n                        ParticipantLogEntry participantLogEntry = coordinatorLogEntry.participants[j];\n                        if (participantLogEntry.txState == TxState.TX_COMMITED_STATE || participantLogEntry.txState == TxState.TX_ROLLBACKED_STATE) {\n                            continue;\n                        }\n                        //XA commit\n                        String xacmd = \"XA COMMIT \" + coordinatorLogEntry.id  +\",'\"+ participantLogEntry.resourceName+\"'\" + ';';\n                        LOGGER.debug(\"send xaCmd : {}\", xacmd);\n                        OneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler(new String[0], new XACommitCallback(coordinatorLogEntry.id,\n                                participantLogEntry\n                        ));\n                        //xa cmd send\n                        sendXaCmd(participantLogEntry, xacmd, resultHandler);\n                    }\n            \t}      \t\n            }\n        }\n   }\n\n    private void sendXaCmd(ParticipantLogEntry participantLogEntry, String xacmd,\n                           OneRawSQLQueryResultHandler resultHandler) {\n        for (SchemaConfig schema : MycatServer.getInstance().getConfig().getSchemas().values()) {\n            for (TableConfig table : schema.getTables().values()) {\n                for (String dataNode : table.getDataNodes()) {\n                    PhysicalDBNode dn = MycatServer.getInstance().getConfig().getDataNodes().get(dataNode);\n                    if (dn.getDbPool().getSource().getConfig().getIp().equals(participantLogEntry.uri)\n                            && dn.getDatabase().equals(participantLogEntry.resourceName)) {\n                        //XA STATE ROLLBACK\n                        SQLJob sqlJob = new SQLJob(xacmd, dn.getDatabase(), resultHandler, dn.getDbPool().getSource());\n                        sqlJob.run();\n                        LOGGER.debug(String.format(\"[XA cmd] [%s] Host:[%s] schema:[%s]\", xacmd, dn.getName(), dn.getDatabase()));\n//                                        break outloop;\n                        return;\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * covert the collection to array\n     **/\n    private CoordinatorLogEntry[] getCoordinatorLogEntries() {\n        Collection<CoordinatorLogEntry> allCoordinatorLogEntries = fileRepository.getAllCoordinatorLogEntries();\n        if (allCoordinatorLogEntries == null) {\n            return new CoordinatorLogEntry[0];\n        }\n        if (allCoordinatorLogEntries.size() == 0) {\n            return new CoordinatorLogEntry[0];\n        }\n        return allCoordinatorLogEntries.toArray(new CoordinatorLogEntry[allCoordinatorLogEntries.size()]);\n    }\n\n    public NameableExecutor getSequenceExecutor() {\n        return sequenceExecutor;\n    }\n\n    //huangyiming add\n    public DirectByteBufferPool getDirectByteBufferPool() {\n        return (DirectByteBufferPool) bufferPool;\n    }\n\n    public boolean isAIO() {\n        return aio;\n    }\n\n\n    public ListeningExecutorService getListeningExecutorService() {\n        return listeningExecutorService;\n    }\n\n    public ScheduledExecutorService getHeartbeatScheduler() {\n        return heartbeatScheduler;\n    }\n\n    public MycatLeaderLatch getLeaderLatch() {\n        return leaderLatch;\n    }\n\n    public static void main(String[] args) throws Exception {\n        String path = ZKUtils.getZKBasePath() + \"bindata\";\n        CuratorFramework zk = ZKUtils.getConnection();\n        if (zk.checkExists().forPath(path) == null) ;\n\n        byte[] data = zk.getData().forPath(path);\n        System.out.println(data.length);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/MycatShutdown.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat;\n\nimport java.util.Date;\n\n/**\n * @author mycat\n */\npublic final class MycatShutdown {\n\n    public static void main(String[] args) {\n        System.out.println(new Date() + \",server shutdown!\");\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/MycatStartup.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat;\n\n\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.model.SystemConfig;\n\n/**\n * @author mycat\n */\npublic final class MycatStartup {\n    private static final String dateFormat = \"yyyy-MM-dd HH:mm:ss\";\n    private static final Logger LOGGER = LoggerFactory.getLogger(MycatStartup.class);\n    public static void main(String[] args) {\n        //use zk ?\n        ZkConfig.getInstance().initZk();\n        try {\n            String home = SystemConfig.getHomePath();\n            if (home == null) {\n                System.out.println(SystemConfig.SYS_HOME + \"  is not set.\");\n                System.exit(-1);\n            }\n            // init\n            MycatServer server = MycatServer.getInstance();\n            //这个方法执行的代码，上面SystemConfig.getHomePath()已经执行过了，建议注释掉。\n            //server.beforeStart();\n\n            // startup\n            server.startup();\n            System.out.println(\"MyCAT Server startup successfully. see logs in logs/mycat.log\");\n\n        } catch (Exception e) {\n            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);\n            LOGGER.error(sdf.format(new Date()) + \" startup error\", e);\n            System.exit(-1);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/BackendConnection.java",
    "content": "package io.mycat.backend;\n\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\n\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.net.ClosableConnection;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.ServerConnection;\n\npublic interface BackendConnection extends ClosableConnection {\n\tpublic boolean isModifiedSQLExecuted();\n\n\tpublic boolean isFromSlaveDB();\n\n\tpublic String getSchema();\n\n\tpublic void setSchema(String newSchema);\n\n\tpublic long getLastTime();\n\n\tpublic boolean isClosedOrQuit();\n\n\tpublic void setAttachment(Object attachment);\n\n\tpublic void quit();\n\n\tpublic void setLastTime(long currentTimeMillis);\n\n\tpublic void release();\n\n\tpublic boolean setResponseHandler(ResponseHandler commandHandler);\n\n\tpublic void commit();\n\n\tpublic void query(String sql) throws UnsupportedEncodingException;\n\n\tpublic Object getAttachment();\n\n    public void closeWithoutRsp(String reason);\n\n\t// public long getThreadId();\n\n\n\n\tpublic void execute(RouteResultsetNode node, ServerConnection source,\n\t\t\tboolean autocommit) throws IOException;\n\n\tpublic void recordSql(String host, String schema, String statement);\n\n\tpublic boolean syncAndExcute();\n\n\tpublic void rollback();\n\n\tpublic boolean isBorrowed();\n\n\tpublic void setBorrowed(boolean borrowed);\n\n\tpublic int getTxIsolation();\n\n\tpublic boolean isAutocommit();\n\n\tpublic boolean isTxReadonly();\n\tpublic int getSqlSelectLimit();\n\n\tpublic long getId();\n\n\tpublic void discardClose(String reason);\n\n\tpublic void query(String sql, int charsetIndex);\n\n\tpublic boolean checkAlive();\n\n    public void disableRead();\n\n    public void enableRead();\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/ConMap.java",
    "content": "package io.mycat.backend;\n\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.jdbc.JDBCConnection;\nimport io.mycat.backend.mysql.nio.MySQLConnection;\nimport io.mycat.net.NIOProcessor;\n\npublic class ConMap {\n\t\n\t// key -schema\n\tprivate final ConcurrentHashMap<String, ConQueue> items = new ConcurrentHashMap<String, ConQueue>();\n\n\tpublic ConQueue getSchemaConQueue(String schema) {\n\t\tConQueue queue = items.get(schema);\n\t\tif (queue == null) {\n\t\t\tConQueue newQueue = new ConQueue();\n\t\t\tqueue = items.putIfAbsent(schema, newQueue);\n\t\t\treturn (queue == null) ? newQueue : queue;\n\t\t}\n\t\treturn queue;\n\t}\n\n\tpublic BackendConnection tryTakeCon(final String schema, boolean autoCommit) {\n\t\tfinal ConQueue queue = items.get(schema);\n\t\tBackendConnection con = tryTakeCon(queue, autoCommit);\n\t\tif (con != null) {\n\t\t\treturn con;\n\t\t} else {\n\t\t\tfor (ConQueue queue2 : items.values()) {\n\t\t\t\tif (queue != queue2) {\n\t\t\t\t\tcon = tryTakeCon(queue2, autoCommit);\n\t\t\t\t\tif (con != null) {\n\t\t\t\t\t\treturn con;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\n\t}\n\n\tprivate BackendConnection tryTakeCon(ConQueue queue, boolean autoCommit) {\n\n\t\tBackendConnection con = null;\n\t\tif (queue != null && ((con = queue.takeIdleCon(autoCommit)) != null)) {\n\t\t\treturn con;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tpublic Collection<ConQueue> getAllConQueue() {\n\t\treturn items.values();\n\t}\n\n\tpublic int getActiveCountForSchema(String schema,\n\t\t\tPhysicalDatasource dataSouce) {\n\t\tint total = 0;\n\t\tfor (NIOProcessor processor : MycatServer.getInstance().getProcessors()) {\n\t\t\tfor (BackendConnection con : processor.getBackends().values()) {\n\t\t\t\tif (con instanceof MySQLConnection) {\n\t\t\t\t\tMySQLConnection mysqlCon = (MySQLConnection) con;\n\n\t\t\t\t\tif (mysqlCon.getSchema().equals(schema)\n\t\t\t\t\t\t\t&& mysqlCon.getPool() == dataSouce\n\t\t\t\t\t\t\t&& mysqlCon.isBorrowed()) {\n\t\t\t\t\t\t\ttotal++;\n\t\t\t\t\t}\n\n                }else if (con instanceof JDBCConnection) {\n                    JDBCConnection jdbcCon = (JDBCConnection) con;\n                    if (jdbcCon.getSchema().equals(schema) && jdbcCon.getPool() == dataSouce\n\t\t\t\t\t\t\t&& jdbcCon.isBorrowed()) {\n                            total++;\n                    }\n                }\n            }\n        }\n        return total;\n    }\n\n\tpublic int getActiveCountForDs(PhysicalDatasource dataSouce) {\n\t\tint total = 0;\n\t\tfor (NIOProcessor processor : MycatServer.getInstance().getProcessors()) {\n\t\t\tfor (BackendConnection con : processor.getBackends().values()) {\n\t\t\t\tif (con instanceof MySQLConnection) {\n\t\t\t\t\tMySQLConnection mysqlCon = (MySQLConnection) con;\n\n\t\t\t\t\tif (mysqlCon.getPool() == dataSouce\n\t\t\t\t\t\t\t&& mysqlCon.isBorrowed() && !mysqlCon.isClosed()) {\n\t\t\t\t\t\t\ttotal++;\n\t\t\t\t\t}\n\n                } else if (con instanceof JDBCConnection) {\n                    JDBCConnection jdbcCon = (JDBCConnection) con;\n                    if (jdbcCon.getPool() == dataSouce\n\t\t\t\t\t\t\t&& jdbcCon.isBorrowed() && !jdbcCon.isClosed()) {\n                            total++;\n                    }\n                }\n            }\n        }\n        return total;\n    }\n\n    public int getTotalCountForDs(PhysicalDatasource dataSouce) {\n        int total = 0;\n        for (NIOProcessor processor : MycatServer.getInstance().getProcessors()) {\n            for (BackendConnection con : processor.getBackends().values()) {\n                if (con instanceof MySQLConnection) {\n                    MySQLConnection mysqlCon = (MySQLConnection) con;\n                    if (mysqlCon.getPool() == dataSouce && !mysqlCon.isClosed()) {\n                        total++;\n                    }\n\n                } else if (con instanceof JDBCConnection) {\n                    JDBCConnection jdbcCon = (JDBCConnection) con;\n                    if (jdbcCon.getPool() == dataSouce && !jdbcCon.isClosed()) {\n                        total++;\n                    }\n                }\n            }\n        }\n        return total;\n    }\n\n    public void clearConnections(String reason, PhysicalDatasource dataSouce) {\n        for (NIOProcessor processor : MycatServer.getInstance().getProcessors()) {\n            ConcurrentMap<Long, BackendConnection> map = processor.getBackends();\n            Iterator<Entry<Long, BackendConnection>> itor = map.entrySet().iterator();\n            while (itor.hasNext()) {\n                Entry<Long, BackendConnection> entry = itor.next();\n                BackendConnection con = entry.getValue();\n                if (con instanceof MySQLConnection) {\n                    if (((MySQLConnection) con).getPool() == dataSouce) {\n                        con.close(reason);\n                        itor.remove();\n                    }\n                } else if((con instanceof JDBCConnection)\n\t\t\t\t\t\t&& (((JDBCConnection) con).getPool() == dataSouce)){\n                        con.close(reason);\n                        itor.remove();\n                }\n            }\n\n\t\t}\n\t\titems.clear();\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/ConQueue.java",
    "content": "package io.mycat.backend;\n\nimport java.util.ArrayList;\nimport java.util.concurrent.ConcurrentLinkedQueue;\n\npublic class ConQueue {\n\tprivate final ConcurrentLinkedQueue<BackendConnection> autoCommitCons = new ConcurrentLinkedQueue<BackendConnection>();\n\tprivate final ConcurrentLinkedQueue<BackendConnection> manCommitCons = new ConcurrentLinkedQueue<BackendConnection>();\n\tprivate long executeCount;\n\n\tpublic BackendConnection takeIdleCon(boolean autoCommit) {\n\t\tConcurrentLinkedQueue<BackendConnection> f1 = autoCommitCons;\n\t\tConcurrentLinkedQueue<BackendConnection> f2 = manCommitCons;\n\n\t\tif (!autoCommit) {\n\t\t\tf1 = manCommitCons;\n\t\t\tf2 = autoCommitCons;\n\n\t\t}\n\t\tBackendConnection con = f1.poll();\n\n\t\twhile (con  != null && !con.checkAlive()){\n\t\t\tcon.close(\"channel is closed\");\n\t\t\tcon = f1.poll();\n\t\t}\n\n\t\tif (con == null || con.isClosedOrQuit()) {\n\t\t\tcon = f2.poll();\n\t\t}\n\t\tif (con == null || con.isClosedOrQuit()) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\treturn con;\n\t\t}\n\n\t}\n\n\tpublic long getExecuteCount() {\n\t\treturn executeCount;\n\t}\n\n\tpublic void incExecuteCount() {\n\t\tthis.executeCount++;\n\t}\n\n\tpublic boolean removeCon(BackendConnection con) {\n\t\tboolean removed = autoCommitCons.remove(con);\n\t\tif (!removed) {\n\t\t\treturn manCommitCons.remove(con);\n\t\t}\n\t\treturn removed;\n\t}\n\n\tpublic boolean isSameCon(BackendConnection con) {\n\t\tif (autoCommitCons.contains(con)) {\n\t\t\treturn true;\n\t\t} else if (manCommitCons.contains(con)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic ConcurrentLinkedQueue<BackendConnection> getAutoCommitCons() {\n\t\treturn autoCommitCons;\n\t}\n\n\tpublic ConcurrentLinkedQueue<BackendConnection> getManCommitCons() {\n\t\treturn manCommitCons;\n\t}\n\n\tpublic ArrayList<BackendConnection> getIdleConsToClose(int count) {\n\t\tArrayList<BackendConnection> readyCloseCons = new ArrayList<BackendConnection>(\n\t\t\t\tcount);\n\t\twhile (!manCommitCons.isEmpty() && readyCloseCons.size() < count) {\n\t\t\tBackendConnection theCon = manCommitCons.poll();\n\t\t\tif (theCon != null&&!theCon.isBorrowed()) {\n\t\t\t\treadyCloseCons.add(theCon);\n\t\t\t}\n\t\t}\n\t\twhile (!autoCommitCons.isEmpty() && readyCloseCons.size() < count) {\n\t\t\tBackendConnection theCon = autoCommitCons.poll();\n\t\t\tif (theCon != null&&!theCon.isBorrowed()) {\n\t\t\t\treadyCloseCons.add(theCon);\n\t\t\t}\n\n\t\t}\n\t\treturn readyCloseCons;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/ConnectionMeta.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend;\n\n/**\n * connection metadata info\n * \n * @author wuzhih\n * \n */\npublic class ConnectionMeta {\n\tprivate final String schema;\n\tprivate final String charset;\n\tprivate final int txIsolation;\n\tprivate final boolean autocommit;\n\n\tpublic ConnectionMeta(String schema, String charset, int txIsolation,\n\t\t\tboolean autocommit) {\n\t\tsuper();\n\t\tthis.schema = schema;\n\t\tthis.charset = charset;\n\t\tthis.txIsolation = txIsolation;\n\t\tthis.autocommit = autocommit;\n\t}\n\n\tpublic String getSchema() {\n\t\treturn schema;\n\t}\n\n//\tpublic String getCharset() {\n//\t\treturn charset;\n//\t}\n//\n//\tpublic int getTxIsolation() {\n//\t\treturn txIsolation;\n//\t}\n//\n//\tpublic boolean isAutocommit() {\n//\t\treturn autocommit;\n//\t}\n\t\n\tpublic boolean isSameSchema(BackendConnection theCon)\n\t{\n\t\treturn theCon.getSchema().equals(schema);\n\t}\n\n\t/**\n\t * get metadata similarity\n\t * \n\t * @param theCon\n\t * @return\n\t */\n\tpublic int getMetaSimilarity(BackendConnection theCon) {\n\t\tint result = 0;\n\t\tif (schema == null || schema.equals(theCon.getSchema())) {\n\t\t\tresult++;\n\t\t}\n\t\tif (charset == null || charset.equals(theCon.getCharset())) {\n\t\t\tresult++;\n\t\t}\n\t\tif (txIsolation == -1 || txIsolation == theCon.getTxIsolation()) {\n\t\t\tresult++;\n\t\t}\n\t\tif (autocommit == theCon.isAutocommit()) {\n\t\t\tresult++;\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ConnectionMeta [schema=\" + schema + \", charset=\" + charset\n\t\t\t\t+ \", txIsolation=\" + txIsolation + \", autocommit=\" + autocommit\n\t\t\t\t+ \"]\";\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/datasource/PhysicalDBNode.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.datasource;\n\nimport io.mycat.MycatServer;\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.route.RouteResultsetNode;\n\npublic class PhysicalDBNode {\n\tprotected static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(PhysicalDBNode.class);\n\n\tprotected final String name;\n\tprotected final String database;\n\tprotected final PhysicalDBPool dbPool;\n\n\tpublic PhysicalDBNode(String hostName, String database,\n\t\t\tPhysicalDBPool dbPool) {\n\t\tthis.name = hostName;\n\t\tthis.database = database;\n\t\tthis.dbPool = dbPool;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic PhysicalDBPool getDbPool() {\n\t\treturn dbPool;\n\t}\n\n\tpublic String getDatabase() {\n\t\treturn database;\n\t}\n\n\t/**\n\t * get connection from the same datasource\n\t * \n\t * @param exitsCon\n\t * @throws Exception\n\t */\n\tpublic void getConnectionFromSameSource(String schema,boolean autocommit,\n\t\t\tBackendConnection exitsCon, ResponseHandler handler,\n\t\t\tObject attachment) throws Exception {\n\n\t\tPhysicalDatasource ds = this.dbPool.findDatasouce(exitsCon);\n\t\tif (ds == null) {\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\"can't find existing connection,maybe fininshed \" + exitsCon);\n\t\t} else {\n\t\t\tds.getConnection(schema,autocommit, handler, attachment);\n\t\t}\n\n\t}\n\n\tprivate void checkRequest(String schema){\n\t\tif (schema != null\n\t\t\t\t&& !schema.equals(this.database)) {\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\"invalid param ,connection request db is :\"\n\t\t\t\t\t\t\t+ schema + \" and datanode db is \"\n\t\t\t\t\t\t\t+ this.database);\n\t\t}\n\t\tif (!dbPool.isInitSuccess()) {\n\t\t\tdbPool.init(dbPool.activedIndex);\n\t\t}\n\t}\n\t\n\tpublic void getConnection(String schema,boolean autoCommit, RouteResultsetNode rrs,\n\t\t\t\t\t\t\tResponseHandler handler, Object attachment) throws Exception {\n\t\tcheckRequest(schema);\n\n\t\tboolean needMaster = !autoCommit && MycatServer.getInstance().getConfig().getSystem().isStrictTxIsolation();\n\t\tif (needMaster && rrs.getRunOnSlave()==null){\n\t\t\trrs.setRunOnSlave(false);//#2305\n\t\t}\n\t\tif (dbPool.isInitSuccess()) {\n\t\t\tLOGGER.debug(\"rrs.getRunOnSlave() \" + rrs.getRunOnSlaveDebugInfo());\n\t\t\tif(rrs.getRunOnSlave() != null){\t\t// 带有 /*db_type=master/slave*/ 注解\n\t\t\t\t// 强制走 slave\n\t\t\t\tif(rrs.getRunOnSlave()){\t\t\t\n\t\t\t\t\tLOGGER.debug(\"rrs.isHasBlanceFlag() \" + rrs.isHasBlanceFlag());\n\t\t\t\t\tif (rrs.isHasBlanceFlag()) {\t\t// 带有 /*balance*/ 注解(目前好像只支持一个注解...)\n\t\t\t\t\t\tdbPool.getReadBanlanceCon(schema,autoCommit,handler, attachment, this.database);\n\t\t\t\t\t}else{\t// 没有 /*balance*/ 注解\n\t\t\t\t\t\tLOGGER.debug(\"rrs.isHasBlanceFlag()\" + rrs.isHasBlanceFlag());\n\t\t\t\t\t\tif(!dbPool.getReadCon(schema, autoCommit, handler, attachment, this.database)){\n\t\t\t\t\t\t\tLOGGER.warn(\"Do not have slave connection to use, use master connection instead.\");\n\t\t\t\t\t\t\tPhysicalDatasource writeSource=dbPool.getSource();\n\t\t\t\t\t\t\t//记录写节点写负载值\n\t\t\t\t\t\t\twriteSource.setWriteCount();\n\t\t\t\t\t\t\twriteSource.getConnection(schema,\n\t\t\t\t\t\t\t\t\tautoCommit, handler, attachment);\n\t\t\t\t\t\t\trrs.setRunOnSlave(false);\n\t\t\t\t\t\t\trrs.setCanRunInReadDB(false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}else{\t// 强制走 master\n\t\t\t\t\t// 默认获得的是 writeSource，也就是 走master\n\t\t\t\t\tLOGGER.debug(\"rrs.getRunOnSlave() \" + rrs.getRunOnSlaveDebugInfo());\n\t\t\t\t\tPhysicalDatasource writeSource=dbPool.getSource();\n\t\t\t\t\t//记录写节点写负载值\n\t\t\t\t\twriteSource.setWriteCount();\n\t\t\t\t\twriteSource.getConnection(schema, autoCommit,\n\t\t\t\t\t\t\thandler, attachment);\n\t\t\t\t\trrs.setCanRunInReadDB(false);\n\t\t\t\t}\n\t\t\t}else{\t// 没有  /*db_type=master/slave*/ 注解，按照原来的处理方式\n\t\t\t\tLOGGER.debug(\"rrs.getRunOnSlave() \" + rrs.getRunOnSlaveDebugInfo());\t// null\n\t\t\t\tif (rrs.canRunnINReadDB(autoCommit)) {\n\t\t\t\t\tdbPool.getRWBanlanceCon(schema,autoCommit, handler, attachment, this.database);\n\t\t\t\t} else {\n\t\t\t\t\tPhysicalDatasource writeSource =dbPool.getSource();\n\t\t\t\t\t//记录写节点写负载值\n\t\t\t\t\twriteSource.setWriteCount();\n\t\t\t\t\twriteSource.getConnection(schema, autoCommit,\n\t\t\t\t\t\t\thandler, attachment);\n\t\t\t\t}\n\t\t\t}\n\t\t\n\t\t} else {\n\t\t\tthrow new IllegalArgumentException(\"Invalid DataSource:\" + dbPool.getActivedIndex());\n\t\t\t}\n\t\t}\n\n//\tpublic void getConnection(String schema,boolean autoCommit, RouteResultsetNode rrs,\n//\t\t\tResponseHandler handler, Object attachment) throws Exception {\n//\t\tcheckRequest(schema);\n//\t\tif (dbPool.isInitSuccess()) {\n//\t\t\tif (rrs.canRunnINReadDB(autoCommit)) {\n//\t\t\t\tdbPool.getRWBanlanceCon(schema,autoCommit, handler, attachment,\n//\t\t\t\t\t\tthis.database);\n//\t\t\t} else {\n//\t\t\t\tdbPool.getSource().getConnection(schema,autoCommit, handler, attachment);\n//\t\t\t}\n//\n//\t\t} else {\n//\t\t\tthrow new IllegalArgumentException(\"Invalid DataSource:\"\n//\t\t\t\t\t+ dbPool.getActivedIndex());\n//\t\t}\n//\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/datasource/PhysicalDBPool.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.datasource;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.heartbeat.DBHeartbeat;\nimport io.mycat.backend.heartbeat.zkprocess.SwitchStatueToZK;\nimport io.mycat.backend.loadbalance.LeastActiveLoadBalance;\nimport io.mycat.backend.loadbalance.LoadBalance;\nimport io.mycat.backend.loadbalance.RandomLoadBalance;\nimport io.mycat.backend.loadbalance.WeightedRoundRobinLoadBalance;\nimport io.mycat.backend.mysql.nio.handler.GetConnectionHandler;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.config.Alarms;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.util.LogUtil;\nimport io.mycat.util.ZKUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.locks.ReentrantLock;\n\npublic class PhysicalDBPool {\n\t\n\tprotected static final Logger LOGGER = LoggerFactory.getLogger(PhysicalDBPool.class);\n\t\n\tpublic static final int BALANCE_NONE = 0;\n\tpublic static final int BALANCE_ALL_BACK = 1;\n\tpublic static final int BALANCE_ALL = 2;\n    public static final int BALANCE_ALL_READ = 3;\n\n\tpublic static final int RANDOM = 0;\n\tpublic static final int WEIGHTED_ROUND_ROBIN = 1;\n\tpublic static final int LEAST_ACTIVE = 2;\n\n\tpublic static final int WRITE_ONLYONE_NODE = 0;\n\tpublic static final int WRITE_RANDOM_NODE = 1;\n\tpublic static final int WRITE_ALL_NODE = 2;\n\t\n\tpublic static final long LONG_TIME = 300000;\n\tpublic static final int WEIGHT = 0;\n\n\tprivate final String hostName;\n\t\n\tprotected PhysicalDatasource[] writeSources;\n\tprotected Map<Integer, PhysicalDatasource[]> readSources;\n\t\n\tprotected volatile int activedIndex;\n\tprotected volatile boolean initSuccess;\n\t\n\tprotected final ReentrantLock switchLock = new ReentrantLock();\n\tprivate final Collection<PhysicalDatasource> allDs;\n\tprivate final int banlance;\n\tprivate final int writeType;\n\tprivate final Random random = new Random();\n\tprivate final Random wnrandom = new Random();\n\tprivate String[] schemas;\n\tprivate final DataHostConfig dataHostConfig;\n\tprivate String slaveIDs;\n\tprivate LoadBalance loadBalance;\n\n\tpublic PhysicalDBPool(String name, DataHostConfig conf,\n\t\t\tPhysicalDatasource[] writeSources,\n\t\t\tMap<Integer, PhysicalDatasource[]> readSources, int balance,\n\t\t\tint writeType) {\n\t\t\n\t\tthis.hostName = name;\n\t\tthis.dataHostConfig = conf;\n\t\tthis.writeSources = writeSources;\n\t\tthis.banlance = balance;\n\t\tthis.writeType = writeType;\n\n\t\tswitch (dataHostConfig.getBalanceType()) {\n\t\t\tcase WEIGHTED_ROUND_ROBIN:\n\t\t\t\tloadBalance = new WeightedRoundRobinLoadBalance();\n\t\t\t\tbreak;\n\t\t\tcase LEAST_ACTIVE:\n\t\t\t\tloadBalance = new LeastActiveLoadBalance();\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tloadBalance = new RandomLoadBalance();\n\t\t\t\tbreak;\n\t\t}\n\t\t\n\t\tIterator<Map.Entry<Integer, PhysicalDatasource[]>> entryItor = readSources.entrySet().iterator();\n\t\twhile (entryItor.hasNext()) {\n\t\t\tPhysicalDatasource[] values = entryItor.next().getValue();\n\t\t\tif (values.length == 0) {\n\t\t\t\tentryItor.remove();\n\t\t\t}\n\t\t}\n\t\t\n\t\tthis.readSources = readSources;\n\t\tthis.allDs = this.genAllDataSources();\n\t\t\n\t\tLOGGER.info(\"total resources of dataHost \" + this.hostName + \" is :\" + allDs.size());\n\t\t\n\t\tsetDataSourceProps();\n\t}\n\n\tpublic int getWriteType() {\n\t\treturn writeType;\n\t}\n\n\tprivate void setDataSourceProps() {\n\t\tfor (PhysicalDatasource ds : this.allDs) {\n\t\t\tds.setDbPool(this);\n\t\t}\n\t}\n\n\tpublic PhysicalDatasource findDatasouce(BackendConnection exitsCon) {\n\t\tfor (PhysicalDatasource ds : this.allDs) {\n\t\t\tif ((ds.isReadNode() == exitsCon.isFromSlaveDB())\n\t\t\t\t\t&& ds.isMyConnection(exitsCon)) {\n\t\t\t\t\treturn ds;\n\t\t\t}\n\t\t}\n\t\t\n\t\tLOGGER.warn(\"can't find connection in pool \" + this.hostName + \" con:\"\t+ exitsCon);\n\t\treturn null;\n\t}\n\n\tpublic String getSlaveIDs() {\n\t\treturn slaveIDs;\n\t}\n\n\tpublic void setSlaveIDs(String slaveIDs) {\n\t\tthis.slaveIDs = slaveIDs;\n\t}\n\n\tpublic String getHostName() {\n\t\treturn hostName;\n\t}\n\n\t/**\n\t * all write datanodes\n\t * @return\n\t */\n\tpublic PhysicalDatasource[] getSources() {\n\t\treturn writeSources;\n\t}\n\t\n\tpublic PhysicalDatasource getSource() {\n\t\t\n\t\tswitch (writeType) {\n\t\t\tcase WRITE_ONLYONE_NODE: {\n\t\t\t\treturn writeSources[activedIndex];\n\t\t\t}\n\t\t\tcase WRITE_RANDOM_NODE: {\n\t\n\t\t\t\tint index = Math.abs(wnrandom.nextInt(Integer.MAX_VALUE)) % writeSources.length;\n\t\t\t\tPhysicalDatasource result = writeSources[index];\n\t\t\t\tif (!this.isAlive(result)) {\n\t\t\t\t\t\n\t\t\t\t\t// find all live nodes\n\t\t\t\t\tArrayList<Integer> alives = new ArrayList<Integer>(writeSources.length - 1);\n\t\t\t\t\tfor (int i = 0; i < writeSources.length; i++) {\n\t\t\t\t\t\tif (i != index\n\t\t\t\t\t\t\t\t&& this.isAlive(writeSources[i])) {\n\t\t\t\t\t\t\t\talives.add(i);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (alives.isEmpty()) {\n\t\t\t\t\t\tresult = writeSources[0];\n\t\t\t\t\t} else {\t\t\t\t\t\t\n\t\t\t\t\t\t// random select one\n\t\t\t\t\t\tindex = Math.abs(wnrandom.nextInt(Integer.MAX_VALUE)) % alives.size();\n\t\t\t\t\t\tresult = writeSources[alives.get(index)];\n\t\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\t\tLOGGER.debug(\"select write source \" + result.getName()\n\t\t\t\t\t\t\t+ \" for dataHost:\" + this.getHostName());\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tthrow new java.lang.IllegalArgumentException(\"writeType is \"\n\t\t\t\t\t\t+ writeType + \" ,so can't return one write datasource \");\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic int getActivedIndex() {\n\t\treturn activedIndex;\n\t}\n\n\tpublic boolean isInitSuccess() {\n\t\treturn initSuccess;\n\t}\n\n\tpublic int next(int i) {\n\t\tif (checkIndex(i)) {\n\t\t\treturn (++i == writeSources.length) ? 0 : i;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n\t//进行投票选择的节点.\n\tprivate boolean switchSourceVoted(int newIndex, boolean isAlarm, String reason) {\n\t\tif (notSwitchSource(newIndex)) {\n\t\t\treturn false;\n\t\t}\t\t\n\t\tfinal ReentrantLock lock = this.switchLock;\n\t\tif(MycatServer.getInstance().isUseZkSwitch()) {\n\t\t\tlock.lock();\n\t\t\ttry {\n\t\t\t\tfinal String myId = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID);\n\t\t\t\tString manageVotePath = ZKUtils.getZKBasePath() +\"heartbeat/\" + hostName +\"/\" + \"voteInformation/\" \n\t\t\t\t\t\t+ myId;\n\t\t\t\tString data = String.format(\"%s=%d\", myId,newIndex);\n\t\t\t\tZKUtils.createPath(manageVotePath, data);\n\t\t\t\tLogUtil.writeDataSourceLog(String.format(\"[%s 發生投票: %s]\", myId, this.getSources()[newIndex].getName()));\n\t\t\t} finally {\n\t\t\t\tlock.unlock();\n\t\t\t}\n\t\t}\t\t\n\t\treturn true;\n\t}\n\t\n\t\n\t\n\t//判断是进行zk投票还是直接切换读写\n\tpublic boolean switchSourceOrVoted(int newIndex, boolean isAlarm, String reason) {\t\t\n\t\tif(MycatServer.getInstance().isUseZkSwitch()) {\n\t\t\treturn switchSourceVoted( newIndex,  isAlarm,  reason); \n\t\t} else {\n\t\t\treturn switchSource( newIndex,  isAlarm,  reason);\n\t\t}\n\t} \n\tpublic boolean notSwitchSource(int newIndex){\n\t\treturn this.writeType != PhysicalDBPool.WRITE_ONLYONE_NODE || !checkIndex(newIndex) ;\n\t}\n\t\n\tpublic boolean switchSource(int newIndex, boolean isAlarm, String reason) {\n\t\tLOGGER.warn(\"switchSource: active=\" + activedIndex + \" new=\" + newIndex + \" alarm=\" + isAlarm + \" reason=\" + reason);\n\t\tif (notSwitchSource(newIndex)) {\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tfinal ReentrantLock lock = this.switchLock;\n\t\tlock.lock();\n\t\ttry {\n\t\t\tint current = activedIndex;\n\t\t\tif (current != newIndex) {\n\t\t\t\t\n\t\t\t\tif(MycatServer.getInstance().isUseZkSwitch()){\n\t\t\t\t\tLOGGER.info( ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID) + \n\t\t\t\t\t\t\t\"正在开始进行转换节点 \" + hostName+ \" = \" + newIndex  );\n\t\t\t\t\tSwitchStatueToZK.startSwitch(hostName);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// switch index\n\t\t\t\tactivedIndex = newIndex;\n\t\t\t\t\n\t\t\t\tinitSuccess = false;\n\t\t\t\t\n\t\t\t\t// init again\n\t\t\t\tthis.init(activedIndex, reason);\n\t\t\t\t\n\t\t\t\t// clear all connections\n\t\t\t\tthis.getSources()[current].clearCons(\"switch datasource\");\n\t\t\t\t\n\t\t\t\t// write log\n\t\t\t\tString msg = switchMessage(current, newIndex, false, reason);\n\t\t\t\tLOGGER.warn(msg);\n\t\t\t\tLogUtil.writeDataSourceLog(msg);\n\t\t\t\tif(MycatServer.getInstance().isUseZkSwitch()) {\n\t\t\t\t\tLOGGER.warn(switchMessage(current, newIndex, false, reason));\t\t\t\t\n\t\t\t\t\tcurrent =   activedIndex;\n \t\t\t\t\tif(!isInitSuccess() || current != newIndex) {\n\t\t\t\t\t\tLOGGER.error(String.format(\"%s switch to index %d error ! now index is to switch %d but %d\", hostName, newIndex ,newIndex, current));\n\n\t\t\t\t\t\t//报错 然后程序直接挂掉\n\t\t\t\t\t\tSystem.exit(-1);\n\t\t\t\t\t}\n\t\t\t\t\tSwitchStatueToZK.endSwitch(hostName);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\tif(MycatServer.getInstance().isUseZkSwitch()) {\n\t\t\t\t\tSwitchStatueToZK.startSwitch(hostName);\n\t\t\t\t\tSwitchStatueToZK.endSwitch(hostName);\n\t\t\t\t}\t\n\t\t\t}\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate String switchMessage(int current, int newIndex, boolean alarm, String reason) {\n\t\tStringBuilder s = new StringBuilder();\n\t\tif (alarm) {\n\t\t\ts.append(Alarms.DATANODE_SWITCH);\n\t\t}\n\t\ts.append(\"[Host=\").append(hostName).append(\",result=[\").append(this.getSources()[current].getName()).append(\"->\");\n\t\ts.append(this.getSources()[newIndex].getName()).append(\"],reason=\").append(reason).append(']');\n\t\treturn s.toString();\n\t}\n\n\tprivate int loop(int i) {\n\t\treturn i < writeSources.length ? i : (i - writeSources.length);\n\t}\n\n\tpublic void init(int index) {\n\t\tinit(index, \"\");\n\t}\n\n\tpublic void init(int index, String reason) {\n\t\t\n\t\tif (!checkIndex(index)) {\n\t\t\tindex = 0;\n\t\t}\n\t\t\n\t\tint active = -1;\n\t\tfor (int i = 0; i < writeSources.length; i++) {\n\t\t\tint j = loop(i + index);\n\t\t\tif ( initSource(j, writeSources[j]) ) {\n\n                //不切换-1时，如果主写挂了   不允许切换过去\n\t\t\t\tboolean isNotSwitchDs = ( dataHostConfig.getSwitchType() == DataHostConfig.NOT_SWITCH_DS )&& !\"MANAGER\".equals(reason);\n\t\t\t\tif ( isNotSwitchDs && j > 0 ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tactive = j;\n\t\t\t\tactivedIndex = active;\n\t\t\t\tinitSuccess = true;\n\t\t\t\tLOGGER.info(getMessage(active, \" init success\"));\n\n\t\t\t\tif (this.writeType == WRITE_ONLYONE_NODE) {\n\t\t\t\t\t// only init one write datasource\n\t\t\t\t\tMycatServer.getInstance().saveDataHostIndex(hostName, activedIndex);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!checkIndex(active)) {\n\t\t\tinitSuccess = false;\n\t\t\tStringBuilder s = new StringBuilder();\n\t\t\ts.append(Alarms.DEFAULT).append(hostName).append(\" init failure\");\n\t\t\tLOGGER.error(s.toString());\n\t\t}\n\t}\n\n\tprivate boolean checkIndex(int i) {\n\t\treturn i >= 0 && i < writeSources.length;\n\t}\n\n\tprivate String getMessage(int index, String info) {\n\t\treturn new StringBuilder().append(hostName).append(\" index:\").append(index).append(info).toString();\n\t}\n\n\tprivate boolean initSource(int index, PhysicalDatasource ds) {\n\t\tint initSize = ds.getConfig().getMinCon();\n\t\t\n\t\tLOGGER.info(\"init backend mysql source ,create connections total \" + initSize + \" for \" + ds.getName() + \" index :\" + index);\n\t\t\n\t\tCopyOnWriteArrayList<BackendConnection> list = new CopyOnWriteArrayList<BackendConnection>();\n\t\tGetConnectionHandler getConHandler = new GetConnectionHandler(list, initSize);\n\t\t// long start = System.currentTimeMillis();\n\t\t// long timeOut = start + 5000 * 1000L;\n\n\t\tfor (int i = 0; i < initSize; i++) {\n\t\t\ttry {\n\t\t\t\tds.getConnection(this.schemas[i % schemas.length], true, getConHandler, null);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.warn(getMessage(index, \" init connection error.\"), e);\n\t\t\t}\n\t\t}\n\t\tlong timeOut = System.currentTimeMillis() + 60 * 1000;\n\n\t\t// waiting for finish\n\t\twhile (!getConHandler.finished() && (System.currentTimeMillis() < timeOut)) {\n\t\t\ttry {\n\t\t\t\tThread.sleep(100);\n\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tLOGGER.error(\"initError\", e);\n\t\t\t}\n\t\t}\n\t\tLOGGER.info(\"init result :\" + getConHandler.getStatusInfo());\n//\t\tfor (BackendConnection c : list) {\n//\t\t\tc.release();\n//\t\t}\n\t\treturn !list.isEmpty();\n\t}\n\n\tpublic void doHeartbeat() {\n\n\n\t\tif (writeSources == null || writeSources.length == 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (PhysicalDatasource source : this.allDs) {\n\n\t\t\tif (source != null) {\n\t\t\t\tsource.doHeartbeat();\n\t\t\t} else {\n\t\t\t\tStringBuilder s = new StringBuilder();\n\t\t\t\ts.append(Alarms.DEFAULT).append(hostName).append(\" current dataSource is null!\");\n\t\t\t\tLOGGER.error(s.toString());\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * back physical connection heartbeat check\n\t */\n\tpublic void heartbeatCheck(long ildCheckPeriod) {\n\t\t\n\t\tfor (PhysicalDatasource ds : allDs) {\n\t\t\t// only readnode or all write node or writetype=WRITE_ONLYONE_NODE\n\t\t\t// and current write node will check\n\t\t\tif (ds != null\n\t\t\t\t\t&& (ds.getHeartbeat().getStatus() == DBHeartbeat.OK_STATUS)\n\t\t\t\t\t&& (ds.isReadNode()\n\t\t\t\t\t\t\t|| (this.writeType != WRITE_ONLYONE_NODE) \n\t\t\t\t\t\t\t|| (this.writeType == WRITE_ONLYONE_NODE \n\t\t\t\t\t\t\t&& ds == this.getSource()))) {\n\t\t\t\t\n\t\t\t\tds.heatBeatCheck(ds.getConfig().getIdleTimeout(), ildCheckPeriod);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void startHeartbeat() {\n\t\tfor (PhysicalDatasource source : this.allDs) {\n\t\t\tsource.startHeartbeat();\n\t\t}\n\t}\n\n\tpublic void stopHeartbeat() {\n\t\tfor (PhysicalDatasource source : this.allDs) {\n\t\t\tsource.stopHeartbeat();\n\t\t}\n\t}\n\n\t/**\n\t *  强制清除 dataSources\n\t * @param reason\n\t */\n\tpublic void clearDataSources(String reason) {\n\t\tLOGGER.info(\"clear datasource of pool \" + this.hostName);\n\t\tfor (PhysicalDatasource source : this.allDs) {\t\t\t\n\t\t\tLOGGER.info(\"clear datasource of pool  \" + this.hostName + \" ds:\" + source.getConfig());\n\t\t\tsource.clearCons(reason);\n\t\t\tsource.stopHeartbeat();\n\t\t}\n\t}\n\n\tpublic Collection<PhysicalDatasource> genAllDataSources() {\n\t\t\n\t\tLinkedList<PhysicalDatasource> allSources = new LinkedList<PhysicalDatasource>();\n\t\tfor (PhysicalDatasource ds : writeSources) {\n\t\t\tif (ds != null) {\n\t\t\t\tallSources.add(ds);\n\t\t\t}\n\t\t}\n\t\t\n\t\tfor (PhysicalDatasource[] dataSources : this.readSources.values()) {\n\t\t\tfor (PhysicalDatasource ds : dataSources) {\n\t\t\t\tif (ds != null) {\n\t\t\t\t\tallSources.add(ds);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn allSources;\n\t}\n\n\tpublic Collection<PhysicalDatasource> getAllDataSources() {\n\t\treturn this.allDs;\n\t}\n\n\t/**\n\t * return connection for read balance\n\t *\n\t * @param handler\n\t * @param attachment\n\t * @param database\n\t * @throws Exception\n\t */\n\tpublic void getRWBanlanceCon(String schema, boolean autocommit,\n\t\t\tResponseHandler handler, Object attachment, String database) throws Exception {\n\t\t\n\t\tPhysicalDatasource theNode = null;\n\t\tArrayList<PhysicalDatasource> okSources = null;\n\t\tswitch (banlance) {\n\t\tcase BALANCE_ALL_BACK: {\t\t\t\n\t\t\t// all read nodes and the standard by masters\n\t\t\tokSources = getAllActiveRWSources(true, false, checkSlaveSynStatus());\n\t\t\tif (okSources.isEmpty()) {\n\t\t\t\ttheNode = this.getSource();\n\t\t\t\t\n\t\t\t} else {\n\t\t\t\ttheNode = randomSelect(okSources);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase BALANCE_ALL: {\n\t\t\tokSources = getAllActiveRWSources(true, true, checkSlaveSynStatus());\n\t\t\ttheNode = randomSelect(okSources);\n\t\t\tbreak;\n\t\t}\n        case BALANCE_ALL_READ: {\n            okSources = getAllActiveRWSources(false, false, checkSlaveSynStatus());\n            theNode = randomSelect(okSources);\n            break;\n        }\n\t\tcase BALANCE_NONE:\n\t\tdefault:\n\t\t\t// return default write data source\n\t\t\ttheNode = this.getSource();\n\t\t}\n\t\t\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"select read source \" + theNode.getName() + \" for dataHost:\" + this.getHostName());\n\t\t}\n\t\t//统计节点读操作次数\n\t\ttheNode.setReadCount();\n\t\ttheNode.getConnection(schema, autocommit, handler, attachment);\n\t}\n\n\t/**\n\t * slave 读负载均衡，也就是 readSource 之间实现负载均衡\n\t * @param schema\n\t * @param autocommit\n\t * @param handler\n\t * @param attachment\n\t * @param database\n\t * @throws Exception\n\t */\n    public void getReadBanlanceCon(String schema, boolean autocommit, ResponseHandler handler, \n\t\t\t\t\t\t\t\t\t\t\tObject attachment, String database)throws Exception {\n\t\tPhysicalDatasource theNode = null;\n\t\tArrayList<PhysicalDatasource> okSources = null;\n\t\tokSources = getAllActiveRWSources(false, false, checkSlaveSynStatus());\n\t\ttheNode = randomSelect(okSources);\n\t\t//统计节点读操作次数\n\t\ttheNode.setReadCount();\n\t\ttheNode.getConnection(schema, autocommit, handler, attachment);\n\t}\n    \n    /**\n     * 从 writeHost 下面的 readHost中随机获取一个 connection, 用于slave注解\n     * @param schema\n     * @param autocommit\n     * @param handler\n     * @param attachment\n     * @param database\n     * @return\n     * @throws Exception\n     */\n    public boolean getReadCon(String schema, boolean autocommit, ResponseHandler handler, \n\t\t\t\t\t\t\t\t\tObject attachment, String database)throws Exception {\n\t\tPhysicalDatasource theNode = null;\n\t\t\n\t\tLOGGER.debug(\"!readSources.isEmpty() \" + !readSources.isEmpty());\n\t\tif (!readSources.isEmpty()) {\n\t\t\tint index = Math.abs(random.nextInt(Integer.MAX_VALUE)) % readSources.size();\n\t\t\tPhysicalDatasource[] allSlaves = this.readSources.get(index);\n//\t\t\tSystem.out.println(\"allSlaves.length \" + allSlaves.length);\n\t\t\tif (allSlaves != null) {\n\t\t\t\tindex = Math.abs(random.nextInt(Integer.MAX_VALUE)) % readSources.size();\n\t\t\t\tPhysicalDatasource slave = allSlaves[index];\n\t\t\t\t\n\t\t\t\tfor (int i=0; i<allSlaves.length; i++) {\n\t\t\t\t\tLOGGER.debug(\"allSlaves.length i:::::: \" + i);\n\t\t\t\t\tif (isAlive(slave)) {\n\t\t\t\t\t\tif (checkSlaveSynStatus()) {\n\t\t\t\t\t\t\tif (canSelectAsReadNode(slave)) {\n\t\t\t\t\t\t\t\ttheNode = slave;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttheNode = slave;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n//\t\t\t\t\tindex = Math.abs(random.nextInt()) % readSources.size();\n\t\t\t\t}\n\t\t\t}\n\t\t\t//统计节点读操作次数\n\t\t\tif(theNode != null) {\n\t\t\t\ttheNode.setReadCount();\n\t\t\t\ttheNode.getConnection(schema, autocommit, handler, attachment);\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\tLOGGER.warn(\"readhost is notavailable.\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}else{\n\t\t\tLOGGER.warn(\"readhost is empty, readSources is empty.\");\n\t\t\treturn false;\n\t\t}\n\t} \n    \n\tprivate boolean checkSlaveSynStatus() {\n\t\treturn ( dataHostConfig.getSlaveThreshold() != -1 )\n\t\t\t\t&& (dataHostConfig.getSwitchType() == DataHostConfig.SYN_STATUS_SWITCH_DS);\n\t}\n\n\t\n\t/**\n\t * TODO: modify by zhuam\n\t * \n\t * 随机选择，按权重设置随机概率。\n     * 在一个截面上碰撞的概率高，但调用量越大分布越均匀，而且按概率使用权重后也比较均匀，有利于动态调整提供者权重。\n\t * @param okSources\n\t * @return\n\t */\n\tpublic PhysicalDatasource randomSelect(ArrayList<PhysicalDatasource> okSources) {\n\t\t\n\t\tif (okSources.isEmpty()) {\n\t\t\treturn this.getSource();\n\t\t\t\n\t\t} else {\n\t\t\treturn loadBalance.doSelect(hostName, okSources);\n//\t\t\tint length = okSources.size(); \t// 总个数\n//\t        int totalWeight = 0; \t\t\t// 总权重\n//\t        boolean sameWeight = true; \t\t// 权重是否都一样\n//\t        for (int i = 0; i < length; i++) {\n//\t            int weight = okSources.get(i).getConfig().getWeight();\n//\t            totalWeight += weight; \t\t// 累计总权重\n//\t            if (sameWeight && i > 0\n//\t            \t\t&& weight != okSources.get(i-1).getConfig().getWeight() ) {\t  // 计算所有权重是否一样\n//\t                sameWeight = false;\n//\t            }\n//\t        }\n//\n//\t        if (totalWeight > 0 && !sameWeight ) {\n//\n//\t        \t// 如果权重不相同且权重大于0则按总权重数随机\n//\t            int offset = random.nextInt(totalWeight);\n//\n//\t            // 并确定随机值落在哪个片断上\n//\t            for (int i = 0; i < length; i++) {\n//\t                offset -= okSources.get(i).getConfig().getWeight();\n//\t                if (offset < 0) {\n//\t                    return okSources.get(i);\n//\t                }\n//\t            }\n//\t        }\n//\n//\t        // 如果权重相同或权重为0则均等随机\n//\t        return okSources.get( random.nextInt(length) );\n//\n//\t\t\t//int index = Math.abs(random.nextInt()) % okSources.size();\n//\t\t\t//return okSources.get(index);\n\t\t}\n\t}\n\t\n\t//\n    public int getBalance() {\n        return banlance;\n    }\n    \n\tprivate boolean isAlive(PhysicalDatasource theSource) {\n\t\treturn (theSource.getHeartbeat().getStatus() == DBHeartbeat.OK_STATUS);\n\t}\n\n\tprivate boolean canSelectAsReadNode(PhysicalDatasource theSource) {\n\t\t\n\t\tInteger slaveBehindMaster = theSource.getHeartbeat().getSlaveBehindMaster();\n\t\tint dbSynStatus = theSource.getHeartbeat().getDbSynStatus();\n\t\t\n\t\tif ( slaveBehindMaster == null || dbSynStatus == DBHeartbeat.DB_SYN_ERROR) {\n\t\t\treturn false;\n\t\t}\t\t\n\t\tboolean isSync = dbSynStatus == DBHeartbeat.DB_SYN_NORMAL;\n\t\tboolean isNotDelay = slaveBehindMaster < this.dataHostConfig.getSlaveThreshold();\t\n\t\treturn isSync && isNotDelay;\n\t}\n\n\t/**\n     * return all backup write sources\n     * \n     * @param includeWriteNode if include write nodes\n     * @param includeCurWriteNode if include current active write node. invalid when <code>includeWriteNode<code> is false\n     * @param filterWithSlaveThreshold\n     *\n     * @return\n     */\n\tprivate ArrayList<PhysicalDatasource> getAllActiveRWSources(\n    \t\tboolean includeWriteNode, boolean includeCurWriteNode, boolean filterWithSlaveThreshold) {\n\t\t\n\t\tint curActive = activedIndex;\n\t\tArrayList<PhysicalDatasource> okSources = new ArrayList<PhysicalDatasource>(this.allDs.size());\n\t\t\n\t\tfor (int i = 0; i < this.writeSources.length; i++) {\n\t\t\tPhysicalDatasource theSource = writeSources[i];\n\t\t\tif (isAlive(theSource)) {// write node is active\n                \n\t\t\t\tif (includeWriteNode) {\t\t\t\t\t\n\t\t\t\t\tboolean isCurWriteNode = ( i == curActive );\n\t\t\t\t\tif ( isCurWriteNode && includeCurWriteNode == false) {\n\t\t\t\t\t\t// not include cur active source\n\t\t\t\t\t} else if (filterWithSlaveThreshold && theSource.isSalveOrRead() ) {\t\n\t\t\t\t\t\tboolean selected = canSelectAsReadNode(theSource);\n\t\t\t\t\t\tif ( selected ) {\n\t\t\t\t\t\t\tokSources.add(theSource);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\t\t\t\t\t\t\t\n\t\t\t\t\t} else {\n\t\t\t\t\t\tokSources.add(theSource);\n\t\t\t\t\t}\n                }\n                \n\t\t\t\tif (!readSources.isEmpty()) {\t\t\t\t\t\n\t\t\t\t\t// check all slave nodes\n\t\t\t\t\tPhysicalDatasource[] allSlaves = this.readSources.get(i);\n\t\t\t\t\tif (allSlaves != null) {\n\t\t\t\t\t\tfor (PhysicalDatasource slave : allSlaves) {\n\t\t\t\t\t\t\tif (isAlive(slave)) {\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif (filterWithSlaveThreshold) {\n\t\t\t\t\t\t\t\t\tboolean selected = canSelectAsReadNode(slave);\n\t\t\t\t\t\t\t\t\tif ( selected ) {\n\t\t\t\t\t\t\t\t\t\tokSources.add(slave);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tokSources.add(slave);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} else {\n\t\t\t\t\n\t\t\t\t// TODO : add by zhuam\t\n\t\t\t    // 如果写节点不OK, 也要保证临时的读服务正常\n\t\t\t\tif ( this.dataHostConfig.isTempReadHostAvailable()\n\t\t\t\t\t\t&& !readSources.isEmpty()) {\n\t\t\t\t\n\t\t\t\t\t\t// check all slave nodes\n\t\t\t\t\t\tPhysicalDatasource[] allSlaves = this.readSources.get(i);\n\t\t\t\t\t\tif (allSlaves != null) {\n\t\t\t\t\t\t\tfor (PhysicalDatasource slave : allSlaves) {\n\t\t\t\t\t\t\t\tif (isAlive(slave)) {\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tif (filterWithSlaveThreshold) {\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\tif (canSelectAsReadNode(slave)) {\n\t\t\t\t\t\t\t\t\t\t\tokSources.add(slave);\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tokSources.add(slave);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\treturn okSources;\n\t}\n\n    public String[] getSchemas() {\n\t\treturn schemas;\n\t}\n\n\tpublic void setSchemas(String[] mySchemas) {\n\t\tthis.schemas = mySchemas;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/datasource/PhysicalDatasource.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.datasource;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.concurrent.atomic.LongAdder;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.ConMap;\nimport io.mycat.backend.ConQueue;\nimport io.mycat.backend.heartbeat.DBHeartbeat;\nimport io.mycat.backend.mysql.nio.MySQLConnection;\nimport io.mycat.backend.mysql.nio.handler.ConnectionHeartBeatHandler;\nimport io.mycat.backend.mysql.nio.handler.DelegateResponseHandler;\nimport io.mycat.backend.mysql.nio.handler.NewConnectionRespHandler;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.config.Alarms;\nimport io.mycat.config.model.DBHostConfig;\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.util.TimeUtil;\n\n\npublic abstract class PhysicalDatasource {\n\t\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(PhysicalDatasource.class);\n\n\tprivate final String name;\n\tprivate final int size;\n\tprivate final DBHostConfig config;\n\tprivate final ConMap conMap = new ConMap();\n\tprivate DBHeartbeat heartbeat;\n\tprivate final boolean readNode;\n\tprivate volatile long heartbeatRecoveryTime;\n\tprivate final DataHostConfig hostConfig;\n\tprivate final ConnectionHeartBeatHandler conHeartBeatHanler = new ConnectionHeartBeatHandler();\n\tprivate PhysicalDBPool dbPool;\n\tprivate volatile long totalConnectionCount = 0L;\n\t//判断是否需要同步 increamentCount\n\tprivate LongAdder increamentCount = new LongAdder();\n\tprivate long preIncrementCount = 0;\n\n\t\n\t// 添加DataSource读计数\n\tprivate AtomicLong readCount = new AtomicLong(0);\n\t\n\t// 添加DataSource写计数\n\tprivate AtomicLong writeCount = new AtomicLong(0);\n\t\n\t\n\t/** \n\t *   edit by dingw at 2017.06.08\n\t *   @see https://github.com/MyCATApache/Mycat-Server/issues/1524\n\t *   \n\t */\n\t// 当前活动连接\n\t//private volatile AtomicInteger activeCount = new AtomicInteger(0);\n\t\n\t// 当前存活的总连接数,为什么不直接使用activeCount,主要是因为连接的创建是异步完成的\n\t//private volatile AtomicInteger totalConnection = new AtomicInteger(0);\n\t\n\t/**\n\t * 由于在Mycat中，returnCon被多次调用（与takeCon并没有成对调用）导致activeCount、totalConnection容易出现负数\n\t */\n\t//private static final String TAKE_CONNECTION_FLAG = \"1\";\n\t//private ConcurrentMap<Long /* ConnectionId */, String /* 常量1*/> takeConnectionContext = new ConcurrentHashMap<>();\n\n\t\n\n\tpublic PhysicalDatasource(DBHostConfig config, DataHostConfig hostConfig,\n\t\t\tboolean isReadNode) {\n\t\tthis.size = config.getMaxCon();\n\t\tthis.config = config;\n\t\tthis.name = config.getHostName();\n\t\tthis.hostConfig = hostConfig;\n\t\theartbeat = this.createHeartBeat();\n\t\tthis.readNode = isReadNode;\n\t}\n\n\tpublic boolean isMyConnection(BackendConnection con) {\n\t\tif (con instanceof MySQLConnection) {\n\t\t\treturn ((MySQLConnection) con).getPool() == this;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n\tpublic long getReadCount() {\n\t\treturn readCount.get();\n\t}\n\n\tpublic void setReadCount() {\n\t\treadCount.addAndGet(1);\n\t}\n\n\tpublic long getWriteCount() {\n\t\treturn writeCount.get();\n\t}\n\n\tpublic void setWriteCount() {\n\t\twriteCount.addAndGet(1);\n\t}\n\n\tpublic DataHostConfig getHostConfig() {\n\t\treturn hostConfig;\n\t}\n\n\tpublic boolean isReadNode() {\n\t\treturn readNode;\n\t}\n\n\tpublic int getSize() {\n\t\treturn size;\n\t}\n\n\tpublic void setDbPool(PhysicalDBPool dbPool) {\n\t\tthis.dbPool = dbPool;\n\t}\n\n\tpublic PhysicalDBPool getDbPool() {\n\t\treturn dbPool;\n\t}\n\n\tpublic abstract DBHeartbeat createHeartBeat();\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic long getExecuteCount() {\n\t\tlong executeCount = 0;\n\t\tfor (ConQueue queue : conMap.getAllConQueue()) {\n\t\t\texecuteCount += queue.getExecuteCount();\n\n\t\t}\n\t\treturn executeCount;\n\t}\n\n\tpublic long getExecuteCountForSchema(String schema) {\n\t\treturn conMap.getSchemaConQueue(schema).getExecuteCount();\n\n\t}\n\n\tpublic int getActiveCountForSchema(String schema) {\n\t\treturn conMap.getActiveCountForSchema(schema, this);\n\t}\n\n\tpublic int getIdleCountForSchema(String schema) {\n\t\tConQueue queue = conMap.getSchemaConQueue(schema);\n\t\tint total = 0;\n\t\ttotal += queue.getAutoCommitCons().size()\n\t\t\t\t+ queue.getManCommitCons().size();\n\t\treturn total;\n\t}\n\n\tpublic DBHeartbeat getHeartbeat() {\n\t\treturn heartbeat;\n\t}\n\n\tpublic int getIdleCount() {\n\t\tint total = 0;\n\t\tfor (ConQueue queue : conMap.getAllConQueue()) {\n\t\t\ttotal += queue.getAutoCommitCons().size()\n\t\t\t\t\t+ queue.getManCommitCons().size();\n\t\t}\n\t\treturn total;\n\t}\n\t\n\t/**\n\t * 该方法也不是非常精确，因为该操作也不是一个原子操作,相对getIdleCount高效与准确一些\n\t * @return\n\t */\n//\tpublic int getIdleCountSafe() {\n//\t\treturn getTotalConnectionsSafe() - getActiveCountSafe();\n//\t}\n\t\n\t/**\n\t * 是否需要继续关闭空闲连接\n\t * @return\n\t */\n//\tprivate boolean needCloseIdleConnection() {\n//\t\treturn getIdleCountSafe() > hostConfig.getMinCon();\n//\t}\n\n\tprivate boolean validSchema(String schema) {\n\t\tString theSchema = schema;\n\t\treturn theSchema != null && !\"\".equals(theSchema)\n\t\t\t\t&& !\"snyn...\".equals(theSchema);\n\t}\n\n\tprivate void checkIfNeedHeartBeat(\n\t\t\tLinkedList<BackendConnection> heartBeatCons, ConQueue queue,\n\t\t\tConcurrentLinkedQueue<BackendConnection> checkLis,\n\t\t\tlong hearBeatTime, long hearBeatTime2) {\n\t\tint maxConsInOneCheck = 10;\n\t\tIterator<BackendConnection> checkListItor = checkLis.iterator();\n\t\twhile (checkListItor.hasNext()) {\n\t\t\tBackendConnection con = checkListItor.next();\n\t\t\tif (con.isClosedOrQuit()) {\n\t\t\t\tcheckListItor.remove();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (validSchema(con.getSchema())) {\n\t\t\t\tif (con.getLastTime() < hearBeatTime\n\t\t\t\t\t\t&& heartBeatCons.size() < maxConsInOneCheck) {\n\t\t\t\t\tif(checkLis.remove(con)) { \n\t\t\t\t\t\t//如果移除成功，则放入到心跳连接中，如果移除失败，说明该连接已经被其他线程使用，忽略本次心跳检测\n\t\t\t\t\t\tcon.setBorrowed(true);\n\t\t\t\t\t\theartBeatCons.add(con);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (con.getLastTime() < hearBeatTime2) {\n\t\t\t\t// not valid schema conntion should close for idle\n\t\t\t\t// exceed 2*conHeartBeatPeriod\n\t\t\t\t// 同样，这里也需要先移除，避免被业务连接\n\t\t\t\tif(checkLis.remove(con)) { \n\t\t\t\t\tcon.close(\" heart beate idle \");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tpublic int getIndex() {\n\t\tint currentIndex = 0;\n\t\tfor (int i = 0; i < dbPool.getSources().length; i++) {\n\t\t\tPhysicalDatasource writeHostDatasource = dbPool.getSources()[i];\n\t\t\tif (writeHostDatasource.getName().equals(getName())) {\n\t\t\t\tcurrentIndex = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn currentIndex;\n\t}\n\n\tpublic boolean isSalveOrRead() {\n\t\tint currentIndex = getIndex();\n\t\tif (currentIndex != dbPool.activedIndex || this.readNode) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic void heatBeatCheck(long timeout, long conHeartBeatPeriod) {\n//\t\tint ildeCloseCount = hostConfig.getMinCon() * 3;\n\t\tint maxConsInOneCheck = 5;\n\t\tLinkedList<BackendConnection> heartBeatCons = new LinkedList<BackendConnection>();\n\n\t\tlong hearBeatTime = TimeUtil.currentTimeMillis() - conHeartBeatPeriod;\n\t\tlong hearBeatTime2 = TimeUtil.currentTimeMillis() - 2\n\t\t\t\t* conHeartBeatPeriod;\n\t\tfor (ConQueue queue : conMap.getAllConQueue()) {\n\t\t\tcheckIfNeedHeartBeat(heartBeatCons, queue,\n\t\t\t\t\tqueue.getAutoCommitCons(), hearBeatTime, hearBeatTime2);\n\t\t\tif (heartBeatCons.size() < maxConsInOneCheck) {\n\t\t\t\tcheckIfNeedHeartBeat(heartBeatCons, queue,\n\t\t\t\t\t\tqueue.getManCommitCons(), hearBeatTime, hearBeatTime2);\n\t\t\t} else if (heartBeatCons.size() >= maxConsInOneCheck) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!heartBeatCons.isEmpty()) {\n\t\t\tfor (BackendConnection con : heartBeatCons) {\n\t\t\t\tconHeartBeatHanler\n\t\t\t\t\t\t.doHeartBeat(con, hostConfig.getHearbeatSQL());\n\t\t\t}\n\t\t}\n\n\t\t// check if there has timeouted heatbeat cons\n\t\tconHeartBeatHanler.abandTimeOuttedConns();\n\t\tint idleCons = getIdleCount();\n\t\tint activeCons = this.getActiveCount();\n\t\tint createCount = (hostConfig.getMinCon() - idleCons) / 3;\n\t\t// create if idle too little\n\t\tif ((createCount > 0) && (idleCons + activeCons < size)\n\t\t\t\t&& (idleCons < hostConfig.getMinCon())) {\n\t\t\tcreateByIdleLitte(idleCons, createCount);\n\t\t} else if (idleCons > hostConfig.getMinCon()) {\n\t\t\tcloseByIdleMany(idleCons - hostConfig.getMinCon());\n\t\t} else {\n\t\t\tint activeCount = this.getActiveCount();\n\t\t\tif (activeCount > size) {\n\t\t\t\tStringBuilder s = new StringBuilder();\n\t\t\t\ts.append(Alarms.DEFAULT).append(\"DATASOURCE EXCEED [name=\")\n\t\t\t\t\t\t.append(name).append(\",active=\");\n\t\t\t\ts.append(activeCount).append(\",size=\").append(size).append(']');\n\t\t\t\tLOGGER.warn(s.toString());\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * \n\t * @param ildeCloseCount\n\t * 首先，从已创建的连接中选择本次心跳需要关闭的空闲连接数（由当前连接连接数-减去配置的最小连接数。\n\t * 然后依次关闭这些连接。由于连接空闲心跳检测与业务是同时并发的，在心跳关闭阶段，可能有连接被使用，导致需要关闭的空闲连接数减少.\n\t * \n\t * 所以每次关闭新连接时，先判断当前空闲连接数是否大于配置的最少空闲连接，如果为否，则结束本次关闭空闲连接操作。\n\t * 该方法修改之前：\n\t *      首先从ConnMap中获取 ildeCloseCount 个连接，然后关闭；在关闭中，可能又有连接被使用，导致可能多关闭一些链接，\n\t *      导致相对频繁的创建新连接和关闭连接\n\t *      \n\t * 该方法修改之后：\n\t *     ildeCloseCount 为预期要关闭的连接\n\t *     使用循环操作，首先在关闭之前，先再一次判断是否需要关闭连接，然后每次从ConnMap中获取一个空闲连接，然后进行关闭\n\t * edit by dingw at 2017.06.16\n\t */\n\tprivate void closeByIdleMany(int ildeCloseCount) {\n\t\tLOGGER.info(\"too many ilde cons ,close some for datasouce  \" + name);\n\t\tList<BackendConnection> readyCloseCons = new ArrayList<BackendConnection>(\n\t\t\t\tildeCloseCount);\n\t\tfor (ConQueue queue : conMap.getAllConQueue()) {\n\t\t\treadyCloseCons.addAll(queue.getIdleConsToClose(ildeCloseCount));\n\t\t\tif (readyCloseCons.size() >= ildeCloseCount) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfor (BackendConnection idleCon : readyCloseCons) {\n\t\t\tif (idleCon.isBorrowed()) {\n\t\t\t\tLOGGER.warn(\"find idle con is using \" + idleCon);\n\t\t\t}\n\t\t\tidleCon.close(\"too many idle con\");\n\t\t}\n\t\t\n//\t\tLOGGER.info(\"too many ilde cons ,close some for datasouce  \" + name);\n//\t\t\n//\t\tIterator<ConQueue> conQueueIt = conMap.getAllConQueue().iterator();\n//\t\tConQueue queue = null;\n//\t\tif(conQueueIt.hasNext()) {\n//\t\t\tqueue = conQueueIt.next();\n//\t\t}\n//\t\t\n//\t\tfor(int i = 0; i < ildeCloseCount; i ++ ) {\n//\t\t\t\n//\t\t\tif(!needCloseIdleConnection() || queue == null) {\n//\t\t\t\tbreak; //如果当时空闲连接数没有超过最小配置连接数，则结束本次连接关闭\n//\t\t\t}\n//\t\t\t\n//\t\t\tLOGGER.info(\"cur conns:\" + getTotalConnectionsSafe() );\n//\t\t\t\n//\t\t\tBackendConnection idleCon = queue.takeIdleCon(false);\n//\t\t\t\n//\t\t\twhile(idleCon == null && conQueueIt.hasNext()) {\n//\t\t\t\tqueue = conQueueIt.next();\n//\t\t\t\tidleCon = queue.takeIdleCon(false);\n//\t\t\t}\n//\t\t\t\n//\t\t\tif(idleCon == null) { \n//\t\t\t\tbreak;\n//\t\t\t}\n//\t\t\t\n//\t\t\tif (idleCon.isBorrowed() ) {\n//\t\t\t\tLOGGER.warn(\"find idle con is using \" + idleCon);\n//\t\t\t}\n//\t\t\tidleCon.close(\"too many idle con\");\n//\t\t\t\n//\t\t}\n\t\t\n\t}\n\n\tprivate void createByIdleLitte(int idleCons, int createCount) {\n\t\tLOGGER.info(\"create connections ,because idle connection not enough ,cur is \"\n\t\t\t\t+ idleCons\n\t\t\t\t+ \", minCon is \"\n\t\t\t\t+ hostConfig.getMinCon()\n\t\t\t\t+ \" for \"\n\t\t\t\t+ name);\n\t\tNewConnectionRespHandler simpleHandler = new NewConnectionRespHandler();\n\n\t\tfinal String[] schemas = dbPool.getSchemas();\n\t\tfor (int i = 0; i < createCount; i++) {\n\t\t\tif (this.getActiveCount() + this.getIdleCount() >= size) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// creat new connection\n\t\t\t\tthis.createNewConnection(simpleHandler, null, schemas[i\n\t\t\t\t\t\t% schemas.length]);\n\t\t\t} catch (IOException e) {\n\t\t\t\tLOGGER.warn(\"create connection err \" + e);\n\t\t\t}\n\n\t\t}\n\t}\n\n\tpublic int getActiveCount() {\n\t\treturn this.conMap.getActiveCountForDs(this);\n\t}\n\n\tpublic long getTotalCount() {\n\t\treturn totalConnectionCount + increamentCount.intValue();\n\t}\n\n\tpublic void calcTotalCount() {\n\t\t//当连接数增量开始变化的时候，先直接用increamentCount记录连接数，当一秒钟内不再有新增了之后，开始同步 totalConnectionCount\n\t\tif (preIncrementCount == increamentCount.longValue()) {\n\t\t\tlong total = this.conMap.getTotalCountForDs(this);\n\t\t\tlong inc = increamentCount.sumThenReset() - preIncrementCount;\n\t\t\ttotalConnectionCount = total + inc;\n\t\t\tpreIncrementCount = 0;\n\n\t\t} else {\n\t\t\tpreIncrementCount = increamentCount.longValue();\n\t\t}\n\t}\n\t\n\t\n\n\tpublic void clearCons(String reason) {\n\t\tthis.conMap.clearConnections(reason, this);\n\t}\n\n\tpublic void startHeartbeat() {\n\t\theartbeat.start();\n\t}\n\n\tpublic void stopHeartbeat() {\n\t\theartbeat.stop();\n\t}\n\n\tpublic void doHeartbeat() {\t\t\n\t\t// 未到预定恢复时间，不执行心跳检测。\n\t\tif (TimeUtil.currentTimeMillis() < heartbeatRecoveryTime) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (!heartbeat.isStop()) {\n\t\t\ttry {\n\t\t\t\theartbeat.heartbeat();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(name + \" heartbeat error.\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate BackendConnection takeCon(BackendConnection conn,\n\t\t\tfinal ResponseHandler handler, final Object attachment,\n\t\t\tString schema) {\n\n\t\tconn.setBorrowed(true);\n\n//\t\tif(takeConnectionContext.putIfAbsent(conn.getId(), TAKE_CONNECTION_FLAG) == null) {\n//\t\t\tincrementActiveCountSafe();\n//\t\t}\n\t\t\n\t\t\n\t\tif (!conn.getSchema().equals(schema)) {\n\t\t\t// need do schema syn in before sql send\n\t\t\tconn.setSchema(schema);\n\t\t}\n\t\tConQueue queue = conMap.getSchemaConQueue(schema);\n\t\tqueue.incExecuteCount();\n\t\tconn.setAttachment(attachment);\n\t\tconn.setLastTime(System.currentTimeMillis()); // 每次取连接的时候，更新下lasttime，防止在前端连接检查的时候，关闭连接，导致sql执行失败\n\t\thandler.connectionAcquired(conn);\n\t\treturn conn;\n\t}\n\n\tprivate void createNewConnection(final ResponseHandler handler,\n\t\t\tfinal Object attachment, final String schema) throws IOException {\t\t\n\t\t// aysn create connection\n\t\tfinal AtomicBoolean hasError = new AtomicBoolean(false);\n\n\t\tMycatServer.getInstance().getBusinessExecutor().execute(new Runnable() {\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\tcreateNewConnection(new DelegateResponseHandler(handler) {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\t\t\t\t\t\t\tif(hasError.compareAndSet(false, true)) {\n\t\t\t\t\t\t\t\thandler.connectionError(e, conn);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tLOGGER.info(\"connection connectionError \");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void connectionAcquired(BackendConnection conn) {\n\t\t\t\t\t\t\tLOGGER.info(\"connection id is \"+conn.getId());\n\t\t\t\t\t\t\ttakeCon(conn, handler, attachment, schema);\n\t\t\t\t\t\t}\n\t\t\t\t\t}, schema);\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tif(hasError.compareAndSet(false, true)) {\n\t\t\t\t\t\thandler.connectionError(e, null);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tLOGGER.info(\"connection connectionError \");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void getConnection(String schema, boolean autocommit,\n\t\t\tfinal ResponseHandler handler, final Object attachment)\n\t\t\tthrows IOException {\n\t\t\n\t\t// 从当前连接map中拿取已建立好的后端连接\n\t\tBackendConnection con = this.conMap.tryTakeCon(schema, autocommit);\n\t\tif (con != null) {\n\t\t\t//如果不为空，则绑定对应前端请求的handler\n\t\t\ttakeCon(con, handler, attachment, schema);\n\t\t\treturn;\t\n\t\t\t\n\t\t} else { // this.getActiveCount并不是线程安全的（严格上说该方法获取数量不准确），\n//\t\t\tint curTotalConnection = this.totalConnection.get();\n//\t\t\twhile(curTotalConnection + 1 <= size) {\n//\t\t\t\t\n//\t\t\t\tif (this.totalConnection.compareAndSet(curTotalConnection, curTotalConnection + 1)) {\n//\t\t\t\t\tLOGGER.info(\"no ilde connection in pool,create new connection for \"\t+ this.name + \" of schema \" + schema);\n//\t\t\t\t\tcreateNewConnection(handler, attachment, schema);\n//\t\t\t\t\treturn;\n//\t\t\t\t}\n//\t\t\t\t\n//\t\t\t\tcurTotalConnection = this.totalConnection.get(); //CAS更新失败，则重新判断当前连接是否超过最大连接数\n//\t\t\t\t\n//\t\t\t}\n//\t\t\t\n//\t\t\t// 如果后端连接不足，立即失败,故直接抛出连接数超过最大连接异常\n//\t\t\tLOGGER.error(\"the max activeConnnections size can not be max than maxconnections:\" + curTotalConnection);\n//\t\t\tthrow new IOException(\"the max activeConnnections size can not be max than maxconnections:\" + curTotalConnection);\n\n\n\t\t\t// 当前最大连接\n\t\t\tlong activeCons = increamentCount.longValue()+totalConnectionCount;\n\t\t\tif (activeCons < size) {// 下一个连接大于最大连接数\n\t\t\t\t//提前increamentCount的操作\n\t\t\t\tincreamentCount.increment();\n\t\t\t\tLOGGER.info(\"no ilde connection in pool \"+System.identityHashCode(this)+\" ,create new connection for \"\t+ this.name + \" of schema \" + schema + \" totalConnectionCount: \" + totalConnectionCount + \" increamentCount: \"+increamentCount);\n\t\t\t\tcreateNewConnection(handler, attachment, schema);\n\t\t\t} else { // create connection\n\t\t\t\tLOGGER.error(\"the max activeConnnections size can not be max than maxconnections\");\n\t\t\t\tthrow new IOException(\"the max activeConnnections size can not be max than maxconnections\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * 是否超过最大连接数\n\t * @return\n\t */\n//\tprivate boolean exceedMaxConnections() {\n//\t\treturn this.totalConnection.get() + 1 > size;\n//\t}\n//\t\n//\tpublic int decrementActiveCountSafe() {\n//\t\treturn this.activeCount.decrementAndGet();\n//\t}\n//\t\n//\tpublic int incrementActiveCountSafe() {\n//\t\treturn this.activeCount.incrementAndGet();\n//\t}\n//\t\n//\tpublic int getActiveCountSafe() {\n//\t\treturn this.activeCount.get();\n//\t}\n//\t\n//\tpublic int getTotalConnectionsSafe() {\n//\t\treturn this.totalConnection.get();\n//\t}\n//\t\n//\tpublic int decrementTotalConnectionsSafe() {\n//\t\treturn this.totalConnection.decrementAndGet();\n//\t}\n//\t\n//\tpublic int incrementTotalConnectionSafe() {\n//\t\treturn this.totalConnection.incrementAndGet();\n//\t}\n\n\tprivate void returnCon(BackendConnection c) {\n\t\t\n\t\tc.setAttachment(null);\n\t\tc.setBorrowed(false);\n\t\tc.setLastTime(TimeUtil.currentTimeMillis());\n\t\tConQueue queue = this.conMap.getSchemaConQueue(c.getSchema());\n\n\t\tboolean ok = false;\n\t\tif (c.isAutocommit()) {\n\t\t\tok = queue.getAutoCommitCons().offer(c);\n\t\t} else {\n\t\t\tok = queue.getManCommitCons().offer(c);\n\t\t}\n\t\t\n//\t\tif(c.getId() > 0 && takeConnectionContext.remove(c.getId(), TAKE_CONNECTION_FLAG) ) {\n//\t\t\tdecrementActiveCountSafe();\n//\t\t}\n\t\t\n\t\tif(!ok) {\n\t\t\tLOGGER.warn(\"can't return to pool ,so close con \" + c);\n\t\t\tc.close(\"can't return to pool \");\n\t\t\t\n\t\t}\n\t\t\n\t}\n\n\tpublic void releaseChannel(BackendConnection c) {\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"release channel \" + c);\n\t\t}\n\t\t// release connection\n\t\treturnCon(c);\n\t}\n\n\tpublic void connectionClosed(BackendConnection conn) {\n\t\tConQueue queue = this.conMap.getSchemaConQueue(conn.getSchema());\n\t\tif (queue != null ) {\n\t\t\tqueue.removeCon(conn);\n\t\t}\n\t\t\n//\t\tdecrementTotalConnectionsSafe(); \n\t}\n\n\t/**\n\t * 创建新连接\n\t */\n\tpublic abstract void createNewConnection(ResponseHandler handler, String schema) throws IOException;\n\t\n\t/**\n\t * 测试连接，用于初始化及热更新配置检测\n\t */\n\tpublic abstract boolean testConnection(String schema) throws IOException;\n\n\tpublic long getHeartbeatRecoveryTime() {\n\t\treturn heartbeatRecoveryTime;\n\t}\n\n\tpublic void setHeartbeatRecoveryTime(long heartbeatRecoveryTime) {\n\t\tthis.heartbeatRecoveryTime = heartbeatRecoveryTime;\n\t}\n\n\tpublic DBHostConfig getConfig() {\n\t\treturn config;\n\t}\n\n\tpublic boolean isAlive() {\n\t\treturn getHeartbeat().getStatus() == DBHeartbeat.OK_STATUS;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/heartbeat/ConsistenCollectHandler.java",
    "content": "package io.mycat.backend.heartbeat;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.mysql.nio.MySQLDataSource;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.manager.response.CheckGlobalTable;\nimport io.mycat.server.interceptor.impl.GlobalTableUtil;\nimport io.mycat.sqlengine.SQLQueryResult;\n\npublic class ConsistenCollectHandler {\n    public static final Logger LOGGER = LoggerFactory.getLogger(ConsistenCollectHandler.class);\n\n\tprivate AtomicBoolean isStop =new AtomicBoolean(false); //任务是否停止\n\t\n\tprivate final int retryTime ; //校验次数\n\tprivate final int dnCount ; //节点数量\n\n\tprivate final  long intervalTime ; //间隔时间\n\tprivate final String tableName; //表名\n\tprivate final String schemaName; //dataBase\n\tprivate final AtomicInteger successTime; //成功次数\n\tprivate final ManagerConnection con; //session\n\tpublic ConsistenCollectHandler(ManagerConnection c, String tableName, \n\t\t\tString schemaName, int dnCount, int retryTime, long intervalTime) {\n\t\tthis.tableName = tableName;\n\t\tthis.schemaName = schemaName;\n\t\tthis.retryTime = retryTime;\n\t\tthis.intervalTime = intervalTime;\n\t\tsuccessTime = new AtomicInteger(0);\n\t\tthis.con = c;\n\t\tthis.dnCount = dnCount;\n\t}\n\tprivate volatile ScheduledFuture<?> task; //定时发送校验的任务\n\t//定时器不断的检测\n\tpublic void startDetector(){\t\t\n\t\t task = MycatServer.getInstance().getScheduler()\n\t\t\t\t.scheduleAtFixedRate(new ConsisterThread(tableName, schemaName, this),\n\t\t\t\t\t\t0, intervalTime, TimeUnit.MILLISECONDS);\n\t}\t\n\t\n\tprivate ReentrantLock lock = new ReentrantLock();\n\tMap<String, List<SQLQueryResult<Map<String, String>>>> resultMap = new HashMap<>();\n\tpublic void onSuccess(SQLQueryResult<Map<String, String>> result) {\n\t\tif(isStop.get() == true){\n\t\t\treturn ;\n\t\t}\n\t\tlock.lock();\n\t\ttry{\n\t\t\tif(!resultMap.containsKey(result.getDataNode())) {\n\t\t\t\tresultMap.put(result.getDataNode(),new ArrayList<SQLQueryResult<Map<String, String>>>());\n\t\t\t}\n\t\t\tresultMap.get(result.getDataNode()).add(result);\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t\tint count = successTime.incrementAndGet();\n\t\tLOGGER.info(count + \" :{}\", JSON.toJSONString(result));\n\t\tif (count == retryTime * dnCount) {\n\t\t\tcancelTask();\n\t\t\tif(LOGGER.isDebugEnabled()){\n\t\t\t\tString str = \"\";\n\t\t\t\tfor(List<SQLQueryResult<Map<String, String>>> list : resultMap.values()) {\n\t\t\t\t\tstr +=  JSONObject.toJSONString(list) + \"\\n\";\n\t\t\t\t}\n\t\t\t\tLOGGER.debug(str);\n\t\t\t}\n\t\t\t///数据的校验 查询最大修改日期和修改时间有交集的节点。\n\t\t\tList<SQLQueryResult<Map<String, String>>> unionResult = resultMap.remove(result.getDataNode());\n\t\t\tList<SQLQueryResult<Map<String, String>>> tempResult = null;\n\t\t\tfor(List<SQLQueryResult<Map<String, String>>> list : resultMap.values()) {\n\t\t\t\ttempResult = new ArrayList<>();\n\t\t\t\tfor(SQLQueryResult<Map<String, String>> r1 : list) {\n\t\t\t\t\tMap<String, String> md1 = r1.getResult();\n\t\t\t\t\tString md1_max_column = md1.get(GlobalTableUtil.MAX_COLUMN);\n\t\t\t\t\tfor(int i = 0 ; i < unionResult.size(); i++) {\n\t\t\t\t\t\tMap<String, String> md2 = unionResult.get(i).getResult();\n\t\t\t\t\t\tString md2_max_column = md2.get(GlobalTableUtil.MAX_COLUMN);\n\t\t\t\t\t\tif(md1.get(GlobalTableUtil.COUNT_COLUMN).equals(md2.get(GlobalTableUtil.COUNT_COLUMN)) &&\n\t\t\t\t\t\t\t\t(md1_max_column==null && null == md2_max_column ) \n\t\t\t\t\t\t\t\t|| ( md1_max_column != null && md1_max_column.equals(md2_max_column))  ) {\n\t\t\t\t\t\t\ttempResult.add(r1);\n\t\t\t\t\t\t\tunionResult.remove(unionResult.get(i));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\t\t\t\t\n\t\t\t\tunionResult = tempResult;\n\t\t\t\tif(unionResult.size() == 0){\n\t\t\t\t\tbreak;\n\t\t\t\t}\t\t\t\t\n\t\t\t}\t\t\t\n\t\t\tif(unionResult.size() == 0) {\n\t\t\t\tLOGGER.debug(\"check table \" +tableName +\" not consistence , get info\" );\n\t\t\t\tCheckGlobalTable.response(con, tableName, \"No\");\n\t\t\t} else {\n\t\t\t\tLOGGER.debug(\"check table \" +tableName +\"  consistence , consistence info\" + JSONObject.toJSONString(unionResult));\t\t\t\t\n\t\t\t\tCheckGlobalTable.response(con, tableName, \"Yes\");\n\n\t\t\t}\t\t\t\n\t\t}\t\t\n\t}\n\t//定时器任务的取消。\n\tpublic void cancelTask() {\n\t\tfinal ScheduledFuture<?> t = task;\n\t\tif(t != null){\n\t\t\tt.cancel(false);\n\t\t\ttask = null;\n\t\t\t\n\t\t}\n\t}\t\n\n\tpublic void onError(String msg) {\n\t\tthis.cancelTask();\n\t\t///将错误消息写回mysql端\n\t\tif(isStop.compareAndSet(false, true)) {\n\t\t\tcon.writeErrMessage(ErrorCode.ERR_HANDLE_DATA, msg);\n\t\t}\n\t}\n\n\tpublic int getRetryTime() {\n\t\treturn retryTime;\n\t}\t\n\n}\n class ConsisterThread implements Runnable{\n\tprivate final String tableName; \n\tprivate final String schemaName;\n\tprivate final  ConsistenCollectHandler handler;\n\tprivate final AtomicInteger sendTime; //\n\tpublic ConsisterThread(String tableName, String schemaName,ConsistenCollectHandler handler) {\n\t\tthis.tableName = tableName;\n\t\tthis.schemaName = schemaName;\n\t\tthis.handler = handler;\n\t\tsendTime = new AtomicInteger(0);\n\t}\n\t \n\t \n\tpublic void run() {\n\t\tint count = sendTime.incrementAndGet();\n\t\t//所有的校验最大的时间的任务发送完成\n\t\tif (count > handler.getRetryTime()) {\n\t\t\thandler.cancelTask();\n\t\t\tConsistenCollectHandler.LOGGER.info(\" table check consistence job send finish\");\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\n\t\t\tMycatConfig config = MycatServer.getInstance().getConfig();\n\t\t\tTableConfig table = config.getSchemas().get(schemaName).getTables().get(tableName.toUpperCase());\n\n\t\t\tList<String> dataNodeList = table.getDataNodes();\n\n\t\t\t// 记录本次已经执行的datanode\n\t\t\t// 多个 datanode 对应到同一个 PhysicalDatasource 只执行一次\n\t\t\tfor (String nodeName : dataNodeList) {\n\t\t\t\tMap<String, PhysicalDBNode> map = config.getDataNodes();\n\t\t\t\tfor (String k2 : map.keySet()) {\n\t\t\t\t\t// <dataNode name=\"dn1\" dataHost=\"localhost1\" database=\"db1\"\n\t\t\t\t\t// />\n\t\t\t\t\tPhysicalDBNode dBnode = map.get(k2);\n\t\t\t\t\tif (nodeName.equals(dBnode.getName())) { // dn1,dn2,dn3\n\t\t\t\t\t\tPhysicalDBPool pool = dBnode.getDbPool(); // dataHost\n\t\t\t\t\t\tCollection<PhysicalDatasource> allDS = pool.genAllDataSources();\n\t\t\t\t\t\tfor (PhysicalDatasource pds : allDS) {\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\tif (pds instanceof MySQLDataSource) {\n\t\t\t\t\t\t\t\tMySQLDataSource mds = (MySQLDataSource) pds;\n\t\t\t\t\t\t\t\t\tMySQLConsistencyChecker checker = new MySQLConsistencyCheckerHandler(dBnode, mds,\n\t\t\t\t\t\t\t\t\t\t\ttable.getName(), handler);\n\t\t\t\t\t\t\t\t\tchecker.checkMaxTimeStamp();\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tConsistenCollectHandler.LOGGER.error(\"check consisten err: {}\", e);\n\t\t\thandler.onError(\"ConsisterThread err:\" + e.getMessage());\n\t\t}\n\t}\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/heartbeat/DBHeartbeat.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.heartbeat;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport io.mycat.statistic.DataSourceSyncRecorder;\nimport io.mycat.statistic.HeartbeatRecorder;\nimport io.mycat.util.LogUtil;\n\npublic abstract class DBHeartbeat {\n\tpublic static final int DB_SYN_ERROR = -1;\n\tpublic static final int DB_SYN_NORMAL = 1;\n\n\tpublic static final int OK_STATUS = 1;\n\tpublic static final int ERROR_STATUS = -1;\n\tpublic static final int TIMEOUT_STATUS = -2;\n\tpublic static final int INIT_STATUS = 0;\n\tprivate static final long DEFAULT_HEARTBEAT_TIMEOUT = 30 * 1000L;\n\tprivate static final int DEFAULT_HEARTBEAT_RETRY = 10;\n\t// heartbeat config\n\tprotected long heartbeatTimeout = DEFAULT_HEARTBEAT_TIMEOUT; // 心跳超时时间\n\tprotected int heartbeatRetry = DEFAULT_HEARTBEAT_RETRY; // 检查连接发生异常到切换，重试次数\n\tprotected String heartbeatSQL;// 静态心跳语句\n\tprotected final AtomicBoolean isStop = new AtomicBoolean(true);\n\tprotected final AtomicBoolean isChecking = new AtomicBoolean(false);\n\tprotected AtomicInteger errorCount = new AtomicInteger(0);\n\tprotected volatile int status;\n\tprotected final HeartbeatRecorder recorder = new HeartbeatRecorder();\n\tprotected final DataSourceSyncRecorder asynRecorder = new DataSourceSyncRecorder();\n\n\tprivate volatile Integer slaveBehindMaster;\n\tprivate volatile int dbSynStatus = DB_SYN_NORMAL;\n\n\tpublic Integer getSlaveBehindMaster() {\n\t\treturn slaveBehindMaster;\n\t}\n\n\tpublic int getDbSynStatus() {\n\t\treturn dbSynStatus;\n\t}\n\n\tpublic void setDbSynStatus(int dbSynStatus) {\n\t\tthis.dbSynStatus = dbSynStatus;\n\t}\n\n\tpublic void setSlaveBehindMaster(Integer slaveBehindMaster) {\n\t\tthis.slaveBehindMaster = slaveBehindMaster;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn status;\n\t}\n\n\tpublic boolean isChecking() {\n\t\treturn isChecking.get();\n\t}\n\n\tpublic abstract void start();\n\n\tpublic abstract void stop();\n\n\tpublic boolean isStop() {\n\t\treturn isStop.get();\n\t}\n\n\tpublic int getErrorCount() {\n\t\treturn errorCount.get();\n\t}\n\n\tpublic HeartbeatRecorder getRecorder() {\n\t\treturn recorder;\n\t}\n\n\tpublic abstract String getLastActiveTime();\n\n\tpublic abstract long getTimeout();\n\n\tpublic abstract void heartbeat();\n\n\tpublic long getHeartbeatTimeout() {\n\t\treturn heartbeatTimeout;\n\t}\n\n\tpublic void setHeartbeatTimeout(long heartbeatTimeout) {\n\t\tthis.heartbeatTimeout = heartbeatTimeout;\n\t}\n\n\tpublic int getHeartbeatRetry() {\n\t\treturn heartbeatRetry;\n\t}\n\n\tpublic void setHeartbeatRetry(int heartbeatRetry) {\n\t\tthis.heartbeatRetry = heartbeatRetry;\n\t}\n\n\tpublic String getHeartbeatSQL() {\n\t\treturn heartbeatSQL;\n\t}\n\n\tpublic void setHeartbeatSQL(String heartbeatSQL) {\n\t\tthis.heartbeatSQL = heartbeatSQL;\n\t}\n\n\tpublic boolean isNeedHeartbeat() {\n\t\treturn heartbeatSQL != null;\n\t}\n\n\tpublic DataSourceSyncRecorder getAsynRecorder() {\n\t\treturn this.asynRecorder;\n\t}\n\t/*\n\t * \n\t * @desc 將心跳的狀態寫入到日誌中\n\t * */\n\tprotected void writeStatusMsg(String dataHost, String dataSourceName,int nextstatus) {\n\t\tif(status != nextstatus) {\n\t\t\tStringBuilder msg = new StringBuilder(\"\");\n\t\t\tmsg.append(\"[dataHost=\").append(dataHost).append(\", dataSource=\").append(dataSourceName)\n\t\t\t.append(\",statue=\").append(getMsg(status)).append(\" -> \").append(getMsg(nextstatus)).append(\"]\");\n\t\t\tLogUtil.writeDataSourceLog(msg.toString());\n\t\t}\n\t}\n\t/*\n\t * \n\t * @return 獲取對應狀態的字符串狀態\n\t * */\n\tprotected String getMsg(int status) {\n\t\tswitch (status) {\n\t\tcase DBHeartbeat.INIT_STATUS:\n\t\t\treturn \"init status\";\n\t\tcase DBHeartbeat.TIMEOUT_STATUS:\n\t\t\treturn \"timeout status\";\n\t\tcase DBHeartbeat.OK_STATUS:\n\t\t\treturn \"ok status\";\n\t\tcase DBHeartbeat.ERROR_STATUS:\n\t\t\treturn \"error status\";\t\n\t\tdefault:\n\t\t\treturn \"unknown status\";\t\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/heartbeat/MySQLConsistencyChecker.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.heartbeat;\n\nimport io.mycat.backend.mysql.nio.MySQLDataSource;\nimport io.mycat.server.interceptor.impl.GlobalTableUtil;\nimport io.mycat.sqlengine.OneRawSQLQueryResultHandler;\nimport io.mycat.sqlengine.SQLJob;\nimport io.mycat.sqlengine.SQLQueryResult;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.fastjson.JSON;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * @author digdeep@126.com\n */\npublic class MySQLConsistencyChecker{\n\tpublic static final Logger LOGGER = LoggerFactory.getLogger(MySQLConsistencyChecker.class);\n\tprotected final MySQLDataSource source;\n\tprotected final ReentrantLock lock;\n\tprotected AtomicInteger jobCount = new AtomicInteger();\n\tprotected String countSQL;\n\tprotected String maxSQL;\n\tprotected String tableName;\t// global table name\n\tprotected long beginTime;\n//\tprotected String columnExistSQL = \"select count(*) as \"+GlobalTableUtil.INNER_COLUMN\n//\t\t\t\t\t\t\t+ \" from information_schema.columns where column_name='\"\n//\t\t\t\t\t\t\t+ GlobalTableUtil.GLOBAL_TABLE_MYCAT_COLUMN + \"' and table_name='\";\n\t\n\t// 此处用到了 mysql 多行转一行 group_concat 的用法，主要是为了简化对结果的处理\n\t// 得到的结果类似于：id,name,_mycat_op_time\n\tprotected String columnExistSQL = \"select group_concat(COLUMN_NAME separator ',') as \"\n\t\t\t+ GlobalTableUtil.INNER_COLUMN +\" from information_schema.columns where TABLE_NAME='\"; //user' and TABLE_SCHEMA='db1';\n\t\n\tprotected List<SQLQueryResult<Map<String, String>>> list = new ArrayList<>();\n\n\t\n\tpublic MySQLConsistencyChecker(MySQLDataSource source, String tableName) {\n\t\tthis.source = source;\n\t\tthis.lock = new ReentrantLock(false);\n\t\tthis.tableName = tableName;\n\t\tthis.countSQL = \" select count(*) as \"+GlobalTableUtil.COUNT_COLUMN+\" from \" \n\t\t\t\t\t\t\t+ this.tableName;\n\t\tthis.maxSQL = \" select max(\"+GlobalTableUtil.GLOBAL_TABLE_MYCAT_COLUMN+\") as \"+\n\t\t\t\t\t\tGlobalTableUtil.MAX_COLUMN+\" from \" + this.tableName;\n\t\tthis.columnExistSQL += this.tableName +\"' \";\n\t}\n\n\tpublic void checkRecordCout() {\n        // [\"db3\",\"db2\",\"db1\"]\n\t\tlock.lock();\n\t\ttry{\n\t\t\tthis.jobCount.set(0);\n\t\t\tbeginTime = new Date().getTime();\n\t        String[] physicalSchemas = source.getDbPool().getSchemas();\n\t        for(String dbName : physicalSchemas){\n\t        \tMySQLConsistencyHelper detector = new MySQLConsistencyHelper(this, null);\n\t        \tOneRawSQLQueryResultHandler resultHandler = \n\t        \t\t\tnew OneRawSQLQueryResultHandler(new String[] {GlobalTableUtil.COUNT_COLUMN}, detector);\n\t        \tSQLJob sqlJob = new SQLJob(this.getCountSQL(), dbName, resultHandler, source);\n\t        \tdetector.setSqlJob(sqlJob);\n\t \t\t    sqlJob.run();\n\t \t\t    this.jobCount.incrementAndGet();\n\t        }\n\t\t}finally{\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\t\n\tpublic void checkMaxTimeStamp() {\n        // [\"db3\",\"db2\",\"db1\"]\n\t\tlock.lock();\n\t\ttry{\n\t\t\tthis.jobCount.set(0);\n\t\t\tbeginTime = new Date().getTime();\n\t        String[] physicalSchemas = source.getDbPool().getSchemas();\n\t        for(String dbName : physicalSchemas){\n\t        \tMySQLConsistencyHelper detector = new MySQLConsistencyHelper(this, null);\n\t        \tOneRawSQLQueryResultHandler resultHandler = \n\t        \t\t\tnew OneRawSQLQueryResultHandler(new String[] {GlobalTableUtil.MAX_COLUMN}, detector);\n\t        \tSQLJob sqlJob = new SQLJob(this.getMaxSQL(), dbName, resultHandler, source);\n\t        \tdetector.setSqlJob(sqlJob);\n\t \t\t    sqlJob.run();\n\t \t\t    this.jobCount.incrementAndGet();\n\t        }\n\t\t}finally{\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\t\n\t/**\n\t * check inner column exist or not\n\t */\n\tpublic void checkInnerColumnExist() {\n        // [\"db3\",\"db2\",\"db1\"]\n\t\tlock.lock();\n\t\ttry{\n\t\t\tthis.jobCount.set(0);\n\t\t\tbeginTime = new Date().getTime();\n\t        String[] physicalSchemas = source.getDbPool().getSchemas();\n\t        for(String dbName : physicalSchemas){\n\t        \tMySQLConsistencyHelper detector = new MySQLConsistencyHelper(this, null, 1);\n\t        \tOneRawSQLQueryResultHandler resultHandler = \n\t        \t\t\tnew OneRawSQLQueryResultHandler(new String[] {GlobalTableUtil.INNER_COLUMN}, detector);\n\t        \tString db = \" and table_schema='\" + dbName + \"'\";\n\t        \tSQLJob sqlJob = new SQLJob(this.columnExistSQL + db , dbName, resultHandler, source);\n\t        \tdetector.setSqlJob(sqlJob);//table_schema='db1'\n\t        \tLOGGER.debug(sqlJob.toString());\n\t \t\t    sqlJob.run();\n\t \t\t    this.jobCount.incrementAndGet();\n\t        }\n\t\t}finally{\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\t\n\tpublic void setResult(SQLQueryResult<Map<String, String>> result) {\n\t\t// LOGGER.debug(\"setResult::::::::::\" + JSON.toJSONString(result));\n\t\tlock.lock();\n\t\ttry{\n\t\t\tthis.jobCount.decrementAndGet();\n\t\t\tif(result != null && result.isSuccess()){\n\t\t\t\tresult.setTableName(tableName);\n\t\t\t\tlist.add(result);\n\t\t\t}else{\n\t\t\t\tif(result != null && result.getResult() != null){\n\t\t\t\t\tString sql = null;\n\t\t\t\t\tif(result.getResult().containsKey(GlobalTableUtil.COUNT_COLUMN))\n\t\t\t\t\t\tsql = this.getCountSQL();\n\t\t\t\t\tif(result.getResult().containsKey(GlobalTableUtil.MAX_COLUMN))\n\t\t\t\t\t\tsql = this.getMaxSQL();\n\t\t\t\t\tif(result.getResult().containsKey(GlobalTableUtil.INNER_COLUMN))\n\t\t\t\t\t\tsql = this.getColumnExistSQL();\n\t\t\t\t\tLOGGER.warn(sql+ \" execute failed in db: \" + result.getDataNode()\n\t\t\t\t\t\t\t\t + \" during global table consistency check task.\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(this.jobCount.get() <= 0 || isTimeOut()){\n\t\t\t\tGlobalTableUtil.finished(list);\n\t    \t}\n\t\t}finally{\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\t\n\tpublic boolean isTimeOut(){\n\t\tlong duration = new Date().getTime() - this.beginTime;\n\t\treturn TimeUnit.MINUTES.convert(duration, TimeUnit.MILLISECONDS) > 1; // 1分钟超时\n\t}\n\t\n\tpublic String getCountSQL() {\n\t\treturn countSQL;\n\t}\n\tpublic String getColumnExistSQL() {\n\t\treturn columnExistSQL;\n\t}\n\tpublic void setColumnExistSQL(String columnExistSQL) {\n\t\tthis.columnExistSQL = columnExistSQL;\n\t}\n\tpublic String getMaxSQL() {\n\t\treturn maxSQL;\n\t}\n\tpublic String getTableName() {\n\t\treturn tableName;\n\t}\n\tpublic MySQLDataSource getSource() {\n\t\treturn source;\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/heartbeat/MySQLConsistencyCheckerHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.heartbeat;\n\nimport java.util.Date;\nimport java.util.Map;\n\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.mysql.nio.MySQLDataSource;\nimport io.mycat.server.interceptor.impl.GlobalTableUtil;\nimport io.mycat.sqlengine.OneRawSQLQueryResultHandler;\nimport io.mycat.sqlengine.SQLJob;\nimport io.mycat.sqlengine.SQLQueryResult;\n\n\n/**\n * @author zwy\n */\npublic class MySQLConsistencyCheckerHandler extends MySQLConsistencyChecker{\n\tprivate final ConsistenCollectHandler handler;\n\tprivate volatile int sqlSeq = 1;\n\tprivate final PhysicalDBNode dbNode;\n\tpublic MySQLConsistencyCheckerHandler(PhysicalDBNode dbNode, MySQLDataSource source,\n\t\t\tString tableName ,ConsistenCollectHandler handler) {\n\t\tsuper(source, tableName);\n\t\tthis.handler = handler;\n\t\tthis.dbNode = dbNode;\n\t}\t\n\n    //2\n\tpublic void checkRecordCout() {\n\t\tthis.jobCount.set(0);\n\t\tbeginTime = new Date().getTime();\n\t\tString dbName = dbNode.getDatabase();\n    \tMySQLConsistencyHelper detector = new MySQLConsistencyHelper(this, null);\n    \tOneRawSQLQueryResultHandler resultHandler = \n    \t\t\tnew OneRawSQLQueryResultHandler(new String[] {GlobalTableUtil.COUNT_COLUMN}, detector);\n    \tSQLJob sqlJob = new SQLJob(this.getCountSQL(), dbName, resultHandler, source);\n    \tdetector.setSqlJob(sqlJob);\n\t    this.jobCount.incrementAndGet();\n\t    sqlJob.run();\n        \t\n\t}\n\t//1\n\tpublic void checkMaxTimeStamp() {\n\t\tthis.jobCount.set(0);\n\t\tbeginTime = new Date().getTime();\n\t\tString dbName = dbNode.getDatabase();\n    \tMySQLConsistencyHelper detector = new MySQLConsistencyHelper(this, null, 0);\n    \tOneRawSQLQueryResultHandler resultHandler = \n    \t\t\tnew OneRawSQLQueryResultHandler(new String[] {GlobalTableUtil.MAX_COLUMN}, detector);\n    \tSQLJob sqlJob = new SQLJob(this.getMaxSQL(), dbName, resultHandler, source);\n    \tdetector.setSqlJob(sqlJob);\n\t    this.jobCount.incrementAndGet();      \n\t    sqlJob.run();\n\t}\n\t\n\t/**\n\t * check inner column exist or not\n\t */\n\t//0\n\tpublic void checkInnerColumnExist() {\n\t\t// [\"db3\",\"db2\",\"db1\"]\n\t\tthis.jobCount.set(0);\n\t\tbeginTime = new Date().getTime();\n\t\tString dbName = dbNode.getDatabase();\n    \tMySQLConsistencyHelper detector = new MySQLConsistencyHelper(this, null, 1);\n    \tOneRawSQLQueryResultHandler resultHandler = \n    \t\t\tnew OneRawSQLQueryResultHandler(new String[] {GlobalTableUtil.INNER_COLUMN}, detector);\n    \tString db = \" and table_schema='\" + dbName + \"'\";\n    \tSQLJob sqlJob = new SQLJob(this.columnExistSQL + db , dbName, resultHandler, source);\n    \tdetector.setSqlJob(sqlJob);//table_schema='db1'\n\t    this.jobCount.incrementAndGet();\n    \tsqlJob.run();\n\t\n\t}\n\tpublic volatile boolean isStop = false;\n//\tvolatile SQLQueryResult<Map<String, String>> record =  null;\n\tvolatile SQLQueryResult<Map<String, String>> resultMap = null;\n\tpublic void setResult(SQLQueryResult<Map<String, String>> result) {\n//\t\t LOGGER.debug(\"setResult::::::::::\" + JSON.toJSONString(result));\n\t\tif(isStop){\n\t\t\treturn ;\n\t\t}\n\t\tif(result != null && result.isSuccess()){\t\n\t\t\tjobCount.decrementAndGet();\n\t\t\tString dataNode = result.getDataNode();\n\t\t\tresult.setTableName(this.getTableName());\n\t\t\tif(resultMap == null) {\n\t\t\t\tresultMap = result;\n\t\t\t} else {\n\t\t\t\t//\n\t\t\t\tSQLQueryResult<Map<String, String>> r = resultMap;\n\t\t\t\tMap<String, String> metaData = result.getResult();\n\t\t\t\tfor(String key : metaData.keySet()) {\n\t\t\t\t\tr.getResult().put(key, metaData.get(key));\n\t\t\t\t}\n\t\t\t\tresultMap = r;\n\t\t\t}\n\t\t\t\n\t\t}else{\n\t\t\tif(result != null && result.getResult() != null) {\n\t\t\t\tString sql = null;\t\t\t\t\n\t\t\t\tfinal int seq = sqlSeq ;\n\t\t\t\tif(seq == 0){\n\t\t\t\t\tsql = this.getColumnExistSQL();\n\t\t\t\t} else if(seq == 1) {\n\t\t\t\t\tsql = this.getMaxSQL();\n\t\t\t\t} else if(seq == 2) {\n\t\t\t\t\tsql = this.getCountSQL();\n\t\t\t\t} else {\n\t\t\t\t\tsql = result.getErrMsg();\n\t\t\t\t}\n\t\t\t\tString errMsg = sql+ \" execute failed in db: \" + result.getDataNode()\n\t\t\t\t + \" during global table consistency check task.\";\n\t\t\t\tLOGGER.warn(errMsg);\n\t\t\t\thandler.onError(errMsg);\n\t\t\t}\n\t\t}\n\t\t//任务都完成之后 进行下一个sql校验\n\t\tif(jobCount.get() == 0 ){\n\t\t\tfinal int seq = ++sqlSeq ;\n\t\t\tif(seq == 1){\n\t\t\t\tthis.checkMaxTimeStamp();\n\t\t\t} else if(seq == 2) {\n\t\t\t\tthis.checkRecordCout();\n\t\t\t} else {\n\t\t\t\thandler.onSuccess(resultMap);\n\t\t\t\tisStop = true;\n\t\t\t}\t\t\t\t\t\t\t\t\t\n\t\t} else if(isTimeOut()){\n\t\t\tString execSql = \"\";\n\t\t\tfinal int seq = sqlSeq ;\n\t\t\tif(seq == 0){\n\t\t\t\texecSql = this.getColumnExistSQL();\n\t\t\t} else if(seq == 1) {\n\t\t\t\texecSql = this.getMaxSQL();\n\t\t\t} else if(seq == 2) {\n\t\t\t\texecSql = this.getCountSQL();\n\t\t\t}\n\t\t\tisStop = true;\n\t\t\thandler.onError(String.format(\"sql %s time out\", execSql));\n\t\t}\t\n\t\t\n\t}\n\t\n\t\n\t\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/heartbeat/MySQLConsistencyHelper.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese\r\n * opensource volunteers. you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n *\r\n * Any questions about this component can be directed to it's project Web address\r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.backend.heartbeat;\r\n\r\nimport io.mycat.server.interceptor.impl.GlobalTableUtil;\r\nimport io.mycat.sqlengine.SQLJob;\r\nimport io.mycat.sqlengine.SQLQueryResult;\r\nimport io.mycat.sqlengine.SQLQueryResultListener;\r\n\r\nimport java.util.Map;\r\nimport java.util.concurrent.TimeUnit;\r\nimport java.util.concurrent.atomic.AtomicInteger;\r\n\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport com.alibaba.fastjson.JSON;\r\n\r\n/**\r\n * @author digdeep@126.com\r\n */\r\npublic class MySQLConsistencyHelper implements SQLQueryResultListener<SQLQueryResult<Map<String, String>>> {\r\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(MySQLConsistencyHelper.class);\r\n    private MySQLConsistencyChecker heartbeat;\r\n    private volatile SQLJob sqlJob;\r\n    private int RETRY_TIMES = 5;\r\n    private AtomicInteger retryTime = new AtomicInteger();\r\n    \r\n    public MySQLConsistencyHelper(MySQLConsistencyChecker heartbeat, SQLJob sqlJob) {\r\n    \tthis.heartbeat = heartbeat;\r\n        this.sqlJob = sqlJob;\r\n        this.retryTime.set(RETRY_TIMES);\r\n    }\r\n    \r\n    public MySQLConsistencyHelper(MySQLConsistencyChecker heartbeat, \r\n    \t\t\t\t\t\t\t\tSQLJob sqlJob, int retryTime) {\r\n    \tthis.heartbeat = heartbeat;\r\n        this.sqlJob = sqlJob;\r\n        if(retryTime > 0 && retryTime < 10)\r\n        \tthis.retryTime.set(retryTime);\r\n        else\r\n        \tthis.retryTime.set(RETRY_TIMES);\r\n    }\r\n\r\n    @Override\r\n    public void onResult(SQLQueryResult<Map<String, String>> result) {\r\n    \t// {\"dataNode\":\"db2\",\"result\":{\"max_timestamp\":\"1450423751170\"},\"success\":true}\r\n    \t// {\"dataNode\":\"db3\",\"result\":{\"count(*)\":\"1\"},\"success\":true}\r\n//    \tLOGGER.debug(\"result:\" + JSON.toJSONString(result));\r\n    \tMap<String, String> rowMap = null;\r\n    \tString count = null; String innerCol = null;\r\n    \tString maxTimestamp = null;\r\n    \tif(result != null)\r\n    \t\trowMap = result.getResult();\r\n    \t\r\n    \tif(rowMap != null){\r\n    \t\tmaxTimestamp = rowMap.get(GlobalTableUtil.MAX_COLUMN);\r\n    \t\tcount = rowMap.get(GlobalTableUtil.COUNT_COLUMN);\r\n    \t\tinnerCol = rowMap.get(GlobalTableUtil.INNER_COLUMN);\r\n    \t\tif((rowMap.containsKey(GlobalTableUtil.MAX_COLUMN) && StringUtils.isNotBlank(maxTimestamp))\r\n    \t\t\t\t|| (rowMap.containsKey(GlobalTableUtil.COUNT_COLUMN) && StringUtils.isNotBlank(count))\r\n    \t\t\t\t|| (rowMap.containsKey(GlobalTableUtil.INNER_COLUMN) && StringUtils.isNotBlank(innerCol))){\r\n    \t\t\theartbeat.setResult(result);\r\n    \t\t\treturn;\r\n    \t\t}else{\r\n    \t\t\tif(this.retryTime.get() > 0){\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tTimeUnit.MICROSECONDS.sleep(10);\r\n\t\t\t\t\t} catch (InterruptedException e) {\r\n\t\t\t\t\t}\r\n\t\t\t\t\tthis.retryTime.decrementAndGet();\r\n    \t\t\t\tthis.sqlJob.run();\r\n    \t\t\t\treturn;\r\n\t\t\t\t}\r\n    \t\t\theartbeat.setResult(result);\r\n    \t\t\treturn;\r\n    \t\t}\r\n    \t}else{\r\n    \t\tif(this.retryTime.get() > 0){\r\n    \t\t\ttry {\r\n    \t\t\t\tTimeUnit.MICROSECONDS.sleep(3);\r\n\t\t\t\t} catch (InterruptedException e) {\r\n\t\t\t\t}\r\n\t\t\t\tthis.retryTime.decrementAndGet();\r\n\t\t\t\tthis.sqlJob.run();\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n    \t\theartbeat.setResult(result);\r\n\t\t\treturn;\r\n    \t}\r\n    }\r\n\r\n    public void close(String msg) {\r\n        SQLJob curJob = sqlJob;\r\n        if (curJob != null && !curJob.isFinished()) {\r\n            curJob.teminate(msg);\r\n            sqlJob = null;\r\n        }\r\n    }\r\n    public MySQLConsistencyChecker getHeartbeat() {\r\n        return heartbeat;\r\n    }\r\n\tpublic SQLJob getSqlJob() {\r\n\t\treturn sqlJob;\r\n\t}\r\n\tpublic void setSqlJob(SQLJob sqlJob) {\r\n\t\tthis.sqlJob = sqlJob;\r\n\t}\r\n    \r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/heartbeat/MySQLDetector.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.heartbeat;\n\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.mysql.nio.MySQLDataSource;\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.sqlengine.OneRawSQLQueryResultHandler;\nimport io.mycat.sqlengine.SQLJob;\nimport io.mycat.sqlengine.SQLQueryResult;\nimport io.mycat.sqlengine.SQLQueryResultListener;\nimport io.mycat.util.TimeUtil;\n\n/**\n * @author mycat\n */\npublic class MySQLDetector implements SQLQueryResultListener<SQLQueryResult<Map<String, String>>> {\n\t\n\tprivate MySQLHeartbeat heartbeat;\n\t\n\tprivate long heartbeatTimeout;\n\tprivate final AtomicBoolean isQuit;\n\tprivate volatile long lastSendQryTime;\n\tprivate volatile long lasstReveivedQryTime;\n\tprivate volatile SQLJob sqlJob;\n\t\n\tprivate static final String[] MYSQL_SLAVE_STAUTS_COLMS = new String[] {\n\t\t\t\"Seconds_Behind_Master\", \n\t\t\t\"Slave_IO_Running\", \n\t\t\t\"Slave_SQL_Running\",\n\t\t\t\"Slave_IO_State\",\n\t\t\t\"Master_Host\",\n\t\t\t\"Master_User\",\n\t\t\t\"Master_Port\", \n\t\t\t\"Connect_Retry\",\n\t\t\t\"Last_IO_Error\",\n\t\t\t\"Last_SQL_Error\",\n\t\t\t\"Last_SQL_Errno\"};\n\n\tprivate static final String[] MYSQL_CLUSTER_STAUTS_COLMS = new String[] {\n\t\t\t\"Variable_name\",\n\t\t\t\"Value\"};\n\t\n\tpublic MySQLDetector(MySQLHeartbeat heartbeat) {\n\t\tthis.heartbeat = heartbeat;\n\t\tthis.isQuit = new AtomicBoolean(false);\n\t}\n\n\tpublic MySQLHeartbeat getHeartbeat() {\n\t\treturn heartbeat;\n\t}\n\n\tpublic long getHeartbeatTimeout() {\n\t\treturn heartbeatTimeout;\n\t}\n\n\tpublic void setHeartbeatTimeout(long heartbeatTimeout) {\n\t\tthis.heartbeatTimeout = heartbeatTimeout;\n\t}\n\n\tpublic boolean isHeartbeatTimeout() {\n\t\treturn TimeUtil.currentTimeMillis() > Math.max(lastSendQryTime,\n\t\t\t\tlasstReveivedQryTime) + heartbeatTimeout;\n\t}\n\n\tpublic long getLastSendQryTime() {\n\t\treturn lastSendQryTime;\n\t}\n\n\tpublic long getLasstReveivedQryTime() {\n\t\treturn lasstReveivedQryTime;\n\t}\n\n\tpublic void heartbeat() {\n\t\tlastSendQryTime = System.currentTimeMillis();\n\t\tMySQLDataSource ds = heartbeat.getSource();\n\t\tString databaseName = ds.getDbPool().getSchemas()[0];\n\t\tString[] fetchColms={};\n\t\tif (heartbeat.getSource().getHostConfig().isShowSlaveSql() ) {\n\t\t\tfetchColms=MYSQL_SLAVE_STAUTS_COLMS;\n\t\t}\n\t\tif (heartbeat.getSource().getHostConfig().isShowClusterSql() ) {\n\t\t\tfetchColms=MYSQL_CLUSTER_STAUTS_COLMS;\n\t\t}\n\t\tOneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler( fetchColms, this);\n\t\tsqlJob = new SQLJob(heartbeat.getHeartbeatSQL(), databaseName, resultHandler, ds);\n\t\tsqlJob.run();\n\t}\n\n\tpublic void quit() {\n\t\tif (isQuit.compareAndSet(false, true)) {\n\t\t\tclose(\"heart beat quit\");\n\t\t}\n\n\t}\n\n\tpublic boolean isQuit() {\n\t\treturn isQuit.get();\n\t}\n\n\t@Override\n\tpublic void onResult(SQLQueryResult<Map<String, String>> result) {\n\t\t\n\t\tif (result.isSuccess()) {\n            \n\t\t\tint balance = heartbeat.getSource().getDbPool().getBalance();\n            \n\t\t\tPhysicalDatasource source = heartbeat.getSource();\n            int switchType = source.getHostConfig().getSwitchType();\n            Map<String, String> resultResult = result.getResult();\n          \n\t\t\tif ( resultResult!=null&& !resultResult.isEmpty() &&switchType == DataHostConfig.SYN_STATUS_SWITCH_DS\n\t\t\t\t\t&& source.getHostConfig().isShowSlaveSql()) {\n\t\t\t\t\n\t\t\t\tString Slave_IO_Running  = resultResult != null ? resultResult.get(\"Slave_IO_Running\") : null;\n\t\t\t\tString Slave_SQL_Running = resultResult != null ? resultResult.get(\"Slave_SQL_Running\") : null;\n\t\t\t\tString Last_SQL_Error    = resultResult != null ? resultResult.get(\"Last_SQL_Error\") : null;\n\t\t\t\tLast_SQL_Error = Last_SQL_Error == null ? \"\" : Last_SQL_Error;\n\n\t\t\t\tif (\"\".equals(Last_SQL_Error) && Slave_IO_Running != null\n\t\t\t\t\t\t&& Slave_IO_Running.equals(Slave_SQL_Running) \n\t\t\t\t\t\t&& Slave_SQL_Running.equals(\"Yes\")\n\t\t\t\t\t\t&& source.isSalveOrRead()) {\n\t\t\t\t\t\n\t\t\t\t\theartbeat.setDbSynStatus(DBHeartbeat.DB_SYN_NORMAL);\n\t\t\t\t\tString Seconds_Behind_Master = resultResult.get( \"Seconds_Behind_Master\");\t\t\t\t\t\n\t\t\t\t\tif (null == Seconds_Behind_Master ){\n\t\t\t\t\t\tMySQLHeartbeat.LOGGER.warn(\"Master is down but its relay log is clean.\");\n\t\t\t\t\t\theartbeat.setSlaveBehindMaster(0);\n\t\t\t\t\t}else if(!\"\".equals(Seconds_Behind_Master)) {\n\t\t\t\t\t\tint Behind_Master = Integer.parseInt(Seconds_Behind_Master);\n\t\t\t\t\t\tif ( Behind_Master >  source.getHostConfig().getSlaveThreshold() ) {\n\t\t\t\t\t\t\tMySQLHeartbeat.LOGGER.warn(\"found MySQL master/slave Replication delay !!! \"\n\t\t\t\t\t\t\t\t\t+ heartbeat.getSource().getConfig() + \", binlog sync time delay: \" + Behind_Master + \"s\" );\n\t\t\t\t\t\t}\t\t\t\t\t\t\n\t\t\t\t\t\theartbeat.setSlaveBehindMaster( Behind_Master );\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t} else if( source.isSalveOrRead() ) {\t\t\t\t\t\n\t\t\t\t\t//String Last_IO_Error = resultResult != null ? resultResult.get(\"Last_IO_Error\") : null;\t\t\t\t\t\n\t\t\t\t\tMySQLHeartbeat.LOGGER.warn(\"found MySQL master/slave Replication err !!! \" \n\t\t\t\t\t\t\t\t+ heartbeat.getSource().getConfig() + \", \" + resultResult);\n\t\t\t\t\theartbeat.setDbSynStatus(DBHeartbeat.DB_SYN_ERROR);\n\t\t\t\t}\n\n\t\t\t\theartbeat.getAsynRecorder().set(resultResult, switchType);\n\t\t\t\theartbeat.setResult(MySQLHeartbeat.OK_STATUS, this,  null);\n\t\t\t\t\n            } else if ( resultResult!=null&& !resultResult.isEmpty() && switchType==DataHostConfig.CLUSTER_STATUS_SWITCH_DS\n            \t\t&& source.getHostConfig().isShowClusterSql() ) {\n            \t\n\t\t\t\t//String Variable_name = resultResult != null ? resultResult.get(\"Variable_name\") : null;\n\t\t\t\tString wsrep_cluster_status = resultResult != null ? resultResult.get(\"wsrep_cluster_status\") : null;// Primary\n\t\t\t\tString wsrep_connected = resultResult != null ? resultResult.get(\"wsrep_connected\") : null;// ON\n\t\t\t\tString wsrep_ready = resultResult != null ? resultResult.get(\"wsrep_ready\") : null;// ON\n\t\t\t\t\n\t\t\t\tif (\"ON\".equals(wsrep_connected) \n\t\t\t\t\t\t&& \"ON\".equals(wsrep_ready)\n\t\t\t\t\t\t&& \"Primary\".equals(wsrep_cluster_status)) {\n\t\t\t\t\t\n\t\t\t\t\theartbeat.setDbSynStatus(DBHeartbeat.DB_SYN_NORMAL);\n\t\t\t\t\theartbeat.setResult(MySQLHeartbeat.OK_STATUS, this, null);\n\t\t\t\t\t\n\t\t\t\t} else {\t\t\t\t\t\n\t\t\t\t\tMySQLHeartbeat.LOGGER.warn(\"found MySQL  cluster status err !!! \" \n\t\t\t\t\t\t\t+ heartbeat.getSource().getConfig() \n\t\t\t\t\t\t\t+ \" wsrep_cluster_status: \"+ wsrep_cluster_status  \n\t\t\t\t\t\t\t+ \" wsrep_connected: \"+ wsrep_connected\n\t\t\t\t\t\t\t+ \" wsrep_ready: \"+ wsrep_ready\n\t\t\t\t\t);\n\t\t\t\t\t\n\t\t\t\t\theartbeat.setDbSynStatus(DBHeartbeat.DB_SYN_ERROR);\n\t\t\t\t\theartbeat.setResult(MySQLHeartbeat.ERROR_STATUS, this,  null);\n\t\t\t\t}\t\t\t\t\n\t\t\t\theartbeat.getAsynRecorder().set(resultResult, switchType);\n    \t\t\t\n\t\t\t} else {\t\t\t\t\n    \t\t\theartbeat.setResult(MySQLHeartbeat.OK_STATUS, this,  null);\n    \t\t}\n\t\t\t//监测数据库同步状态，在 switchType=-1或者1的情况下，也需要收集主从同步状态\n\t\t\theartbeat.getAsynRecorder().set(resultResult, switchType);\n            \n\t\t} else {\n\t\t\tMySQLHeartbeat.LOGGER.warn(\"heart beat error: \" + heartbeat.getSource().getName() + \"/\"\n\t\t\t\t\t+ heartbeat.getSource().getHostConfig().getName() + \" retry=\" + heartbeat.getHeartbeatRetry()\n\t\t\t\t\t+ \" tmo=\" + heartbeat.getHeartbeatTimeout());\n\t\t\theartbeat.setResult(MySQLHeartbeat.ERROR_STATUS, this,  null);\n\t\t}\n\t\t\n\t\tlasstReveivedQryTime = System.currentTimeMillis();\n\t\theartbeat.getRecorder().set((lasstReveivedQryTime - lastSendQryTime));\n\t}\n\n\tpublic void close(String msg) {\n\t\tSQLJob curJob = sqlJob;\n\t\tif (curJob != null && !curJob.isFinished()) {\n\t\t\tcurJob.teminate(msg);\n\t\t\tsqlJob = null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/heartbeat/MySQLHeartbeat.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.heartbeat;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.mysql.nio.MySQLDataSource;\nimport io.mycat.config.model.DataHostConfig;\n\n/**\n * @author mycat\n */\npublic class MySQLHeartbeat extends DBHeartbeat {\n\n//\tprivate static final int MAX_RETRY_COUNT = 5;\n\tpublic static final Logger LOGGER = LoggerFactory.getLogger(MySQLHeartbeat.class);\n\n\tprivate final MySQLDataSource source;\n\n\tprivate final ReentrantLock lock;\n\tprivate final int maxRetryCount;\n\n\tprivate MySQLDetector detector;\n\n\tpublic MySQLHeartbeat(MySQLDataSource source) {\n\t\tthis.source = source;\n\t\tthis.lock = new ReentrantLock(false);\n\t\tthis.maxRetryCount = source.getHostConfig().getMaxRetryCount();\n\t\tthis.status = INIT_STATUS;\n\t\tthis.heartbeatSQL = source.getHostConfig().getHearbeatSQL();\n\t}\n\n\tpublic MySQLDataSource getSource() {\n\t\treturn source;\n\t}\n\n\tpublic MySQLDetector getDetector() {\n\t\treturn detector;\n\t}\n\n\tpublic long getTimeout() {\n\t\tMySQLDetector detector = this.detector;\n\t\tif (detector == null) {\n\t\t\treturn -1L;\n\t\t}\n\t\treturn detector.getHeartbeatTimeout();\n\t}\n\n\tpublic String getLastActiveTime() {\n\t\tMySQLDetector detector = this.detector;\n\t\tif (detector == null) {\n\t\t\treturn null;\n\t\t}\n\t\tlong t = detector.getLasstReveivedQryTime();\n\t\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n\t\treturn sdf.format(new Date(t));\n\t}\n\n\tpublic void start() {\n\t\tfinal ReentrantLock lock = this.lock;\n\t\tlock.lock();\n\t\ttry {\n\t\t\tisStop.compareAndSet(true, false);\n\t\t\tsuper.status = DBHeartbeat.OK_STATUS;\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\n\tpublic void stop() {\n\t\tfinal ReentrantLock lock = this.lock;\n\t\tlock.lock();\n\t\ttry {\n\t\t\tif (isStop.compareAndSet(false, true)) {\n\t\t\t\tif (isChecking.get()) {\n\t\t\t\t\t// nothing\n\t\t\t\t} else {\n\t\t\t\t\tMySQLDetector detector = this.detector;\n\t\t\t\t\tif (detector != null) {\n\t\t\t\t\t\tdetector.quit();\n\t\t\t\t\t\tisChecking.set(false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\n\t/**\n\t * execute heart beat\n\t */\n\tpublic void heartbeat() {\n\t\tfinal ReentrantLock lock = this.lock;\n\t\tif(!lock.tryLock()){\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tif (isChecking.compareAndSet(false, true)) {\n\t\t\t\tMySQLDetector detector = this.detector;\n\t\t\t\tif (detector == null || detector.isQuit()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdetector = new MySQLDetector(this);\n\t\t\t\t\t\t//由于没有设置导致无限循环. modifyBy zwy  todo 对应修改其他的心跳机制.\n\t\t\t\t\t\tdetector.setHeartbeatTimeout(this.getHeartbeatTimeout());\n\t\t\t\t\t\tdetector.heartbeat();\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOGGER.warn(source.getConfig().toString(), e);\n\t\t\t\t\t\tsetResult(ERROR_STATUS, detector, null);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tthis.detector = detector;\n\t\t\t\t} else {\n\t\t\t\t\t\tdetector.heartbeat();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tMySQLDetector detector = this.detector;\n\t\t\t\tif (detector != null) {\n\t\t\t\t\tif (detector.isQuit()) {\n\t\t\t\t\t\tisChecking.compareAndSet(true, false);\n\t\t\t\t\t} else if (detector.isHeartbeatTimeout()) {\n\t\t\t\t\t\tsetResult(TIMEOUT_STATUS, detector, null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\n\tpublic void setResult(int result, MySQLDetector detector, String msg) {\n\t\tthis.isChecking.set(false);\n\t\tswitch (result) {\n\t\tcase OK_STATUS:\n\t\t\tsetOk(detector);\n\t\t\tbreak;\n\t\tcase ERROR_STATUS:\n\t\t\tsetError(detector);\n\t\t\tbreak;\n\t\tcase TIMEOUT_STATUS:\n\t\t\tsetTimeout(detector);\n\t\t\tbreak;\n\t\t}\n\t\tif (this.status != OK_STATUS) {\n\t\t\tswitchSourceIfNeed(\"heartbeat error\");\n\t\t}\n\n\t}\n\n\tprivate void setOk(MySQLDetector detector) {\n\t\tswitch (status) {\n\t\tcase DBHeartbeat.TIMEOUT_STATUS:\n\t\t\twriteStatusMsg(source.getDbPool().getHostName(), source.getName() ,DBHeartbeat.INIT_STATUS);\n\t\t\tthis.status = DBHeartbeat.INIT_STATUS;\n\t\t\tthis.errorCount.set(0);\t\t\t\n\t\t\t//前一个状态为超时 当前状态为正常状态  那就马上发送一个请求 来验证状态是否恢复为Ok\n\t\t\tif (isStop.get()) {\n\t\t\t\tdetector.quit();\n\t\t\t} else {\n\t\t\t\theartbeat();// timeout, heart beat again\n\t\t\t}\n\t\t\tbreak;\n\t\tcase DBHeartbeat.OK_STATUS:\n\t\t\tthis.errorCount.set(0);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\twriteStatusMsg(source.getDbPool().getHostName(), source.getName() ,DBHeartbeat.OK_STATUS);\n\t\t\tthis.status = OK_STATUS;\n\t\t\tthis.errorCount.set(0);;\n\t\t}\n\t\tif (isStop.get()) {\n\t\t\tdetector.quit();\n\t\t}\n\t}\n\t//发生错误了,是否进行下一次心跳检测的策略 . 是否进行下一次心跳检测.\n\tprivate void nextDector(MySQLDetector detector, int nextStatue) {\t\n\t\t\n\t\tif (isStop.get()) {\n\t\t\tdetector.quit();\n\t\t\twriteStatusMsg(source.getDbPool().getHostName(), source.getName() ,DBHeartbeat.OK_STATUS);\n\t\t\tthis.status = nextStatue;\t\t\t\n\t\t} else {  \n\t\t\t// should continues check error status\n\t\t\tif(errorCount.get() < maxRetryCount) {\n\t\t\t\t//设置3秒钟之后重试.\n\t\t\t\tif (detector != null && !detector.isQuit()) {\n\t            \tLOGGER.error(\"set Error \" + errorCount + \"  \" +  this.source.getConfig() );\n\t\t\t\t//\tsource.setHeartbeatRecoveryTime( TimeUtil.currentTimeMillis() + 3000);\n\t               // heartbeat(); // error count not enough, heart beat again\n\t            }\n\t\t\t} else {\n\t\t\t\tif (detector != null ) {\n\t                detector.quit();\n\t            }\n\t\t\t\twriteStatusMsg(source.getDbPool().getHostName(), source.getName() ,nextStatue);\n\t\t\t\tthis.status = nextStatue;\t            \n\t\t\t\tthis.errorCount.set(0);\n\t\t\t}\n\t\t}\n\t}\n\n\n\tprivate void setError(MySQLDetector detector) {\n\t\terrorCount.incrementAndGet() ;\n\t\tnextDector(detector, ERROR_STATUS);\n\t\t// should continues check error status\n//\t\tif (errorCount.incrementAndGet() < maxRetryCount) {\n//\n//            if (detector != null && !detector.isQuit()) {\n//            \tLOGGER.debug(\"set Error \" + errorCount);\n//\t\t\t\tsource.setHeartbeatRecoveryTime( TimeUtil.currentTimeMillis() + 3000);\n//               // heartbeat(); // error count not enough, heart beat again\n//            }\n//\n//\t\t}else\n//        {\n//            if (detector != null ) {\n//                detector.quit();\n//            }\n//            this.status = ERROR_STATUS;\n//\t\t\tthis.errorCount.set(0);\n//        }\n\t}\n\n\tprivate void setTimeout(MySQLDetector detector) {\n\t\tthis.isChecking.set(false);\n\t\terrorCount.incrementAndGet() ;\n\t\tnextDector(detector, TIMEOUT_STATUS);\n\t\t//status = DBHeartbeat.TIMEOUT_STATUS;\n\t}\n\n\t/**\n\t * switch data source\n\t */\n\tprivate void switchSourceIfNeed(String reason) {\n\t\tint switchType = source.getHostConfig().getSwitchType();\n\t\tString notSwitch = source.getHostConfig().getNotSwitch();\n\t\tif (notSwitch.equals(DataHostConfig.FOVER_NOT_SWITCH_DS) \n\t\t\t\t|| switchType == DataHostConfig.NOT_SWITCH_DS) {\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(\"not switch datasource ,for switchType is \"\n\t\t\t\t\t\t+ DataHostConfig.NOT_SWITCH_DS);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"to  switchSourceIfNeed function 进行读节点转换 \"\n\t\t\t\t\t);\n\t\t}\n\t\tPhysicalDBPool pool = this.source.getDbPool();\n\t\tint curDatasourceHB = pool.getSource().getHeartbeat().getStatus();\n\t\t// read node can't switch ,only write node can switch\n\t\tif (pool.getWriteType() == PhysicalDBPool.WRITE_ONLYONE_NODE\n\t\t\t\t&& !source.isReadNode()\n\t\t\t\t&& curDatasourceHB != DBHeartbeat.OK_STATUS\n\t\t\t\t&& pool.getSources().length > 1) {\n\t\t\tsynchronized (pool) {\n\t\t\t\t// try to see if need switch datasource\n\t\t\t\tcurDatasourceHB = pool.getSource().getHeartbeat().getStatus();\n\t\t\t\tif (curDatasourceHB != DBHeartbeat.INIT_STATUS && curDatasourceHB != DBHeartbeat.OK_STATUS) {\n\t\t\t\t\tint curIndex = pool.getActivedIndex();\n\t\t\t\t\tint nextId = pool.next(curIndex);\n\t\t\t\t\tPhysicalDatasource[] allWriteNodes = pool.getSources();\n\t\t\t\t\twhile (true) {\n\t\t\t\t\t\tif (nextId == curIndex) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tPhysicalDatasource theSource = allWriteNodes[nextId];\n\t\t\t\t\t\tDBHeartbeat theSourceHB = theSource.getHeartbeat();\n\t\t\t\t\t\tint theSourceHBStatus = theSourceHB.getStatus();\n\t\t\t\t\t\tif (theSourceHBStatus == DBHeartbeat.OK_STATUS) {\n\t\t\t\t\t\t\tif (switchType == DataHostConfig.SYN_STATUS_SWITCH_DS) {\n\t\t\t\t\t\t\t\tLOGGER.warn(\"switchSourceIfNeed: LagTime=\" + theSourceHB.getSlaveBehindMaster());\n\t\t\t\t\t\t\t\tif (Integer.valueOf(0).equals( theSourceHB.getSlaveBehindMaster())) {\n\t\t\t\t\t\t\t\t\tLOGGER.info(\"try to switch datasource ,slave is synchronized to master \" + theSource.getConfig());\n\t\t\t\t\t\t\t\t\tpool.switchSourceOrVoted(nextId, true, reason);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tLOGGER.warn(\"ignored  datasource ,slave is not  synchronized to master , slave behind master :\"\n\t\t\t\t\t\t\t\t\t\t\t+ theSourceHB.getSlaveBehindMaster()\n\t\t\t\t\t\t\t\t\t\t\t+ \" \" + theSource.getConfig());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// normal switch\n\t\t\t\t\t\t\t\tLOGGER.info(\"try to switch datasource ,not checked slave synchronize status \" + theSource.getConfig());\n\t\t\t\t\t\t\t\tpool.switchSourceOrVoted(nextId, true, reason);\n                                break;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnextId = pool.next(nextId);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/heartbeat/zkprocess/ManageHeartBeatChange.java",
    "content": "package io.mycat.backend.heartbeat.zkprocess;\n\nimport java.io.ByteArrayInputStream;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.recipes.cache.ChildData;\nimport org.apache.curator.framework.recipes.cache.NodeCache;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCache;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;\nimport org.apache.curator.framework.recipes.leader.Participant;\nimport org.apache.curator.framework.recipes.locks.InterProcessMutex;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.util.StringUtil;\nimport io.mycat.util.TimeUtil;\nimport io.mycat.util.ZKUtils;\nimport io.netty.util.internal.ConcurrentSet;\n\n//作为leader节点去管理所有的\n//注释：\n/* 源文件名：ManageHeartBeatChange.java\n* 文件版本：1.0.0\n* 创建作者：zwy\n* 创建日期：2018年5月20日\n*/\npublic class ManageHeartBeatChange implements Runnable {\n\t\n\tpublic static final Logger LOGGER = LoggerFactory.getLogger(ManageHeartBeatChange.class);\n\t\n\tpublic static int ENTER_SELECT = 0; //每个人进行 投票\n\tpublic static int IS_SELECT = 1; //开始进入统计票数\n\tpublic static int IS_CHANGING = 2; //正在切换节点.\n\n\tpublic static int NOT_SELECT = -1;\n\tprivate final String dataHost;\n\tprivate volatile AtomicInteger statue = new AtomicInteger(NOT_SELECT);\n\tpublic ConcurrentSet<String> voteSet = new ConcurrentSet<>(); //投票的结果集\n\tprivate volatile PathChildrenCache manageVoteCache; //投票的结果集处理\n\tprivate final CuratorFramework client;\n\tprivate final String path; \n\tfinal ScheduledExecutorService service = MycatServer.getInstance().getHeartbeatScheduler(); //定时器\n\tprivate volatile NodeCache changingResultNode; //节点切换读写节点的状态的改变 的处理 \n\tprivate final MycatLeaderLatch mycatLeaderLatch; // \n\tprivate  InterProcessMutex changingStatueLock;\n\tprivate final String manageVotePath; \n\tprivate final String changingResultPath; //节点切换读写节点的状态的改变的路径.\n\n\tprivate long maxTimeToWait = 60 * 1000; //最多的等待时间去进行投票结果\n\tprivate final long minTimeToSwitched = 30 * 60 * 1000; //至少的等待时间去进行下一次切换\n\tprivate volatile long changingFinishDate = 0; //节点切换读写节点的状态的改变的路径.\n\tprivate volatile ScheduledFuture<?> future = null;\n\tprivate volatile ScheduledFuture<?> changingResultFutrue = null;\n\tpublic ManageHeartBeatChange(MycatLeaderLatch myLeaderLatch,\n\t\t\tfinal String dataHost) throws Exception{\n\t\tstatue.set(NOT_SELECT);\n\t\tthis.dataHost = dataHost; //dataSource的名称\n\t\tthis.path = ZKUtils.getZKBasePath() +\"heartbeat/\" + dataHost +\"/\";\n\t\tthis.manageVotePath =  path+ \"voteInformation\";\n\t\tthis.changingResultPath = path + \"changingStatue\";\n\t\tthis.client = ZKUtils.getConnection();\n\t\tchangingStatueLock =  new InterProcessMutex(client, ZKUtils.getZKBasePath() +\"heartbeat/changingStatueLock\");\n\t\tthis.mycatLeaderLatch = myLeaderLatch;\t\t\n\t\t\n\t}\n\t//收集投票结果\n\tpublic boolean addPath(String nodePath) {\n\t\tLOGGER.debug(\"add vote information \"  + nodePath);\n\t\t//判断是否可以收集投票结果 如果不行直接删除\n\t\tif(TimeUtil.currentTimeMillis() - changingFinishDate  < minTimeToSwitched && statue.get() == NOT_SELECT ) {\n\t\t\ttry {\n\t\t\t\tclient.delete().deletingChildrenIfNeeded().forPath(nodePath);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace(); \n\t\t\t\tLOGGER.error(\"remove vote information debug during not voting time\" ,e);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tif(statue.compareAndSet(NOT_SELECT, ENTER_SELECT)){\n//\t\t\tbeginVoteTime = new Date(); // 开始投票时间\n\t\t\t future = service.schedule(this, maxTimeToWait , TimeUnit.MILLISECONDS);\n\t\t}\n\t\tif(statue.get() == ENTER_SELECT) {\n\t\t\tboolean flag = voteSet.add(nodePath); \t\t\n\t\t\treturn flag;\n\t\t}\n\t\t\n\t\treturn false;\t\t\n\t}\n\t\n\t//清除投票结果\n\tpublic boolean removePath(String nodePath) {\n\t\tLOGGER.debug(\"remove vote Information\" + nodePath);\n\t\t //删除投票结果.\t\t\n\t\tif(statue.get() == IS_CHANGING) {\n\t\t\tboolean flag =  voteSet.remove(nodePath);\n\t\t\tif(voteSet.isEmpty() && changingResultFutrue == null){\n\t\t\t\tstatue.set(NOT_SELECT);\n\t\t\t}\t\n\t\t\treturn flag;\n\t\t}\t\t\t\t\n\t\treturn false;\n\t}\n\t//如果是leader 节点 开始进行监听\n\tpublic void leaderlisten() {\n\t\ttry {\n\t\t\tif(manageVoteCache != null){\n\t\t\t\tmanageVoteCache.close();\n\t\t\t}\n\t\t\tmanageVoteCache = new PathChildrenCache(client, manageVotePath, true);\n\t\t\tfinal ManageHeartBeatChange manager = this;\n\t\t\t//监听投票结果, 然后决定需要选举哪一个为最终的结果。\n\t\t\tmanageVoteCache.getListenable().addListener(new PathChildrenCacheListener() {\t\t\t\n\t\t\t\t@Override\n\t\t\t\tpublic void childEvent(CuratorFramework client, PathChildrenCacheEvent event)  {\n\t\t\t\t\t// TODO Auto-generated method stub\n\t\t\t\t\tLOGGER.debug(\"event Type \" + event.getType());\n//\t\t\t\t\tLOGGER.debug( ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID) +\" is leader ? \" + mycatLeaderLatch.isLeaderShip());\n\t\t\t\t\tif(null != event.getData()) {\n\t\t\t\t\t\tType type = event.getType();\n\t\t\t\t\t\tswitch(type) {\n\t\t\t\t\t\t\tcase CHILD_ADDED : {\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase CHILD_UPDATED: {\n\t\t\t\t\t\t\t\tmanager.addPath(event.getData().getPath());\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase CHILD_REMOVED:{\n\t\t\t\t\t\t\t\tmanager.removePath(event.getData().getPath());\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tbreak;\t\t\t\t\t\t\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif(manager.hasCollectFinish()) {\n\t\t\t\t\t\t\tmanager.run();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\n\t\t\t\t}\n\t\t\t});\n\t\t\tmanageVoteCache.start();\n\t\t\tif(changingResultNode != null){\n\t\t\t\tchangingResultNode.close();\n\t\t\t}\n\t\t\t//监听切换结果,如果全部完成写入完成的时间\n\n\t\t\tchangingResultNode =  new NodeCache( client,  changingResultPath);\t\t\n\t\t\tchangingResultNode.start();\t\t\n\t\t\t\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage());\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\t//如果不是leader 节点 停止进行监听\n\tpublic void stop() {\t\t\n\t\ttry {\n\t\t\tmanageVoteCache.close();\n\t\t\tmanageVoteCache = null;\n\t\t\tchangingResultNode.close();\n\t\t\tchangingResultNode = null;\n\t\t\t//isLeader.compareAndSet(true, false);\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage());\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\t/*\n\t * 决定哪个节点为最终的投票结果\n\t * 所有的节点投票完成 或者 5分钟之内有投票的\n\t * */\n\t@Override\n\tpublic void run() {\n\t\tif(!statue.compareAndSet(ENTER_SELECT, IS_SELECT) || getNodeSize() == 0) {\n\t\t\treturn;\n\t\t}\n\t\tif(future != null) {\n\t\t\tfuture.cancel(false);\n\t\t\tfuture = null;\n\t\t}\n\t\t//获取最后一次的投票时间 如果\n\t\tList<ChildData> ChildDataList = manageVoteCache.getCurrentData();\n\t\tMap<Integer, Integer> countMap = new HashMap<>();\n\t\tInteger maxIndex = -1 ;\n\t\tInteger maxCount= -1 ;\n\t\ttry {\n\t\t    Collection<Participant> participants = mycatLeaderLatch.getParticipants();\n\t\t\tfor(ChildData childData : ChildDataList) {\n\t\t\t\tString data = new String(childData.getData());\n\t\t\t\tLOGGER.debug(childData.getPath()+ \"  \" + data);\n\t\t\t\tint index = data.indexOf(\"=\");\n\t\t\t\tInteger key =  Integer.valueOf(data.substring(index + 1));\n\t\t\t\tString myId = data.substring(0, index);\n\t\t\t\t//只对在线的节点进行统计 如果某个节点挂了 不再进行统计了\n\t\t\t\tboolean checkExist = false;\n\t\t\t\tfor(Participant participant : participants) {\n\t\t\t\t\tif(participant.getId().equals(myId)) {\n\t\t\t\t\t\tcheckExist = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(!checkExist){\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tInteger value = countMap.get(key);\n\t\t\t\t\n\t\t\t\tif(value == null) {\n\t\t\t\t\tvalue = new Integer(0);\n\t\t\t\t}\n\t\t\t\tvalue += 1;\n\t\t\t\tcountMap.put(key, value);\n\t\t\t\t//所有总数最大的为投票结果\n\t\t\t\tif(maxCount.compareTo(value) < 0) {\n\t\t\t\t\tmaxCount = value;\n\t\t\t\t\tmaxIndex = key;\n\t\t\t\t}\t\t\t\n\t\t\t}\n\t\t\t//节点切换\n\t\t\tstatue.set(IS_CHANGING);\n\t\t\n\t\t\tif(maxIndex != -1) { \n\t\t\t\tLOGGER.debug(\"投票结果：\" + dataHost + \" = \" + maxIndex);\n\t\t\t\t//向集群写入修改的结果\n\t\t\t\tZKUtils.createPath(changingResultPath, \"\");\n\t\t\t\tboolean result = MycatServer.getInstance().saveDataHostIndexToZk(dataHost, maxIndex);\n\t\t\t\tif(result) {\n\t\t\t\t\t//开启对结果切换的监控\n\t\t\t\t\tstartChangingResultListen();\n\t\t\t\t} else {\n\t\t\t\t\t //删除投票结果。\n\t\t\t\t\ttry {\t\t\t\t\n\t\t\t\t\t\tfor(ChildData childData : ChildDataList)  {\n\t\t\t\t\t\t\tclient.delete().deletingChildrenIfNeeded().forPath(childData.getPath());\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOGGER.error(e.getMessage());\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} else {\n\t\t\t\tLOGGER.debug(\"投票错误：\" + dataHost + \" = \" + maxIndex);\t\t\t\t\n\t\t\t}\n\n\t\t\t\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage());\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\t//对状态结果切换的主动监控\n\tprivate void startChangingResultListen() {\n\t\t //主动监控切换节点的状态\n\t\t changingResultFutrue = service.scheduleAtFixedRate(new Runnable() {\t\t\t\t\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tChildData currentData = changingResultNode.getCurrentData();\n\t\t\t\tif(null != currentData) {\n\t\t\t\t\ttry{\n\t\t\t\t\t\tbyte[] data = changingResultNode.getCurrentData().getData();\n\t\t\t\t\t\tProperties properties = new Properties();\n\t\t\t\t\t\tproperties.load(new ByteArrayInputStream(data));\n\t\t\t\t\t\tint count = 0;\n\t\t\t\t\t\tCollection<Participant> participants = mycatLeaderLatch.getParticipants();\n\t\t\t\t\t\tfor(Participant participant : participants) {\n\t\t\t\t\t\t\tString key = participant.getId() + \"_endTime\";\n\t\t\t\t\t\t\tString value = properties.getProperty(key);\n\t\t\t\t\t\t\tif(!StringUtil.isEmpty(value)) {\n\t\t\t\t\t\t\t\tcount ++;\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t}else {\n\t\t\t\t\t\t\t\tLOGGER.debug(String.format(\"%s 还未结束切换\", participant.getId()));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tString changingFinishKey = dataHost + \"_changing_finish_time\";\n\t\t\t\t\t\tString value = properties.getProperty(changingFinishKey);\n\t\t\t\t\t\tif(!StringUtil.isEmpty(value)) {\n\t\t\t\t\t\t\tchangingFinishDate = Long.valueOf(value);\n\t\t\t\t\t\t}\t\t\t\t\t\t\n\t\t\t\t\t\tint onLineNode = participants.size(); //在线的节点\t\t\t\t\t\t\n\t\t\t\t\t\tif(count == onLineNode ) {\t\t\t\t\t\t\t //\n\t\t\t\t\t\t\t LOGGER.debug(\"所有节点切换完成 ,当前时间\" + TimeUtil.currentTimeMillis());\n\t\t\t\t\t\t\t Map<String,String> propertyMap = new HashMap<>();\n\t\t\t\t\t\t\t propertyMap.put(changingFinishKey, TimeUtil.currentTimeMillis()+\"\");\n\t\t\t\t\t\t\t try{\n\t\t\t\t\t\t\t\t changingStatueLock.acquire(30, TimeUnit.SECONDS);\n\t\t\t\t\t\t\t\t ZKUtils.writeProperty( changingResultPath, propertyMap);\n\t\t\t\t\t\t\t\t if(changingResultFutrue !=null) {\n\t\t\t\t\t\t\t\t\t changingResultFutrue.cancel(false);\n\t\t\t\t\t\t\t\t\t changingResultFutrue = null;\n\t\t\t\t\t\t\t\t }\n\t\t\t\t\t\t\t\t \n\t\t\t\t\t\t\t\t //删除投票结果。\n\t\t\t\t\t\t\t\tList<ChildData> ChildDataList = manageVoteCache.getCurrentData();\n\t\t\t\t\t\t\t\tfor(ChildData childData : ChildDataList)  {\n\t\t\t\t\t\t\t\t\tclient.delete().deletingChildrenIfNeeded().forPath(childData.getPath());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t if(voteSet.isEmpty() ){\n\t\t\t\t\t\t\t\t\t //删除投票结果。\n\t\t\t\t\t\t\t\t\tstatue.set(NOT_SELECT);\n\t\t\t\t\t\t\t\t }\t\n\t\t\t\t\t\t\t\t \n\t\t\t\t\t\t  \t }finally {\n\t\t\t\t\t\t  \t\t changingStatueLock.release();\n\t\t\t\t\t\t  \t  }\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOGGER.error(e.getMessage());\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tLOGGER.debug(\"集群切换结果的状态文件夹 已经被删除！！！\");\n\t\t\t\t}\t\t\t\t\t\n\t\t\t\t\n\t\t\t}\n\t\t}, 100, 1000, TimeUnit.MILLISECONDS);\n\t}\n\n\tpublic int getNodeSize(){\n\t\treturn voteSet.size();\n\t}\n\n\t//所有节点收集完毕.\n\tpublic boolean hasCollectFinish() {\n\t\treturn voteSet.size() == mycatLeaderLatch.getParticipantsCount();\n\t}\t\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/heartbeat/zkprocess/MycatLeaderLatch.java",
    "content": "package io.mycat.backend.heartbeat.zkprocess;\n\n\n/**\n * 重定义的leaderLatch 因为curator的选举存在着丢包的情况.\n* 源文件名：MyLeaderLatch.java\n* 文件版本：1.0.0\n* 创建作者：zwy\n* 创建日期：2018年5月20日\n*/\n\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.recipes.leader.LeaderLatch;\nimport org.apache.curator.framework.recipes.leader.Participant;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.util.ZKUtils;\nimport io.netty.util.internal.ConcurrentSet;\n\npublic class MycatLeaderLatch {\n\tpublic static final Logger LOGGER = LoggerFactory.getLogger(MycatLeaderLatch.class);\n\n//\tRunnable isLeaderRunnable = null; //当选leader之后的回调方法\n//\tRunnable notLeaderRunnable = null; //失去leader之后的回调方法.\n\tprivate final String latchPath;\n\tvolatile LeaderLatch latch;\n\tString myId;\n\tCuratorFramework client;\n\tint isLeaderCount = 0;\n\tint isSlaveCount = 0;\n\tvolatile boolean isLeader;\n\tfinal ScheduledExecutorService service = Executors.newScheduledThreadPool(1);\n\n\tConcurrentSet<ManageHeartBeatChange> manageHeartBeatChangeSet = new ConcurrentSet<>();\n\tpublic MycatLeaderLatch( String latchPath )  {\t\t\n\t\tthis.myId = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID);\n\t\tthis.latchPath = ZKUtils.getZKBasePath() + latchPath;\n\t\tthis.client  = ZKUtils.getConnection();\n\t\tisLeader = false;\n\t\t//ZKUtils.createPath(this.latchPath, \"\");\n\t\tlatch = new LeaderLatch(client, this.latchPath ,this.myId);\t\n\t\t\n\t\tMap<String, PhysicalDBPool> dataSourceHosts = MycatServer.getInstance().getConfig().getDataHosts();\n\t\ttry {\n\t\t\tfor(String dataSource : dataSourceHosts.keySet()) {\n\t\t\t\tmanageHeartBeatChangeSet.add(new ManageHeartBeatChange(this, dataSource));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.warn(\"init ManageHeartBeatChange err:\", e);\n\t\t}\n\t\t\n\t}\n\t//成为leader的回调方法\n\tprivate void isLeaderRunnable() {\n\t\t//\n\t\tLOGGER.debug(ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID)  + \" success to leader\");\n\t\tfor(ManageHeartBeatChange manageHeartBeatChange: manageHeartBeatChangeSet) {\n\t\t\tmanageHeartBeatChange.leaderlisten();\n\t\t}\n\t\t\n\t}\n\t//不再是leader的回调方法\n\tprivate void notLeaderRunnable() {\n\t\tLOGGER.debug(ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID)  +  \" fail to leader, now is slave\");\n\t\tfor(ManageHeartBeatChange manageHeartBeatChange: manageHeartBeatChangeSet) {\n\t\t\tmanageHeartBeatChange.stop();\n\t\t}\n\t}\n\tpublic void start() throws Exception {\n\t\tlatch.start();\n\t\t\n\t\tservice.scheduleAtFixedRate(checkIsLeader(), 0, 5, TimeUnit.SECONDS);\n\t}\n\tpublic void stop() throws IOException {\n\t\tlatch.close();\n\t\tservice.shutdown();\n\t}\n \tprivate Runnable checkIsLeader() {\n\t\treturn new Runnable() {\t\t\t\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tboolean isExist = false;\n\t\t\t\ttry {\n\t\t\t\t\tCollection<Participant> participants = latch.getParticipants();\n\t\t\t\t\tfor (Participant participant : participants) {\n\t\t\t\t        if (myId.equals(participant.getId())) {\n\t\t\t\t            isExist = true;\n\t\t\t\t            break;\n\t\t\t\t        }\n\t\t\t\t    }\n\t\t\t\t\tif(!isExist) {\n\t\t\t\t\t\t//输出已经不再集群中了哦 \n\t\t\t\t\t\tLOGGER.info(myId + \" current does not exist on zk\");\t\n\t\t\t\n\t\t\t\t\t\tlatch.close();\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\tlatch = new LeaderLatch(client, latchPath ,myId);\n\t\t\t\t\t\tlatch.start();\n\t\t\t\t\t\tLOGGER.info(myId + \" success reset leaderLatch @ \" + latchPath);\n\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t//查看当前leader是否是自己\n\t\t\t\t    //注意，不能用leaderLatch.hasLeadership()因为有zk数据丢失的不确定性\n\t\t\t\t    //利用serverId对比确认是否主为自己\n\t\t\t\t    Participant leader = latch.getLeader();\n\t\t\t\t    boolean hashLeaderShip = myId.equals(leader.getId());\t\t\t\t    \n\t\t\t\t    judgeIsLeader(hashLeaderShip);\t\t\t\t    \n\n\t\t\t\t} catch (Exception e) {\t\t\t\t\t\n\t\t\t\t    judgeIsLeader(false);    \t\t\t\t\t\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\t\t\t\n\t\t\t\t\n\t\t\t}\n\t\t};\n\t}\n\t//缓冲区 判断是否是leader\n\tpublic void judgeIsLeader(boolean hashLeaderShip) {\n\t\t//主从切换缓冲\n\t    if(hashLeaderShip) {\n\t        isLeaderCount++;\n\t        isSlaveCount = 0;\n\t    } else {\n\t        isLeaderCount = 0;\n\t        isSlaveCount ++;\n\t    }\n\n\t\tif (isLeaderCount > 3 && !isLeader) {\n\t        LOGGER.info(myId + \" Currently run as leader\");\n\t\t\tisLeader = true;\n\t\t\t//执行换为leader的方法\n\t\t\t//isLeaderRunnable.run();\n\t\t\tisLeaderRunnable();\n\t\t}\n\n\t    if (isSlaveCount > 3 && isLeader) {\n\t        LOGGER.info(myId + \" Currently run as slave\");\n\t    \tisLeader = false;\n\t    \tnotLeaderRunnable();\n\t    }\n\t}\n\t\n\t//是否leader节点\n\tpublic boolean isLeaderShip(){\n\t\treturn isSlaveCount == 0 && isLeader;\n\t}\n\t\n\tpublic Collection<Participant> getParticipants() throws Exception {\n\t\treturn latch.getParticipants();\n\t}\n\t\n\tpublic int getParticipantsCount() {\t\t\n\t\ttry {\n\t\t\treturn latch.getParticipants().size();\n\t\t} catch (Exception e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t\tLOGGER.error(\"get clusters number error\");\n\t\t\treturn 0;\n\t\t} \n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/heartbeat/zkprocess/SwitchStatueToZK.java",
    "content": "package io.mycat.backend.heartbeat.zkprocess;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.curator.framework.recipes.locks.InterProcessMutex;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.util.ZKUtils;\n\npublic class SwitchStatueToZK{\n\tpublic static final Logger LOGGER = LoggerFactory.getLogger(SwitchStatueToZK.class);\n\n\tprivate static  InterProcessMutex changingStatueLock;\n\tstatic {\n\t\tchangingStatueLock =  new InterProcessMutex(ZKUtils.getConnection(),\n\t\t\t\tZKUtils.getZKBasePath() +\"heartbeat/changingStatueLock\");\n\t}\n\tpublic static boolean startSwitch(String dataHost) {\n\t\t\n\t\tString path = ZKUtils.getZKBasePath() +\"heartbeat/\" + dataHost +\"/\";\n\t\tString changingResultPath = path + \"changingStatue\";\n        Map<String, String> propertyMap = new HashMap<>();\n        String myId = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID);\n        propertyMap.put(myId+\"_changing_statue\",\"switching now\"); //状态\n        propertyMap.put(myId + \"_startTime\",new Date().toString()); //切换开始时间\n        propertyMap.put(myId + \"_endTime\", \"\"); //结束时间\n        try{\n        \ttry {\n        \t\tchangingStatueLock.acquire(30000, TimeUnit.MILLISECONDS);\n                ZKUtils.writeProperty(changingResultPath, propertyMap);\n\t\t\t} finally {\n\t\t\t\tchangingStatueLock.release();\n\t\t\t}     \n        \treturn true;\n        }catch (Exception e) {\n        \tLOGGER.error(dataHost + \" startSwitch err \"   , e);\n\t\t}\n        return false;\n\t}\n\tpublic static boolean endSwitch(String dataHost) {\t\t\n\t\tString path = ZKUtils.getZKBasePath() +\"heartbeat/\" + dataHost +\"/\";\n\t\tString changingResultPath = path + \"changingStatue\";\n        Map<String, String> propertyMap = new HashMap<>();\n        String myId = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID);\n        propertyMap.put(myId+\"_changing_statue\",\"switching success\"); //状态\n        propertyMap.put(myId + \"_endTime\",new Date().toString()); //切换结束时间\n        try{\n        \ttry {\n        \t\tchangingStatueLock.acquire(30000, TimeUnit.MILLISECONDS);\n                ZKUtils.writeProperty(changingResultPath, propertyMap);\n\t\t\t} finally {\n\t\t\t\tchangingStatueLock.release();\n\t\t\t}  \n            return true;\n        }catch (Exception e) {\n        \tLOGGER.error(dataHost + \" endSwitch err \"   , e);\n            return false;\n        }\n        \n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/JDBCConnection.java",
    "content": "package io.mycat.backend.jdbc;\n\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.math.BigDecimal;\nimport java.nio.ByteBuffer;\nimport java.sql.CallableStatement;\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.backend.mysql.nio.handler.ConnectionHeartBeatHandler;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.Isolations;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.Procedure;\nimport io.mycat.route.ProcedureParameter;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.util.MysqlDefs;\nimport io.mycat.util.ObjectUtil;\nimport io.mycat.util.ResultSetUtil;\nimport io.mycat.util.StringUtil;\nimport io.mycat.util.TimeUtil;\n\npublic class JDBCConnection implements BackendConnection {\n\tprotected static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(JDBCConnection.class);\n\tprivate JDBCDatasource pool;\n\tprivate volatile String schema;\n\tprivate volatile String dbType;\n\tprivate volatile String oldSchema;\n\tprivate byte packetId;\n\tprivate int txIsolation;\n\tprivate volatile boolean running = false;\n\tprivate volatile boolean borrowed;\n\tprivate long id = 0;\n\tprivate String host;\n\tprivate int port;\n\tprivate Connection con;\n\tprivate ResponseHandler respHandler;\n\tprivate volatile Object attachement;\n\n\tboolean headerOutputed = false;\n\tprivate volatile boolean modifiedSQLExecuted;\n\tprivate final long startTime;\n\tprivate long lastTime;\n\tprivate boolean isSpark = false;\n\n\tprivate NIOProcessor processor;\n\tprivate boolean setSchemaFail = false;\n\n\tprivate volatile int sqlSelectLimit = -1;\n\tprivate boolean fromSlaveDB;\n\t\n\t\n\t\n\tpublic NIOProcessor getProcessor() {\n        return processor;\n    }\n\n    public void setProcessor(NIOProcessor processor) {\n        this.processor = processor;\n    }\n\n    public JDBCConnection() {\n\t\tstartTime = System.currentTimeMillis();\n\t}\n\n\tpublic Connection getCon() {\n\t\treturn con;\n\t}\n\n\tpublic void setCon(Connection con) {\n\t\tthis.con = con;\n\n\t}\n\n\t@Override\n\tpublic void close(String reason) {\n\t\ttry {\n\t\t\ttry {\n\t\t\t\tif (!isAutocommit()) {\n\t\t\t\t\trollback();\n\t\t\t\t\tcon.setAutoCommit(true);\n\t\t\t\t}\n\t\t\t}catch (Exception e){\n\t\t\t\tLOGGER.error(\"close jdbc connection, found it is in transcation so try to rollback\");\n\t\t\t}\n\t\t\tcon.close();\n\t\t\tif(processor!=null){\n\t\t\t    processor.removeConnection(this);\n\t\t\t}\n\t\t\t\n\t\t} catch (SQLException e) {\n\t\t}\n\n\t}\n\n    @Override\n    public void closeWithoutRsp(String reason) {\n        // TODO Auto-generated method stub\n        close(reason);\n    }\n\n\tpublic void setId(long id) {\n        this.id = id;\n    }\n\t\n\tpublic JDBCDatasource getPool() {\n        return pool;\n    }\n\n    public void setPool(JDBCDatasource pool) {\n\t\tthis.pool = pool;\n\t}\n\n\tpublic void setHost(String host) {\n\t\tthis.host = host;\n\t}\n\n\tpublic void setPort(int port) {\n\t\tthis.port = port;\n\t}\n\n\t@Override\n\tpublic boolean isClosed() {\n\t\ttry {\n\t\t\treturn con == null || con.isClosed();\n\t\t} catch (SQLException e) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void idleCheck() {\n\t    if(TimeUtil.currentTimeMillis() > lastTime + pool.getConfig().getIdleTimeout()){\n\t        close(\" idle  check\");\n\t    }\n\t}\n\n\t@Override\n\tpublic long getStartupTime() {\n\t\treturn startTime;\n\t}\n\n\t@Override\n\tpublic String getHost() {\n\t\treturn this.host;\n\t}\n\n\t@Override\n\tpublic int getPort() {\n\t\treturn this.port;\n\t}\n\n\t@Override\n\tpublic int getLocalPort() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic long getNetInBytes() {\n\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic long getNetOutBytes() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean isModifiedSQLExecuted() {\n\t\treturn modifiedSQLExecuted;\n\t}\n\n\t@Override\n\tpublic boolean isFromSlaveDB() {\n\t\treturn fromSlaveDB;\n\t}\n\n\tpublic String getDbType() {\n\t\treturn this.dbType;\n\t}\n\n\tpublic void setDbType(String newDbType) {\n\t\tthis.dbType = newDbType.toUpperCase();\n\t\tthis.isSpark = dbType.equals(\"SPARK\");\n\n\t}\n\n\t@Override\n\tpublic String getSchema() {\n\t\treturn this.schema;\n\t}\n\n\t@Override\n\tpublic void setSchema(String newSchema) {\n\t\tthis.oldSchema = this.schema;\n\t\tthis.schema = newSchema;\n\n\t}\n\n\t@Override\n\tpublic long getLastTime() {\n\n\t\treturn lastTime;\n\t}\n\n\t@Override\n\tpublic boolean isClosedOrQuit() {\n\t\treturn this.isClosed();\n\t}\n\n\t@Override\n\tpublic void setAttachment(Object attachment) {\n\t\tthis.attachement = attachment;\n\n\t}\n\n\t@Override\n\tpublic void quit() {\n\t\tthis.close(\"client quit\");\n\n\t}\n\n\t@Override\n\tpublic void setLastTime(long currentTimeMillis) {\n\t\tthis.lastTime = currentTimeMillis;\n\n\t}\n\n\t@Override\n\tpublic void release() {\n\t\tmodifiedSQLExecuted = false;\n\t\tsetResponseHandler(null);\n\t\tpool.releaseChannel(this);\n\t}\n\n\tpublic void setRunning(boolean running) {\n\t\tthis.running = running;\n\n\t}\n\n\t@Override\n\tpublic boolean setResponseHandler(ResponseHandler commandHandler) {\n\t\trespHandler = commandHandler;\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void commit() {\n\t\ttry {\n\t\t\tif (con.getAutoCommit()) {\n\t\t\t\tLOGGER.warn(\"when jdbc con is autocommit call commit\");\n\t\t\t} else {\n\t\t\t\tcon.commit();\n\t\t\t}\n\t\t\tthis.respHandler.okResponse(OkPacket.OK, this);\n\t\t} catch (SQLException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n    private  int convertNativeIsolationToJDBC(int nativeIsolation)\n    {\n        if(nativeIsolation== Isolations.REPEATED_READ)\n        {\n            return Connection.TRANSACTION_REPEATABLE_READ;\n        }else\n        if(nativeIsolation== Isolations.SERIALIZABLE)\n        {\n            return Connection.TRANSACTION_SERIALIZABLE;\n        } else\n        {\n            return nativeIsolation;\n        }\n    }\n\n\n\tprivate void syncTxReadonly(boolean txReadonly)\n\t{\n\t\tif(isTxReadonly() == txReadonly) {\n\t\t\treturn;\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tcon.setReadOnly(false);\n\t\t} catch (SQLException e)\n\t\t{\n\t\t\tLOGGER.warn(\"set setReadOnly error:\",e);\n\t\t}\n\t}\n    private void syncIsolation(int nativeIsolation)\n    {\n        int jdbcIsolation=convertNativeIsolationToJDBC(nativeIsolation);\n        int srcJdbcIsolation=   getTxIsolation();\n\t\tif (jdbcIsolation == srcJdbcIsolation || \"oracle\".equalsIgnoreCase(getDbType())\n\t\t\t\t&& jdbcIsolation != Connection.TRANSACTION_READ_COMMITTED\n\t\t\t\t&& jdbcIsolation != Connection.TRANSACTION_SERIALIZABLE) {\n\t\t\treturn;\n\t\t}\n\t\ttry\n        {\n            con.setTransactionIsolation(jdbcIsolation);\n        } catch (SQLException e)\n        {\n            LOGGER.warn(\"set txisolation error:\",e);\n        }\n    }\n\tprivate void executeSQL(RouteResultsetNode rrn, ServerConnection sc,\n\t\t\t\t\t\t\tboolean autocommit) throws IOException {\n\t\tString orgin = rrn.getStatement();\n\t\t// String sql = rrn.getStatement().toLowerCase();\n\t\t// LOGGER.info(\"JDBC SQL:\"+orgin+\"|\"+sc.toString());\n\t\tif (!modifiedSQLExecuted && rrn.isModifySQL()) {\n\t\t\tmodifiedSQLExecuted = true;\n\t\t}\n\n\t\ttry {\n            syncIsolation(sc.getTxIsolation()) ;\n\t\t\tsyncTxReadonly(sc.isTxReadonly());\n\t\t\tif (!this.schema.equals(this.oldSchema)) {\n\t\t\t\tcon.setCatalog(schema);\n\t\t\t\tif (!setSchemaFail) {\n                    try {\n                        con.setSchema(schema); //add@byron to test\n                    } catch (Throwable e) {\n                        LOGGER.error(\"JDBC setSchema Exception for \" + schema, e);\n                        setSchemaFail = true;\n                    }\n                }\n\t\t\t\tthis.oldSchema = schema;\n\t\t\t}\n\t\t\tif (!this.isSpark) {\n\t\t\t\tcon.setAutoCommit(autocommit);\n\t\t\t}\n\t\t\tint sqlType = rrn.getSqlType();\n             if(rrn.isCallStatement()&&\"oracle\".equalsIgnoreCase(getDbType()))\n             {\n                 //存储过程暂时只支持oracle\n                 ouputCallStatement(rrn,sc,orgin);\n             }  else\n\t\t\tif (sqlType == ServerParse.SELECT || sqlType == ServerParse.SHOW) {\n\t\t\t\tif ((sqlType == ServerParse.SHOW) && (!dbType.equals(\"MYSQL\"))) {\n\t\t\t\t\t// showCMD(sc, orgin);\n\t\t\t\t\t//ShowVariables.execute(sc, orgin);\n\t\t\t\t\tShowVariables.execute(sc, orgin,this);\n\t\t\t\t} else if (\"SELECT CONNECTION_ID()\".equalsIgnoreCase(orgin)) {\n\t\t\t\t\t//ShowVariables.justReturnValue(sc,String.valueOf(sc.getId()));\n\t\t\t\t\tShowVariables.justReturnValue(sc,String.valueOf(sc.getId()),this);\n\t\t\t\t} else {\n\t\t\t\t\touputResultSet(sc, orgin);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\texecuteddl(sc, orgin);\n\t\t\t}\n\n\t\t} catch (SQLException e) {\n\n\t\t\tString msg = e.getMessage();\n\t\t\tErrorPacket error = new ErrorPacket();\n\t\t\terror.packetId = ++packetId;\n\t\t\terror.errno = e.getErrorCode();\n\t\t\terror.message = msg.getBytes();\n\t\t\tLOGGER.error(\"sql execute error, \"+ msg , e);\n\t\t\tthis.respHandler.errorResponse(error.writeToBytes(sc), this);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tString msg = e.getMessage();\n\t\t\tErrorPacket error = new ErrorPacket();\n\t\t\terror.packetId = ++packetId;\n\t\t\terror.errno = ErrorCode.ER_UNKNOWN_ERROR;\n\t\t\terror.message = ((msg == null) ? e.toString().getBytes() : msg.getBytes());\n\t\t\tString err = null;\n\t\t\tif(error.message!=null){\n\t\t\t    err = new String(error.message);\n\t\t\t}\n\t\t\tLOGGER.error(\"sql execute error, \"+ err , e);\n\t\t\tthis.respHandler.errorResponse(error.writeToBytes(sc), this);\n\t\t}\n\t\tfinally {\n\t\t\tthis.running = false;\n\t\t}\n\n\t}\n\n\tprivate FieldPacket getNewFieldPacket(String charset, String fieldName) {\n\t\tFieldPacket fieldPacket = new FieldPacket();\n\t\tfieldPacket.orgName = StringUtil.encode(fieldName, charset);\n\t\tfieldPacket.name = StringUtil.encode(fieldName, charset);\n\t\tfieldPacket.length = 20;\n\t\tfieldPacket.flags = 0;\n\t\tfieldPacket.decimals = 0;\n\t\tint javaType = 12;\n\t\tfieldPacket.type = (byte) (MysqlDefs.javaTypeMysql(javaType) & 0xff);\n\t\treturn fieldPacket;\n\t}\n\n\tprivate void executeddl(ServerConnection sc, String sql)\n\t\t\tthrows SQLException {\n\t\tStatement stmt = null;\n\t\ttry {\n\t\t\tstmt = con.createStatement();\n\t\t\tint count = stmt.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS);\n\t\t\tlong lastInsertId = 0;\n\t\t\tif(\"mysql\".equalsIgnoreCase(getDbType())) {\n\t\t\t\tResultSet generatedKeys = stmt.getGeneratedKeys();\n\t\t\t\tif (generatedKeys != null){\n\t\t\t\t\tResultSetMetaData metaData = generatedKeys.getMetaData();\n\t\t\t\t\tif (metaData.getColumnCount() == 1){\n\t\t\t\t\t\tlastInsertId = (generatedKeys.next() ? generatedKeys.getLong(1) : 0L);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tOkPacket okPck = new OkPacket();\n\t\t\tokPck.affectedRows = count;\n\t\t\tokPck.insertId = lastInsertId;\n\t\t\tokPck.packetId = ++packetId;\n\t\t\tokPck.message = \" OK!\".getBytes();\n\t\t\tthis.respHandler.okResponse(okPck.writeToBytes(sc), this);\n\t\t}catch (Exception e){\n\t\t\tLOGGER.error(\"\",e);\n\t\t\tthrow e;\n\t\t}finally {\n\t\t\tif (stmt != null) {\n\t\t\t\ttry {\n\t\t\t\t\tstmt.close();\n\t\t\t\t} catch (SQLException e) {\n\t\t\t\t\tLOGGER.error(\"\",e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n    private static int oracleCURSORTypeValue=-10;\n    static\n    {\n        Object cursor = ObjectUtil.getStaticFieldValue(\"oracle.jdbc.OracleTypes\", \"CURSOR\");\n        if(cursor!=null) {\n\t\t\toracleCURSORTypeValue = (int) cursor;\n\t\t}\n    }\n\tprivate void ouputCallStatement(RouteResultsetNode rrn,ServerConnection sc, String sql)\n\t\t\tthrows SQLException {\n\n        CallableStatement stmt = null;\n        ResultSet rs = null;\n\t\ttry {\n            Procedure procedure = rrn.getProcedure();\n            Collection<ProcedureParameter> paramters=    procedure.getParamterMap().values();\n            String callSql = procedure.toPreCallSql(null);\n            stmt = con.prepareCall(callSql);\n\t\t\tif (sc.getSqlSelectLimit() > 0) {\n\t\t\t\tstmt.setMaxRows(sc.getSqlSelectLimit());\n\t\t\t}\n            for (ProcedureParameter paramter : paramters)\n            {\n                if((ProcedureParameter.IN.equalsIgnoreCase(paramter.getParameterType())\n                        ||ProcedureParameter.INOUT.equalsIgnoreCase(paramter.getParameterType())))\n                {\n                  Object value=  paramter.getValue()!=null ?paramter.getValue():paramter.getName();\n                    stmt.setObject(paramter.getIndex(),value);\n                }\n\n                if(ProcedureParameter.OUT.equalsIgnoreCase(paramter.getParameterType())\n                        ||ProcedureParameter.INOUT.equalsIgnoreCase(paramter.getParameterType())  )\n                {\n                    int jdbcType =\"oracle\".equalsIgnoreCase(getDbType())&& procedure.getListFields().contains(paramter.getName())?oracleCURSORTypeValue: paramter.getJdbcType();\n                    stmt.registerOutParameter(paramter.getIndex(), jdbcType);\n                }\n            }\n\n            boolean hadResults= stmt.execute();\n\n            ByteBuffer byteBuf = sc.allocate();\n            if(procedure.getSelectColumns().size()>0&&!procedure.isResultList())\n            {\n                List<FieldPacket> fieldPks = new LinkedList<FieldPacket>();\n                for (ProcedureParameter paramter : paramters)\n                {\n                    if (!procedure.getListFields().contains(paramter.getName())&&(ProcedureParameter.OUT.equalsIgnoreCase(paramter.getParameterType())\n                            || ProcedureParameter.INOUT.equalsIgnoreCase(paramter.getParameterType()))   )\n                    {\n                        FieldPacket packet = PacketUtil.getField(paramter.getName(), MysqlDefs.javaTypeMysql(paramter.getJdbcType()));\n                        fieldPks.add(packet);\n                    }\n                }\n                int colunmCount = fieldPks.size();\n\n                ResultSetHeaderPacket headerPkg = new ResultSetHeaderPacket();\n                headerPkg.fieldCount = fieldPks.size();\n                headerPkg.packetId = ++packetId;\n\n                byteBuf = headerPkg.write(byteBuf, sc, true);\n                byteBuf.flip();\n                byte[] header = new byte[byteBuf.limit()];\n                byteBuf.get(header);\n                byteBuf.clear();\n\n\n                List<byte[]> fields = new ArrayList<byte[]>(fieldPks.size());\n                Iterator<FieldPacket> itor = fieldPks.iterator();\n                while (itor.hasNext()) {\n                    FieldPacket curField = itor.next();\n                    curField.packetId = ++packetId;\n                    byteBuf = curField.write(byteBuf, sc, false);\n                    byteBuf.flip();\n                    byte[] field = new byte[byteBuf.limit()];\n                    byteBuf.get(field);\n                    byteBuf.clear();\n                    fields.add(field);\n                    itor.remove();\n                }\n                EOFPacket eofPckg = new EOFPacket();\n                eofPckg.packetId = ++packetId;\n                byteBuf = eofPckg.write(byteBuf, sc, false);\n                byteBuf.flip();\n                byte[] eof = new byte[byteBuf.limit()];\n                byteBuf.get(eof);\n                byteBuf.clear();\n                this.respHandler.fieldEofResponse(header, fields, eof, this);\n                RowDataPacket curRow = new RowDataPacket(colunmCount);\n                for (String name : procedure.getSelectColumns())\n                {\n                    ProcedureParameter procedureParameter=   procedure.getParamterMap().get(name);\n\t\t\t\t\tObject object = stmt.getObject(procedureParameter.getIndex());\n\t\t\t\t\tif (object != null){\n\t\t\t\t\t\tcurRow.add(StringUtil.encode(String.valueOf(object),\n\t\t\t\t\t\t\t\tsc.getCharset()));\n\t\t\t\t\t}else {\n\t\t\t\t\t\tcurRow.add(null);\n\t\t\t\t\t}\n                }\n\n                curRow.packetId = ++packetId;\n                byteBuf = curRow.write(byteBuf, sc, false);\n                byteBuf.flip();\n                byte[] row = new byte[byteBuf.limit()];\n                byteBuf.get(row);\n                byteBuf.clear();\n                this.respHandler.rowResponse(row, this);\n\n                eofPckg = new EOFPacket();\n                eofPckg.packetId = ++packetId;\n                if(procedure.isResultList())\n                {\n                    eofPckg.status = 42;\n                }\n                byteBuf = eofPckg.write(byteBuf, sc, false);\n                byteBuf.flip();\n                eof = new byte[byteBuf.limit()];\n                byteBuf.get(eof);\n                byteBuf.clear();\n                this.respHandler.rowEofResponse(eof, this);\n            }\n\n\n            if(procedure.isResultList())\n            {\n                List<FieldPacket> fieldPks = new LinkedList<FieldPacket>();\n                int listSize=procedure.getListFields().size();\n                for (ProcedureParameter paramter : paramters)\n                {\n                    if (procedure.getListFields().contains(paramter.getName())&&(ProcedureParameter.OUT.equalsIgnoreCase(paramter.getParameterType())\n                            || ProcedureParameter.INOUT.equalsIgnoreCase(paramter.getParameterType()))  )\n                    {\n                        listSize--;\n\n                        Object object = stmt.getObject(paramter.getIndex());\n                        rs= (ResultSet) object;\n                        if(rs==null) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n                        ResultSetUtil.resultSetToFieldPacket(sc.getCharset(), fieldPks, rs,\n                                this.isSpark);\n\n                        int colunmCount = fieldPks.size();\n                        ResultSetHeaderPacket headerPkg = new ResultSetHeaderPacket();\n                        headerPkg.fieldCount = fieldPks.size();\n                        headerPkg.packetId = ++packetId;\n\n                        byteBuf = headerPkg.write(byteBuf, sc, true);\n                        byteBuf.flip();\n                        byte[] header = new byte[byteBuf.limit()];\n                        byteBuf.get(header);\n                        byteBuf.clear();\n\n\n                        List<byte[]> fields = new ArrayList<byte[]>(fieldPks.size());\n                        Iterator<FieldPacket> itor = fieldPks.iterator();\n                        while (itor.hasNext()) {\n                            FieldPacket curField = itor.next();\n                            curField.packetId = ++packetId;\n                            byteBuf = curField.write(byteBuf, sc, false);\n                            byteBuf.flip();\n                            byte[] field = new byte[byteBuf.limit()];\n                            byteBuf.get(field);\n                            byteBuf.clear();\n                            fields.add(field);\n                            itor.remove();\n                        }\n                        EOFPacket eofPckg = new EOFPacket();\n                        eofPckg.packetId = ++packetId;\n                        byteBuf = eofPckg.write(byteBuf, sc, false);\n                        byteBuf.flip();\n                        byte[] eof = new byte[byteBuf.limit()];\n                        byteBuf.get(eof);\n                        byteBuf.clear();\n                        this.respHandler.fieldEofResponse(header, fields, eof, this);\n\n                        // output row\n                        while (rs.next()) {\n                            RowDataPacket curRow = new RowDataPacket(colunmCount);\n                            for (int i = 0; i < colunmCount; i++) {\n                                int j = i + 1;\n\t\t\t\t\t\t\t\tObject object1 = rs.getObject(j);\n\t\t\t\t\t\t\t\tif (object1 == null){\n\t\t\t\t\t\t\t\t\tcurRow.add(null);\n\t\t\t\t\t\t\t\t}else {\n\t\t\t\t\t\t\t\t\tcurRow.add(StringUtil.encode(Objects.toString(object1),\n\t\t\t\t\t\t\t\t\t\t\tsc.getCharset()));\n\t\t\t\t\t\t\t\t}\n                            }\n                            curRow.packetId = ++packetId;\n                            byteBuf = curRow.write(byteBuf, sc, false);\n                            byteBuf.flip();\n                            byte[] row = new byte[byteBuf.limit()];\n                            byteBuf.get(row);\n                            byteBuf.clear();\n                            this.respHandler.rowResponse(row, this);\n                        }\n                        eofPckg = new EOFPacket();\n                        eofPckg.packetId = ++packetId;\n                        if(listSize!=0)\n                        {\n                            eofPckg.status = 42;\n                        }\n                        byteBuf = eofPckg.write(byteBuf, sc, false);\n                        byteBuf.flip();\n                        eof = new byte[byteBuf.limit()];\n                        byteBuf.get(eof);\n                        byteBuf.clear();\n                        this.respHandler.rowEofResponse(eof, this);\n                    }\n                }\n\n            }\n\n\n\n            if(!procedure.isResultSimpleValue())\n            {\n                byte[] OK = new byte[] { 7, 0, 0, 1, 0, 0, 0, 2, 0, 0,\n                        0 };\n                OK[3]=++packetId;\n                this.respHandler.okResponse(OK,this);\n            }\n            sc.recycle(byteBuf);\n\t\t} finally {\n            if (rs != null) {\n                try {\n                    rs.close();\n                } catch (SQLException e) {\n\n                }\n            }\n\t\t\tif (stmt != null) {\n\t\t\t\ttry {\n\t\t\t\t\tstmt.close();\n\t\t\t\t} catch (SQLException e) {\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n    private void ouputResultSet(ServerConnection sc, String sql)\n            throws SQLException {\n        ResultSet rs = null;\n        Statement stmt = null;\n\n\t\ttry {\n\t\t\tstmt = con.createStatement();\n\t\t\tif (sc.getSqlSelectLimit() > 0) {\n\t\t\t\tstmt.setMaxRows(sc.getSqlSelectLimit());\n\t\t\t}\n\t\t\trs = stmt.executeQuery(sql);\n\n\t\t\tList<FieldPacket> fieldPks = new LinkedList<FieldPacket>();\n\t\t\tResultSetUtil.resultSetToFieldPacket(sc.getCharset(), fieldPks, rs,\n\t\t\t\t\tthis.isSpark);\n\t\t\tint colunmCount = fieldPks.size();\n\t\t\tByteBuffer byteBuf = sc.allocate();\n\t\t\tResultSetHeaderPacket headerPkg = new ResultSetHeaderPacket();\n\t\t\theaderPkg.fieldCount = fieldPks.size();\n\t\t\theaderPkg.packetId = ++packetId;\n\n\t\t\tbyteBuf = headerPkg.write(byteBuf, sc, true);\n\t\t\tbyteBuf.flip();\n\t\t\tbyte[] header = new byte[byteBuf.limit()];\n\t\t\tbyteBuf.get(header);\n\t\t\tbyteBuf.clear();\n\t\t\tList<byte[]> fields = new ArrayList<byte[]>(fieldPks.size());\n\t\t\tIterator<FieldPacket> itor = fieldPks.iterator();\n\t\t\twhile (itor.hasNext()) {\n\t\t\t\tFieldPacket curField = itor.next();\n\t\t\t\tcurField.packetId = ++packetId;\n\t\t\t\tbyteBuf = curField.write(byteBuf, sc, false);\n\t\t\t\tbyteBuf.flip();\n\t\t\t\tbyte[] field = new byte[byteBuf.limit()];\n\t\t\t\tbyteBuf.get(field);\n\t\t\t\tbyteBuf.clear();\n\t\t\t\tfields.add(field);\n\t\t\t}\n\t\t\tEOFPacket eofPckg = new EOFPacket();\n\t\t\teofPckg.packetId = ++packetId;\n\t\t\tbyteBuf = eofPckg.write(byteBuf, sc, false);\n\t\t\tbyteBuf.flip();\n\t\t\tbyte[] eof = new byte[byteBuf.limit()];\n\t\t\tbyteBuf.get(eof);\n\t\t\tbyteBuf.clear();\n\t\t\tthis.respHandler.fieldEofResponse(header, fields, eof, this);\n\n\t\t\t// output row\n\t\t\twhile (rs.next()) {\n\t\t\t\tRowDataPacket curRow = new RowDataPacket(colunmCount);\n\t\t\t\tfor (int i = 0; i < colunmCount; i++) {\n\t\t\t\t\tint j = i + 1;\n\t\t\t\t\tif(MysqlDefs.isBianry((byte) fieldPks.get(i).type)) {\n\t\t\t\t\t\t\tcurRow.add(rs.getBytes(j));\n\t\t\t\t\t} else if(fieldPks.get(i).type == MysqlDefs.FIELD_TYPE_DECIMAL ||\n\t\t\t\t\t\t\tfieldPks.get(i).type == (MysqlDefs.FIELD_TYPE_NEW_DECIMAL - 256)) { // field type is unsigned byte\n\t\t\t\t\t\t// ensure that do not use scientific notation format\n\t\t\t\t\t\tBigDecimal val = rs.getBigDecimal(j);\n\t\t\t\t\t\tcurRow.add(StringUtil.encode(val != null ? val.toPlainString() : null,\n\t\t\t\t\t\t\t\tsc.getCharset()));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t   curRow.add(StringUtil.encode(rs.getString(j),\n\t\t\t\t\t\t\t\t   sc.getCharset()));\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\tcurRow.packetId = ++packetId;\n\t\t\t\tbyteBuf = curRow.write(byteBuf, sc, false);\n\t\t\t\tbyteBuf.flip();\n\t\t\t\tbyte[] row = new byte[byteBuf.limit()];\n\t\t\t\tbyteBuf.get(row);\n\t\t\t\tbyteBuf.clear();\n\t\t\t\tthis.respHandler.rowResponse(row, this);\n\t\t\t}\n\n\t\t\tfieldPks.clear();\n\n\t\t\t// end row\n\t\t\teofPckg = new EOFPacket();\n\t\t\teofPckg.packetId = ++packetId;\n\t\t\tbyteBuf = eofPckg.write(byteBuf, sc, false);\n\t\t\tbyteBuf.flip();\n\t\t\teof = new byte[byteBuf.limit()];\n\t\t\tbyteBuf.get(eof);\n\t\t\tsc.recycle(byteBuf);\n\t\t\tthis.respHandler.rowEofResponse(eof, this);\n\t\t} finally {\n\t\t\tif (rs != null) {\n\t\t\t\ttry {\n\t\t\t\t\trs.close();\n\t\t\t\t} catch (SQLException e) {\n\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (stmt != null) {\n\t\t\t\ttry {\n\t\t\t\t\tstmt.close();\n\t\t\t\t} catch (SQLException e) {\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void query(final String sql) throws UnsupportedEncodingException {\n\t\tif(respHandler instanceof ConnectionHeartBeatHandler)\n\t\t{\n\t\t\tjustForHeartbeat(sql);\n\t\t}    else\n\t\t{\n\t\t\tthrow new UnsupportedOperationException(\"global seq and share join and special io.mycat.backend.mysql.nio.handler.ResponseHandler  are not unsupported in jdbc driver yet \");\n\t\t}\n\t}\n\tprivate void justForHeartbeat(String sql)\n\t\t\t  {\n\n\t\tStatement stmt = null;\n\n\t\ttry {\n\t\t\tstmt = con.createStatement();\n\t\t\tstmt.execute(sql);\n\t\t\tif(!isAutocommit()){ //如果在写库上，如果是事务方式的连接，需要进行手动commit\n\t\t\t    con.commit();\n\t\t\t}\n\t\t\tthis.respHandler.okResponse(OkPacket.OK, this);\n\n\t\t}\n\t\tcatch (Exception e)\n\t\t{\n\t\t\tString msg = e.getMessage();\n\t\t\tErrorPacket error = new ErrorPacket();\n\t\t\terror.packetId = ++packetId;\n\t\t\terror.errno = ErrorCode.ER_UNKNOWN_ERROR;\n\t\t\terror.message = msg.getBytes();\n\t\t\tthis.respHandler.errorResponse(error.writeToBytes(), this);\n\t\t}\n\t\tfinally {\n\t\t\tif (stmt != null) {\n\t\t\t\ttry {\n\t\t\t\t\tstmt.close();\n\t\t\t\t} catch (SQLException e) {\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t@Override\n\tpublic Object getAttachment() {\n\t\treturn this.attachement;\n\t}\n\n\t@Override\n\tpublic String getCharset() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void execute(final RouteResultsetNode node,\n\t\t\t\t\t\tfinal ServerConnection source, final boolean autocommit)\n\t\t\tthrows IOException {\n\t\tthis.sqlSelectLimit = source.getSqlSelectLimit();\n    \tRunnable runnable = new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\texecuteSQL(node, source, autocommit);\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tMycatServer.getInstance().getBusinessExecutor().execute(runnable);\n\t}\n\n\t@Override\n\tpublic void recordSql(String host, String schema, String statement) {\n\n\t}\n\n\t@Override\n\tpublic boolean syncAndExcute() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void rollback() {\n\t\ttry {\n\t\t\tcon.rollback();\n\n\t\t\tthis.respHandler.okResponse(OkPacket.OK, this);\n\t\t} catch (SQLException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tpublic boolean isRunning() {\n\t\treturn this.running;\n\t}\n\n\t@Override\n\tpublic boolean isBorrowed() {\n\t\treturn this.borrowed;\n\t}\n\n\t@Override\n\tpublic void setBorrowed(boolean borrowed) {\n\t\tthis.borrowed = borrowed;\n\n\t}\n\n\t@Override\n\tpublic int getTxIsolation() {\n\t\tif (con != null) {\n\t\t\ttry {\n\t\t\t\treturn con.getTransactionIsolation();\n\t\t\t} catch (SQLException e) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t} else {\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isAutocommit() {\n\t\tif (con == null) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\ttry {\n\t\t\t\treturn con.getAutoCommit();\n\t\t\t} catch (SQLException e) {\n\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isTxReadonly() {\n\t\tif (con == null) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\ttry {\n\t\t\t\treturn con.isReadOnly();\n\t\t\t} catch (SQLException e) {\n\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int getSqlSelectLimit() {\n\t\treturn sqlSelectLimit;\n\t}\n\n\t@Override\n\tpublic long getId() {\n\t\treturn id;\n\t}\n\n\t@Override\n    public String toString() {\n        return \"JDBCConnection [id=\" + id +\",autocommit=\"+this.isAutocommit()+\",pool=\" + pool + \", schema=\" + schema + \", dbType=\" + dbType + \", oldSchema=\"\n                + oldSchema + \", packetId=\" + packetId + \", txIsolation=\" + txIsolation + \", running=\" + running\n                + \", borrowed=\" + borrowed + \", host=\" + host + \", port=\" + port + \", con=\" + con\n                + \", respHandler=\" + respHandler + \", attachement=\" + attachement + \", headerOutputed=\"\n                + headerOutputed + \", modifiedSQLExecuted=\" + modifiedSQLExecuted + \", startTime=\" + startTime\n                + \", lastTime=\" + lastTime + \", isSpark=\" + isSpark + \", processor=\" + processor + \"]\";\n    }\n\n\t@Override\n\tpublic void discardClose(String reason) {\n\t\t// TODO Auto-generated method stub\n\t\t\n\t}\n\n\t@Override\n\tpublic void query(String sql, int charsetIndex) {\n\t\ttry {\n\t\t\tquery(sql);\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\te.printStackTrace();\n\t\t\tLOGGER.debug(\"UnsupportedEncodingException :\"+ e.getMessage());\n\t\t}\t\t\n\t}\n\n\t@Override\n\tpublic boolean checkAlive() {\n\t\ttry {\n\t\t\tif(!con.isClosed()){\n\t\t\t\tif(pool.getConfig().isCheckAlive()){\n\t\t\t\t\ttry(Statement statement = con.createStatement()){\n\t\t\t\t\t\tstatement.execute(pool.getHeartbeat().getHeartbeatSQL());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tLOGGER.error(\"connection is closed\",e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n    @Override\n    public void disableRead() {\n        // TODO Auto-generated method stub\n\n    }\n\n    @Override\n    public void enableRead() {\n        // TODO Auto-generated method stub\n\n    }\n\n\tpublic void setFromSlaveDB(boolean fromSlaveDB) {\n\t\tthis.fromSlaveDB = fromSlaveDB;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/JDBCDatasource.java",
    "content": "package io.mycat.backend.jdbc;\n\nimport java.io.IOException;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport com.alibaba.druid.pool.DruidDataSource;\nimport com.google.common.collect.Lists;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.heartbeat.DBHeartbeat;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.config.model.DBHostConfig;\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.net.NIOConnector;\nimport io.mycat.net.NIOProcessor;\n\npublic class JDBCDatasource extends PhysicalDatasource {\n\n\tprivate DruidDataSource dataSource;\n\t\n\tstatic {\t\t\n\t\t// 加载可能的驱动\n\t\tList<String> drivers = Lists.newArrayList(\n\t\t\t\t\"com.mysql.jdbc.Driver\", \n\t\t\t\t\"io.mycat.backend.jdbc.mongodb.MongoDriver\",\n\t\t\t\t\"io.mycat.backend.jdbc.sequoiadb.SequoiaDriver\", \n\t\t\t\t\"oracle.jdbc.OracleDriver\",\n\t\t\t\t\"com.microsoft.sqlserver.jdbc.SQLServerDriver\",\n\t\t\t\t\"net.sourceforge.jtds.jdbc.Driver\",\n\t\t\t\t\"org.apache.hive.jdbc.HiveDriver\",\n\t\t\t\t\"com.ibm.db2.jcc.DB2Driver\", \n\t\t\t\t\"org.postgresql.Driver\");\n\t\t\n\t\tfor (String driver : drivers) {\n\t\t\ttry {\n\t\t\t\tClass.forName(driver);\n\t\t\t} catch (ClassNotFoundException ignored) {\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic JDBCDatasource(DBHostConfig config, DataHostConfig hostConfig, boolean isReadNode) {\n\t\tsuper(config, hostConfig, isReadNode);\n\t\tDBHostConfig curConfig = getConfig();\n\t\tthis.dataSource = new DruidDataSource();\n\t\tdataSource.setUrl(curConfig.getUrl());\n\t\tdataSource.setUsername(curConfig.getUser());\n\t\tdataSource.setPassword(curConfig.getPassword());\n\t\tdataSource.setMaxWait(TimeUnit.SECONDS.toMillis(1));\n\t\tdataSource.setMaxActive(curConfig.getMaxCon());\n\t\tdataSource.setMinIdle(curConfig.getMinCon());\n\t}\n\n\tpublic Connection getDruidConnection() throws SQLException {\n\t\treturn this.dataSource.getConnection();\n\t}\n\n\t@Override\n\tpublic DBHeartbeat createHeartBeat() {\n\t\treturn new JDBCHeartbeat(this);\n\t}\n\n\t@Override\n\tpublic void createNewConnection(ResponseHandler handler,String schema) throws IOException {\n\t\tDBHostConfig cfg = getConfig();\n\t\tJDBCConnection c = new JDBCConnection();\n\t\tc.setHost(cfg.getIp());\n\t\tc.setPort(cfg.getPort());\n\t\tc.setPool(this);\n\t\tc.setSchema(schema);\n\t\tc.setDbType(cfg.getDbType());\n\t\tc.setFromSlaveDB(isReadNode());\n\t\t\n\t\tNIOProcessor processor = (NIOProcessor) MycatServer.getInstance().nextProcessor();\n\t\tc.setProcessor(processor);\n\t\tc.setId(NIOConnector.ID_GENERATOR.getId());  //复用mysql的Backend的ID，需要在process中存储\n\n\t\tprocessor.addBackend(c);\n\t\ttry {\n\t\t\tConnection con = getConnection();\n\t\t\t// c.setIdleTimeout(pool.getConfig().getIdleTimeout());\n\t\t\tc.setCon(con);\n\t\t\t// notify handler\n\t\t\thandler.connectionAcquired(c);\n\t\t} catch (Exception e) {\n\t\t\thandler.connectionError(e, c);\n\t\t}\n\t}\n\t\n\n\t@Override\n\tpublic boolean testConnection(String schema) throws IOException {\n\t\tboolean isConnected = false;\t\n\t\t\n\t\tConnection connection = null;\n\t\tStatement statement = null;\n\t\ttry {\n\t\t\tDBHostConfig cfg = getConfig();\n\t\t\tconnection = DriverManager.getConnection(cfg.getUrl(), cfg.getUser(), cfg.getPassword());\n\t\t\tstatement = connection.createStatement();\t\t\t\n\t\t\tif (connection != null && statement != null) {\n\t\t\t\tisConnected = true;\n\t\t\t}\t\t\t\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t} finally {\t\t\t\n\t\t\tif (statement != null) {\n\t\t\t\ttry { statement.close(); } catch (SQLException e) {}\n\t\t\t}\n\t\t\t\n\t\t\tif (connection != null) {\n\t\t\t\ttry { connection.close(); } catch (SQLException e) {}\n\t\t\t}\n\t\t}\t\t\n\t\treturn isConnected;\n\t}\n\n    Connection getConnection() throws SQLException {\n        DBHostConfig cfg = getConfig();\n\t\tConnection connection = DriverManager.getConnection(cfg.getUrl(), cfg.getUser(), cfg.getPassword());\n\t\tString initSql=getHostConfig().getConnectionInitSql();\n\t\tif (initSql != null && !\"\".equals(initSql)) {\n\t\t\tStatement statement = null;\n\t\t\ttry {\n\t\t\t\tstatement = connection.createStatement();\n\t\t\t\tstatement.execute(initSql);\n\t\t\t} finally {\n\t\t\t\tif (statement != null) {\n\t\t\t\t\tstatement.close();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn connection;\n    }\n    \n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/JDBCHeartbeat.java",
    "content": "package io.mycat.backend.jdbc;\n\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.heartbeat.MySQLHeartbeat;\nimport io.mycat.config.model.DataHostConfig;\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.heartbeat.DBHeartbeat;\nimport io.mycat.statistic.HeartbeatRecorder;\n\npublic class JDBCHeartbeat extends DBHeartbeat{\n\tpublic static final Logger LOGGER = LoggerFactory.getLogger(JDBCHeartbeat.class);\n\n\tprivate final ReentrantLock lock;\n\tprivate final JDBCDatasource source;\n    private final boolean heartbeatnull;\n    private Long lastSendTime = System.currentTimeMillis();\n    private Long lastReciveTime = System.currentTimeMillis();\n\n\tprivate final int maxRetryCount;\n\n\tprivate Logger logger = LoggerFactory.getLogger(this.getClass());\n    \n\tpublic JDBCHeartbeat(JDBCDatasource source)\n\t{\n\t\tthis.source = source;\n\t\tlock = new ReentrantLock(false);\n\t\tthis.status = INIT_STATUS;\n\t\tthis.heartbeatSQL = source.getHostConfig().getHearbeatSQL().trim();\n\t\tthis.heartbeatnull= heartbeatSQL.length()==0;\n\t\tthis.maxRetryCount = source.getHostConfig().getMaxRetryCount();\n\n\t}\n\n\t@Override\n\tpublic void start()\n\t{\n\t\tif (this.heartbeatnull){\n\t\t\tstop();\n\t\t\treturn;\n\t\t}\n\t\tlock.lock();\n\t\ttry\n\t\t{\n\t\t\tisStop.compareAndSet(true, false);\n\t\t\tthis.status = DBHeartbeat.OK_STATUS;\n\t\t} finally\n\t\t{\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stop()\n\t{\n\t\tlock.lock();\n\t\ttry\n\t\t{\n\t\t\tif (isStop.compareAndSet(false, true))\n\t\t\t{\n\t\t\t\tisChecking.set(false);\n\t\t\t}\n\t\t} finally\n\t\t{\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getLastActiveTime()\n\t{\n\t    long t = lastReciveTime;\n\t    SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        return sdf.format(new Date(t));\n\t}\n\n\t@Override\n\tpublic long getTimeout()\n\t{\n\t\treturn 0;\n\t}\n\t@Override\n\tpublic HeartbeatRecorder getRecorder() {\n        recorder.set(lastReciveTime - lastSendTime);\n        return recorder;\n    }\n\t\n\t@Override\n\tpublic void heartbeat()\n\t{\n\t    \n\t\tif (isStop.get()) {\n\t\t\treturn;\n\t\t}\n\t\tlastSendTime = System.currentTimeMillis();\n\t\tlock.lock();\n\t\ttry\n\t\t{\n\t\t\tisChecking.set(true);\n\t\t\ttry (Connection c = source.getConnection())\n\t\t\t{\n\t\t\t\ttry (Statement s = c.createStatement())\n\t\t\t\t{\n\t\t\t\t\ts.execute(heartbeatSQL);\n\t\t\t\t}\n\t\t\t\tc.close();\n\t\t\t}\n\t\t\tsetResult(OK_STATUS);\n\t\t\tif(logger.isDebugEnabled()){\n\t\t\t    logger.debug(\"JDBCHeartBeat con query sql: \"+heartbeatSQL);\n\t\t\t}\n\t\t\t\n\t\t} catch (Exception ex)\n\t\t{\n\t\t    logger.error(\"JDBCHeartBeat error\",ex);\n//\t\t\tstatus = ERROR_STATUS;\n\t\t\tsetResult(ERROR_STATUS);\n\t\t} finally\n\t\t{\n\t\t\tlock.unlock();\n\t\t\tthis.isChecking.set(false);\n\t\t\tlastReciveTime = System.currentTimeMillis();\n\t\t}\n\t}\n\n\tpublic void setResult(int result) {\n\t\tswitch (result) {\n\t\t\tcase OK_STATUS:\n\t\t\t\tsetOk();\n\t\t\t\tbreak;\n\t\t\tcase ERROR_STATUS:\n\t\t\t\tsetError();\n\t\t\t\tbreak;\n\t\t\tcase TIMEOUT_STATUS:\n\t\t\t\tsetTimeout();\n\t\t\t\tbreak;\n\t\t}\n\t\tif (this.status != OK_STATUS) {\n\t\t\tswitchSourceIfNeed(\"heartbeat error\");\n\t\t}\n\n\t}\n\n\tprivate void setOk() {\n\t\tswitch (status) {\n\t\t\tcase DBHeartbeat.TIMEOUT_STATUS:\n\t\t\t\twriteStatusMsg(source.getDbPool().getHostName(), source.getName() ,DBHeartbeat.INIT_STATUS);\n\t\t\t\tthis.status = DBHeartbeat.INIT_STATUS;\n\t\t\t\tthis.errorCount.set(0);\n\t\t\t\t//前一个状态为超时 当前状态为正常状态  那就马上发送一个请求 来验证状态是否恢复为Ok\n\t\t\t\theartbeat();// timeout, heart beat again\n\t\t\t\tbreak;\n\t\t\tcase DBHeartbeat.OK_STATUS:\n\t\t\t\tthis.errorCount.set(0);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\twriteStatusMsg(source.getDbPool().getHostName(), source.getName() ,DBHeartbeat.OK_STATUS);\n\t\t\t\tthis.status = OK_STATUS;\n\t\t\t\tthis.errorCount.set(0);;\n\t\t}\n\t}\n\t//发生错误了,是否进行下一次心跳检测的策略 . 是否进行下一次心跳检测.\n\tprivate void nextDector( int nextStatue) {\n\n\t\tif (isStop.get()) {\n\t\t\twriteStatusMsg(source.getDbPool().getHostName(), source.getName() ,DBHeartbeat.OK_STATUS);\n\t\t\tthis.status = nextStatue;\n\t\t} else {\n\t\t\t// should continues check error status\n\t\t\tif(errorCount.get() < maxRetryCount) {\n\t\t\t} else {\n\t\t\t\twriteStatusMsg(source.getDbPool().getHostName(), source.getName() ,nextStatue);\n\t\t\t\tthis.status = nextStatue;\n\t\t\t\tthis.errorCount.set(0);\n\t\t\t}\n\t\t}\n\t}\n\n\n\tprivate void setError() {\n\t\terrorCount.incrementAndGet() ;\n\t\tnextDector( ERROR_STATUS);\n\t}\n\n\tprivate void setTimeout() {\n\t\terrorCount.incrementAndGet() ;\n\t\tnextDector( TIMEOUT_STATUS);\n\t\t//status = DBHeartbeat.TIMEOUT_STATUS;\n\t}\n\n\t/**\n\t * switch data source\n\t */\n\tprivate void switchSourceIfNeed(String reason) {\n\t\tint switchType = source.getHostConfig().getSwitchType();\n\t\tString notSwitch = source.getHostConfig().getNotSwitch();\n\t\tif (notSwitch.equals(DataHostConfig.FOVER_NOT_SWITCH_DS)\n\t\t\t\t|| switchType == DataHostConfig.NOT_SWITCH_DS) {\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(\"not switch datasource ,for switchType is \"\n\t\t\t\t\t\t+ DataHostConfig.NOT_SWITCH_DS);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"to  switchSourceIfNeed function 进行读节点转换 \"\n\t\t\t);\n\t\t}\n\t\tPhysicalDBPool pool = this.source.getDbPool();\n\t\tint curDatasourceHB = pool.getSource().getHeartbeat().getStatus();\n\t\t// read node can't switch ,only write node can switch\n\t\tif (pool.getWriteType() == PhysicalDBPool.WRITE_ONLYONE_NODE\n\t\t\t\t&& !source.isReadNode()\n\t\t\t\t&& curDatasourceHB != DBHeartbeat.OK_STATUS\n\t\t\t\t&& pool.getSources().length > 1) {\n\t\t\tsynchronized (pool) {\n\t\t\t\t// try to see if need switch datasource\n\t\t\t\tcurDatasourceHB = pool.getSource().getHeartbeat().getStatus();\n\t\t\t\tif (curDatasourceHB != DBHeartbeat.INIT_STATUS && curDatasourceHB != DBHeartbeat.OK_STATUS) {\n\t\t\t\t\tint curIndex = pool.getActivedIndex();\n\t\t\t\t\tint nextId = pool.next(curIndex);\n\t\t\t\t\tPhysicalDatasource[] allWriteNodes = pool.getSources();\n\t\t\t\t\twhile (true) {\n\t\t\t\t\t\tif (nextId == curIndex) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tPhysicalDatasource theSource = allWriteNodes[nextId];\n\t\t\t\t\t\tDBHeartbeat theSourceHB = theSource.getHeartbeat();\n\t\t\t\t\t\tint theSourceHBStatus = theSourceHB.getStatus();\n\t\t\t\t\t\tif (theSourceHBStatus == DBHeartbeat.OK_STATUS) {\n\t\t\t\t\t\t\tif (switchType == DataHostConfig.SYN_STATUS_SWITCH_DS) {\n\t\t\t\t\t\t\t\tif (Integer.valueOf(0).equals( theSourceHB.getSlaveBehindMaster())) {\n\t\t\t\t\t\t\t\t\tLOGGER.info(\"try to switch datasource ,slave is synchronized to master \" + theSource.getConfig());\n\t\t\t\t\t\t\t\t\tpool.switchSourceOrVoted(nextId, true, reason);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tLOGGER.warn(\"ignored  datasource ,slave is not  synchronized to master , slave behind master :\"\n\t\t\t\t\t\t\t\t\t\t\t+ theSourceHB.getSlaveBehindMaster()\n\t\t\t\t\t\t\t\t\t\t\t+ \" \" + theSource.getConfig());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// normal switch\n\t\t\t\t\t\t\t\tLOGGER.info(\"try to switch datasource ,not checked slave synchronize status \" + theSource.getConfig());\n\t\t\t\t\t\t\t\tpool.switchSourceOrVoted(nextId, true, reason);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnextId = pool.next(nextId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/ShowVariables.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.jdbc;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.NonBlockingSession;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic final class ShowVariables\n{\n    private static final Logger LOGGER = LoggerFactory.getLogger(ShowVariables.class);\n    private static final int FIELD_COUNT = 2;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    private static final    Pattern pattern = Pattern.compile(\"(?:like|=)\\\\s*'([^']*(?:\\\\w+)+[^']*)+'\",Pattern.CASE_INSENSITIVE);\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"VARIABLE_NAME\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"VALUE\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n    private static List<String> parseVariable(String sql)\n    {\n        List<String> variableList=new ArrayList<>();\n        Matcher matcher = pattern.matcher(sql);\n        while (matcher.find())\n        {\n            variableList.add(matcher.group(1));\n        }\n        return variableList;\n    }\n    public static void execute(ServerConnection c, String sql) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n\n        List<String> variableList= parseVariable(sql);\n        for (String key : variableList)\n        {\n          String value=  variables.get(key)  ;\n            if(value!=null)\n            {\n                RowDataPacket row = getRow(key, value, c.getCharset());\n                row.packetId = ++packetId;\n                buffer = row.write(buffer, c,true);\n            }\n        }\n\n\n\n        // write lastEof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    public static void justReturnValue(ServerConnection c, String value) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n\n\n\n            if(value!=null)\n            {\n\n                RowDataPacket row = new RowDataPacket(1);\n                row.add(StringUtil.encode(value, c.getCharset()));\n                row.packetId = ++packetId;\n                buffer = row.write(buffer, c,true);\n            }\n\n\n\n        // write lastEof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(String name, String value, String charset) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(StringUtil.encode(name, charset));\n        row.add(StringUtil.encode(value, charset));\n        return row;\n    }\n\n    private static final Map<String, String> variables = new HashMap<String, String>();\n    static {\n        variables.put(\"character_set_client\", \"utf8\");\n        variables.put(\"character_set_connection\", \"utf8\");\n        variables.put(\"character_set_results\", \"utf8\");\n        variables.put(\"character_set_server\", \"utf8\");\n        variables.put(\"init_connect\", \"\");\n        variables.put(\"interactive_timeout\", \"172800\");\n        variables.put(\"lower_case_table_names\", \"1\");\n        variables.put(\"max_allowed_packet\", \"16777216\");\n        variables.put(\"net_buffer_length\", \"16384\");\n        variables.put(\"net_write_timeout\", \"60\");\n        variables.put(\"query_cache_size\", \"0\");\n        variables.put(\"query_cache_type\", \"OFF\");\n        variables.put(\"sql_mode\", \"STRICT_TRANS_TABLES\");\n        variables.put(\"system_time_zone\", \"CST\");\n        variables.put(\"time_zone\", \"SYSTEM\");\n        variables.put(\"tx_isolation\", \"REPEATABLE-READ\");\n        variables.put(\"wait_timeout\", \"172800\");\n    }\n    \n     public static void execute(ServerConnection sc, String orgin, BackendConnection jdbcConnection) {\n        execute(sc, orgin);\n        NonBlockingSession session = sc.getSession2();\n        session.releaseConnectionIfSafe(jdbcConnection, LOGGER.isDebugEnabled(), false);\n    }\n     public static void justReturnValue(ServerConnection sc, String orgin, BackendConnection jdbcConnection) {\n    \t justReturnValue(sc, orgin);\n         NonBlockingSession session = sc.getSession2();\n         session.releaseConnectionIfSafe(jdbcConnection, LOGGER.isDebugEnabled(), false);\n     }\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/DriverPropertyInfoHelper.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\r\n\r\nimport java.sql.DriverPropertyInfo;\r\nimport java.util.ArrayList;\r\n\r\n\r\npublic class DriverPropertyInfoHelper{\r\n\t\r\n\tpublic static final String AUTO_CONNECT_RETRY = \"autoConnectRetry\";\r\n\r\n\tpublic static final String CONNECTIONS_PER_HOST = \"connecionsPerHost\";\r\n\r\n\tpublic static final String CONNECT_TIMEOUT = \"connectTimeout\";\r\n\r\n\tpublic static final String CURSOR_FINALIZER_ENABLED = \"cursorFinalizerEnabled\";\r\n\r\n\tpublic static final String MAX_AUTO_CONNECT_RETRY_TIME = \"maxAutoConnectRetryTime\";\r\n\r\n\tpublic static final String READ_PREFERENCE = \"readPreference\";\r\n\r\n\tpublic static final String SOCKET_TIMEOUT = \"socketTimeout\";\r\n\r\n\tpublic DriverPropertyInfo[] getPropertyInfo()\r\n\t{\r\n\t\tArrayList<DriverPropertyInfo> propInfos = new ArrayList<DriverPropertyInfo>();\r\n\r\n\t\taddPropInfo(\r\n\t\t\tpropInfos,\r\n\t\t\tAUTO_CONNECT_RETRY,\r\n\t\t\t\"false\",\r\n\t\t\t\"If true, the driver will keep trying to connect to the same server in case that the socket \"\r\n\t\t\t\t+ \"cannot be established. There is maximum amount of time to keep retrying, which is 15s by \"\r\n\t\t\t\t+ \"default.\", null);\r\n\r\n\t\taddPropInfo(propInfos, CONNECTIONS_PER_HOST, \"10\", \"The maximum number of connections allowed per \"\r\n\t\t\t+ \"host for this Mongo instance. Those connections will be kept in a pool when idle.\", null);\r\n\r\n\t\taddPropInfo(propInfos, CONNECT_TIMEOUT, \"10000\", \"The connection timeout in milliseconds. \", null);\r\n\r\n\t\taddPropInfo(propInfos, CURSOR_FINALIZER_ENABLED, \"true\", \"Sets whether there is a a finalize \"\r\n\t\t\t+ \"method created that cleans up instances of DBCursor that the client does not close.\",\r\n\t\t\tnull);\r\n\r\n\t\taddPropInfo(propInfos, MAX_AUTO_CONNECT_RETRY_TIME, \"0\",\r\n\t\t\t\"The maximum amount of time in MS to spend retrying to open connection to the same server.\"\r\n\t\t\t\t+ \"Default is 0, which means to use the default 15s if autoConnectRetry is on.\", null);\r\n\r\n\t\taddPropInfo(propInfos, READ_PREFERENCE, \"primary\",\r\n\t\t\t\"represents preferred replica set members to which a query or command can be sent\", new String[] {\r\n\t\t\t\t\t\"primary\", \"primary preferred\", \"secondary\", \"secondary preferred\", \"nearest\" });\r\n\r\n\t\taddPropInfo(propInfos, SOCKET_TIMEOUT, \"0\", \"The socket timeout in milliseconds It is used for \"\r\n\t\t\t+ \"I/O socket read and write operations \"\r\n\t\t\t+ \"Socket.setSoTimeout(int) Default is 0 and means no timeout.\", null);\r\n\r\n\t\treturn propInfos.toArray(new DriverPropertyInfo[propInfos.size()]);\r\n\t}\r\n\r\n\tprivate void addPropInfo(final ArrayList<DriverPropertyInfo> propInfos, final String propName,\r\n\t\tfinal String defaultVal, final String description, final String[] choices)\r\n\t{\r\n\t\tDriverPropertyInfo newProp = new DriverPropertyInfo(propName, defaultVal);\r\n\t\tnewProp.description = description;\r\n\t\tif (choices != null)\r\n\t\t{\r\n\t\t\tnewProp.choices = choices;\r\n\t\t}\r\n\t\tpropInfos.add(newProp);\r\n\t}\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/MongoClientPropertyHelper.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\n\n\nimport com.google.common.base.Joiner;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.Set;\n\n/**\n * @author liuxinsi\n * @mail akalxs@gmail.com\n */\npublic class MongoClientPropertyHelper {\n    /**\n     * 格式化<code>pro</code>中的属性为{@link com.mongodb.MongoClientURI}中要求的格式。\n     *\n     * @param pro 配置参数\n     * @return 格式化后的字符串\n     */\n    public static String formatProperties(Properties pro) {\n        if (pro == null || pro.isEmpty()) {\n            return null;\n        }\n\n        Set<Object> keys = pro.keySet();\n        List<String> props = new ArrayList<>(keys.size());\n        for (Object key : keys) {\n            Object value = pro.get(key);\n            props.add(key + \"=\" + value.toString());\n        }\n        return Joiner.on(\";\").join(props);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/MongoConnection.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\n\nimport java.net.UnknownHostException;\nimport java.sql.Array;\nimport java.sql.Blob;\nimport java.sql.CallableStatement;\nimport java.sql.Clob;\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.NClob;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLClientInfoException;\nimport java.sql.SQLException;\nimport java.sql.SQLWarning;\nimport java.sql.SQLXML;\nimport java.sql.Savepoint;\nimport java.sql.Statement;\nimport java.sql.Struct;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.concurrent.Executor;\n\nimport com.mongodb.DB;\nimport com.mongodb.MongoClient;\nimport com.mongodb.MongoClientURI;\n/**  \n * 功能详细描述\n * @author sohudo[http://blog.csdn.net/wind520]\n * @create 2014年12月19日 下午6:50:23 \n * @version 0.0.1\n */\n\npublic class MongoConnection implements Connection {\n\t\n\t//private String url = null;\n\tprivate MongoClient mc = null;\t\n\tprivate boolean isClosed = false;\t\n\tprivate String _schema;\n\tprivate Properties _clientInfo;\n\n\tpublic MongoConnection(MongoClientURI mcu, String url) throws UnknownHostException {\n\t//\tthis.url = url;\n\t\tthis._schema = mcu.getDatabase();\t\t\n\t\tmc = new MongoClient(mcu);\n\t}\n\t\n\tpublic DB getDB()  {\n\t\tif (this._schema!=null) {\n\t      return this.mc.getDB(this._schema);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\t   \n\t@Override\n\tpublic <T> T unwrap(Class<T> iface) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isWrapperFor(Class<?> iface) throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String nativeSQL(String sql) throws SQLException {\n\t\t\n\t\treturn sql;\n\t}\n\n\t@Override\n\tpublic void setAutoCommit(boolean autoCommit) throws SQLException {\t\t\n\t   //if (!autoCommit)  \n\t\t//  throw new RuntimeException(\"autoCommit has to be on\");\t\n\t}\n\n\t@Override\n\tpublic boolean getAutoCommit() throws SQLException {\n\t\t\n\t\treturn true;//return false;\n\t}\n\n\t@Override\n\tpublic void commit() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void rollback() throws SQLException {\n\t\t\n\t\t//throw new RuntimeException(\"can't rollback\");\n\t}\n\n\t@Override\n\tpublic void close() throws SQLException {\n\t\t\n\t\tthis.mc=null;\n\t    isClosed=true;\t\n\t}\n\n\t@Override\n\tpublic boolean isClosed() throws SQLException {\n\t\t\n\t\treturn isClosed;//return false;\n\t}\n\n\t@Override\n\tpublic DatabaseMetaData getMetaData() throws SQLException {\n\t\t// 获取一个 DatabaseMetaData 对象，该对象包含关于此 Connection 对象所连接的数据库的元数据。\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setReadOnly(boolean readOnly) throws SQLException {\n\t\t\n\t\t//if (readOnly)\n\t\t//    throw new RuntimeException(\"no read only mode\");\t\t\n\t}\n\n\t@Override\n\tpublic boolean isReadOnly() throws SQLException {\n\t\t// 查询此 Connection 对象是否处于只读模式。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setCatalog(String catalog) throws SQLException {\n\t\t\n\t\tthis._schema=catalog;\n\t}\n\n\t@Override\n\tpublic String getCatalog() throws SQLException {\n\t\t// 获取此 Connection 对象的当前目录名称\n\t\treturn this._schema;\n\t}\n\n\t@Override\n\tpublic void setTransactionIsolation(int level) throws SQLException {\n\t\t\n\t   //throw new RuntimeException(\"no TransactionIsolation\");\n\t}\n\n\t@Override\n\tpublic int getTransactionIsolation() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic SQLWarning getWarnings() throws SQLException {\n\t\t\n\t\treturn null;//throw new RuntimeException(\"should do get last error\");\n\t}\n\n\t@Override\n\tpublic void clearWarnings() throws SQLException {\n\t\t\n\t   \n\t}\n\n\t@Override\n\tpublic Map<String, Class<?>> getTypeMap() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setTypeMap(Map<String, Class<?>> map) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void setHoldability(int holdability) throws SQLException {\n\t\t// 将使用此 Connection 对象创建的 ResultSet 对象的默认可保存性 (holdability) 更改为给定可保存性。\n\t\t\n\t}\n\n\t@Override\n\tpublic int getHoldability() throws SQLException {\n\t\t// 获取使用此 Connection 对象创建的 ResultSet 对象的当前可保存性。\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic Savepoint setSavepoint() throws SQLException {\n\t\t\n\t\treturn null;//throw new RuntimeException(\"no savepoints\");\n\t}\n\n\t@Override\n\tpublic Savepoint setSavepoint(String name) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void rollback(Savepoint savepoint) throws SQLException {\n\t\t\n\t\t// throw new RuntimeException(\"can't rollback\");\n\t}\n\n\t@Override\n\tpublic void releaseSavepoint(Savepoint savepoint) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic Statement createStatement() throws SQLException {\n\t\t// 创建一个 Statement 对象来将 SQL 语句发送到数据库。\n\t\treturn createStatement(0, 0, 0);\n\t}\n\n\t@Override\n\tpublic Statement createStatement(int resultSetType, int resultSetConcurrency)\n\t\t\tthrows SQLException {\n\t\t// 创建一个 Statement 对象，该对象将生成具有给定类型和并发性的 ResultSet 对象。\n\t\treturn createStatement(resultSetType, resultSetConcurrency, 0);\n\t}\t\n\t\n\t@Override\n\tpublic Statement createStatement(int resultSetType,\n\t\t\tint resultSetConcurrency, int resultSetHoldability)\n\t\t\tthrows SQLException {\n\t\t// 创建一个 Statement 对象，该对象将生成具有给定类型、并发性和可保存性的 ResultSet 对象。\n\t\treturn new MongoStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability);\n\t}\n\t\n\t@Override\n\tpublic CallableStatement prepareCall(String sql) throws SQLException {\n\t\t\n\t\treturn prepareCall(sql, 0, 0, 0);\n\t}\n\t\n\t@Override\n\tpublic CallableStatement prepareCall(String sql, int resultSetType,\n\t\t\tint resultSetConcurrency) throws SQLException {\n\t\t\n\t\treturn prepareCall(sql, resultSetType, resultSetConcurrency, 0);\n\t}\n\n\t@Override\n\tpublic CallableStatement prepareCall(String sql, int resultSetType,\n\t\t\tint resultSetConcurrency, int resultSetHoldability)\n\t\t\tthrows SQLException {\n\t\t\n\t\t//return null;\n\t\tthrow new RuntimeException(\"CallableStatement not supported\");\n\t}\n\t\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql) throws SQLException {\n\t\t\n\t\treturn prepareStatement(sql, 0, 0, 0);\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, int resultSetType,\n\t\t\tint resultSetConcurrency) throws SQLException {\n\t\t\n\t\treturn prepareStatement(sql, resultSetType, resultSetConcurrency, 0);\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, int resultSetType,\n\t\t\tint resultSetConcurrency, int resultSetHoldability)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn new MongoPreparedStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability,sql);\n\t}\n\t\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, int[] columnIndexes)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, String[] columnNames)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Clob createClob() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Blob createBlob() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic NClob createNClob() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic SQLXML createSQLXML() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isValid(int timeout) throws SQLException {\n\t\t\n\t\treturn this.mc.getDB(_schema) != null;\n\t}\n\n\t@Override\n\tpublic void setClientInfo(String name, String value)\n\t\t\tthrows SQLClientInfoException {\n\t\t\n\t\tthis._clientInfo.put(name, value);\n\t}\n\n\t@Override\n\tpublic void setClientInfo(Properties properties)\n\t\t\tthrows SQLClientInfoException {\n\t\t\n\t\tthis._clientInfo = properties;\n\t}\n\n\t@Override\n\tpublic String getClientInfo(String name) throws SQLException {\n\t\t// 返回通过名称指定的客户端信息属性的值。\n\t\treturn (String)this._clientInfo.get(name);\n\t}\n\n\t@Override\n\tpublic Properties getClientInfo() throws SQLException {\n\t\t// 返回一个列表，它包含驱动程序支持的每个客户端信息属性的名称和当前值。\n\t\treturn this._clientInfo;\n\t}\n\n\t@Override\n\tpublic Array createArrayOf(String typeName, Object[] elements)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Struct createStruct(String typeName, Object[] attributes)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setSchema(String schema) throws SQLException {\n\t\t\n\t\t//this._schema=schema;\n\t}\n\n\t@Override\n\tpublic String getSchema() throws SQLException {\n\t\t\n\t\treturn this._schema;\n\t}\n\n\t@Override\n\tpublic void abort(Executor executor) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void setNetworkTimeout(Executor executor, int milliseconds)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic int getNetworkTimeout() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n }\n\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/MongoData.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\r\n\r\nimport java.sql.Date;\r\nimport java.sql.Time;\r\nimport java.sql.Timestamp;\r\nimport java.sql.Types;\r\nimport java.util.HashMap;\r\n\r\nimport com.mongodb.DBCursor;\r\nimport com.mongodb.DBObject;\r\nimport com.mongodb.BasicDBList;\r\n\r\npublic class MongoData {\r\n\t\r\n   private DBCursor cursor;\r\n   private long count;\r\n   private String table;\r\n   private DBObject groupby;\r\n   \r\n   private HashMap<String,Integer> map = new HashMap<String,Integer>(); \r\n   private boolean type=false;\r\n   \r\n   public MongoData(){\r\n\t this.count=0;\r\n\t this.cursor=null;\r\n   }\r\n   \r\n   public long getCount() {\r\n\t  return this.count;\r\n   } \r\n   \r\n   \r\n   public void setCount(long count)  {\r\n\t  this.count=count;\t\t\r\n   } \r\n   \r\n   public String getTable() {\r\n\t  return this.table;\r\n   }   \r\n   \r\n   public void setTable(String table)  {\r\n\t  this.table=table;\t\t\r\n   } \r\n   \r\n   public DBObject getGrouyBy() {\r\n\t  return this.groupby;\r\n   }   \r\n   \r\n   public BasicDBList getGrouyBys() {\r\n\t   if (this.groupby instanceof BasicDBList) {\r\n\t\t  return (BasicDBList)this.groupby;  \r\n\t   }\t     \r\n\t   else {\r\n\t     return null;\r\n\t   }\r\n   }\r\n\r\n\tpublic void setGrouyBy(DBObject gb) {\r\n\t\tthis.groupby = gb;\r\n\t\tthis.type = true;\r\n\t\tif (gb instanceof BasicDBList) {\r\n\t\t\tBasicDBList basicDBList = (BasicDBList)gb;\r\n\t\t\tif(!basicDBList.isEmpty()){\r\n\t\t\t\tObject gb2 = basicDBList.get(0);\r\n\t\t\t\tif (gb2 instanceof DBObject) {\r\n\t\t\t\t\tfor (String field : ((DBObject) gb2).keySet()) {\r\n\t\t\t\t\t\tObject val = ((DBObject) gb2).get(field);\r\n\t\t\t\t\t\tsetField(field, getObjectToType(val));\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n   \r\n   public static int getObjectToType(Object ob){\r\n\t\tif (ob instanceof Integer) {\r\n\t\t\treturn Types.INTEGER;\r\n\t\t}\r\n\t\telse if (ob instanceof Boolean) {\r\n\t\t\treturn Types.BOOLEAN;\r\n\t\t}\r\n\t\telse if (ob instanceof Byte) {\r\n\t\t\treturn Types.BIT;\r\n\t\t}\t\r\n\t\telse if (ob instanceof Short) {\r\n\t\t\treturn Types.INTEGER;\r\n\t\t}\t\r\n\t\telse if (ob instanceof Float) {\r\n\t\t\treturn Types.FLOAT;\r\n\t\t}\t\t\t\r\n\t\telse if (ob instanceof Long) {\r\n\t\t\treturn Types.BIGINT;\r\n\t\t}\r\n\t\telse if (ob instanceof Double) {\r\n\t\t\treturn Types.DOUBLE;\r\n\t\t}\t\t\t\r\n\t\telse if (ob instanceof Date) {\r\n\t\t\treturn Types.DATE;\r\n\t\t}\t\r\n\t\telse if (ob instanceof Time) {\r\n\t\t\treturn Types.TIME;\r\n\t\t}\t\r\n\t\telse if (ob instanceof Timestamp) {\r\n\t\t\treturn Types.TIMESTAMP;\r\n\t\t}\r\n\t\telse if (ob instanceof String) {\r\n\t\t\treturn Types.VARCHAR;\r\n\t\t}\t\t\t\r\n\t\telse  {\r\n\t\t\treturn Types.VARCHAR;\r\n\t\t}\t   \r\n   }\r\n      \r\n   public void setField(String field,int ftype)  {\r\n\t   map.put(field, ftype);\r\n   } \r\n   \r\n   public HashMap<String,Integer> getFields()  {\r\n\t   return this.map;\r\n   } \r\n   \r\n   public boolean getType() {\r\n\t  return this.type;\r\n   }  \r\n   \r\n   public DBCursor getCursor() {\r\n\t  return this.cursor;\r\n   }  \r\n  \r\n   public DBCursor setCursor(DBCursor cursor)  {\r\n\t   return this.cursor=cursor;\t\t\r\n   }    \r\n   \r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/MongoDriver.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\n\nimport java.sql.Connection;\nimport java.sql.Driver;\nimport java.sql.DriverManager;\nimport java.sql.DriverPropertyInfo;\nimport java.sql.SQLException;\nimport java.sql.SQLFeatureNotSupportedException;\nimport java.util.Properties;\nimport java.util.logging.Logger;\n\nimport org.slf4j.LoggerFactory;\n\nimport com.mongodb.MongoClientURI;\n/**  \n * 功能详细描述\n * @author sohudo[http://blog.csdn.net/wind520]\n * @create 2014年12月19日 下午6:50:23 \n * @version 0.0.1\n */\npublic class MongoDriver implements Driver\n\n{\n    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(MongoDriver.class);\n    static final String PREFIX = \"mongodb://\";\n    private DriverPropertyInfoHelper propertyInfoHelper = new DriverPropertyInfoHelper();\n    \n\tstatic{\n\t\ttry{\n\t\t\tDriverManager.registerDriver(new MongoDriver());\n\t\t}catch (SQLException e){\n\t\t    LOGGER.error(\"initError\",e);\n\t\t}\n\t}\n\n\n\t@Override\n\tpublic Connection connect(String url, Properties info) throws SQLException {\n\t\tMongoClientURI mcu = null;\n\t\tif ((mcu = parseURL(url, info)) == null) {\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tMongoConnection result = null;\n\t\t//System.out.print(info);\n\t\ttry{\n\t\t\tresult = new MongoConnection(mcu, url);\n\t\t}catch (Exception e){\n\t\t\tthrow new SQLException(\"Unexpected exception: \" + e.getMessage(), e);\n\t\t}\n\t\t\n\t\treturn result;\n\t}\n\t\n\tprivate MongoClientURI parseURL(String url, Properties defaults) {\n\t\tif (url == null) {\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tif (!StringUtils.startsWithIgnoreCase(url, PREFIX)) {\t\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t//删掉开头的 jdbc:\n\t\t//url = url.replace(URL_JDBC, \"\");\n\n\t\t//替换user:\n\t\tif(defaults.getProperty(\"user\")!=null && defaults.getProperty(\"password\")!=null ){\n\t\t\turl = url.replace(PREFIX, PREFIX+defaults.getProperty(\"user\")+\":\"+defaults.getProperty(\"password\")+\"@\");\n\t\t}\n\t\tLOGGER.info(\"Mongodb url:\"+url);\n\n\t\tString options = MongoClientPropertyHelper.formatProperties(defaults);\n\t\tLOGGER.debug(\"the options:{}\",options);\n\t\ttry {\n\t\t\treturn new MongoClientURI(options == null ? url : url + \"?\" + options);\n\t\t} catch (Exception e) {\n\t        LOGGER.error(\"parseURLError\",e);\n\t\t\treturn null;\n\t\t}\n\t\t\n\t}\n\t\n\t@Override\n\tpublic boolean acceptsURL(String url) throws SQLException {\n\t\tif (StringUtils.startsWithIgnoreCase(url, PREFIX)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t@Override\n\tpublic DriverPropertyInfo[] getPropertyInfo(String url, Properties info)\n\t\t\tthrows SQLException {\n\n\t\treturn propertyInfoHelper.getPropertyInfo();\n\t}\t\n\t\n\n\t@Override\n\tpublic int getMajorVersion() {\n\t\treturn 1;\n\t}\n\n\t@Override\n\tpublic int getMinorVersion() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean jdbcCompliant() {\n\t\treturn true;\n\t}\n\t@Override  \n\tpublic Logger getParentLogger() throws SQLFeatureNotSupportedException{\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/MongoEmbeddedObjectProcessor.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\n\nimport com.mongodb.BasicDBList;\nimport com.mongodb.BasicDBObject;\nimport org.bson.types.ObjectId;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 处理从MongoDB中获取的内嵌对象(Embeedded Object|SubDocument)，将MongoDB对象转换为对应的Java对象。\n * <hr/>\n * 支持：\n * <ul>\n * <li>{@link ObjectId}</li>\n * <li>基本类型</li>\n * <li>枚举</li>\n * <li>内嵌对象</li>\n * <li>内嵌数组</li>\n * </ul>\n * eg.<br/>\n * public class A{<br/>\n * &nbsp; private ObjectId _id;<br/>\n * &nbsp; private String name;<br/>\n * &nbsp; private Integer age;<br/>\n * &nbsp; private B b;<br/>\n * &nbsp; private Address[] addresses;<br/>\n * &nbsp; private String[] someCode;<br/>\n * &nbsp; ...<br/>\n * }\n * <p>\n * 不支持：\n * <ul>\n * <li>第一层的内嵌集合类型</li>\n * </ul>\n * eg.<br/>\n * public class A{<br/>\n * &nbsp; private ObjectId _id;<br/>\n * &nbsp; private String name;<br/>\n * &nbsp; private Integer age;<br/>\n * &nbsp; private B b;<br/>\n * &nbsp; private List&lt;Address> addresses;<br/>\n * &nbsp; private Set&lt;String> someCode;<br/>\n * &nbsp; ...<br/>\n * }\n * <br/>\n * 第一次拿不到范型，所以addresses、someCode不支持，直接返回null。B对象里的则没问题。<br/>\n *\n * @author liuxinsi\n * @mail akalxs@gmail.com\n */\npublic class MongoEmbeddedObjectProcessor {\n    private static final Logger LOG = LoggerFactory.getLogger(MongoEmbeddedObjectProcessor.class);\n\n    /**\n     * 将传入的值<code>value</code>转换成对应的类型<code>type</code>返回。\n     *\n     * @param columnLabel 列名\n     * @param value       值\n     * @param type        对应的类型\n     * @return 转换后的对象\n     */\n    public static Object valueMapper(String columnLabel, Object value, Class<?> type) {\n        if (value == null) {\n            return null;\n        }\n\n        // mongodb _id field\n        if (type.isAssignableFrom(ObjectId.class)\n                && (value instanceof ObjectId || value instanceof String)) {\n            return new ObjectId(value.toString());\n        }\n\n        // enum\n        if (type.isEnum()) {\n            return value.toString();\n        }\n\n        // embedded collection，内嵌集合\n        if ((type.isAssignableFrom(List.class) || type.isAssignableFrom(Set.class))\n                && value instanceof BasicDBList) {\n            // TODO 拿不到范型，list没法转\n            LOG.debug(\"column:[{}],type:[{}]为内嵌列表,无法获取范型类,无法映射.return null.\", columnLabel, type);\n            return null;\n        }\n\n        // embedded object，内嵌对象\n        if (value instanceof BasicDBObject) {\n            BasicDBObject dbObj = (BasicDBObject) value;\n            return beanMapper(dbObj, type);\n        }\n\n        // embedded array,内嵌数组\n        if (type.isArray() && value instanceof BasicDBList) {\n            BasicDBList basicDBList = (BasicDBList) value;\n            return arrayMapper(basicDBList, type);\n        }\n\n        LOG.debug(\"column:[{}],type:[{}] unsupported type yet.return null\", columnLabel, type);\n        return null;\n    }\n\n    /**\n     * 加载<code>clazzToMapper</code>下所有field。\n     *\n     * @param clazzToMapper class\n     * @return filed map，k=field name，v=field\n     */\n    private static Map<String, Field> loadFields(Class<?> clazzToMapper) {\n        Map<String, Field> fieldMap = new HashMap<>();\n        Field[] fields = clazzToMapper.getDeclaredFields();\n        for (Field field : fields) {\n            field.setAccessible(true);\n            fieldMap.put(field.getName(), field);\n        }\n        return fieldMap;\n    }\n\n    /**\n     * 获取<code>field</code>字段的范型类。\n     *\n     * @param field field\n     * @return null 如果没有获取到或异常。\n     */\n    private static Class<?> getParameterizedClass(Field field) {\n        Type type = field.getGenericType();\n        String parameterizedType;\n        if (type instanceof ParameterizedType) {\n            ParameterizedType pt = (ParameterizedType) type;\n            if (pt.getActualTypeArguments() == null || pt.getActualTypeArguments().length == 0) {\n                return null;\n            }\n            parameterizedType = pt.getActualTypeArguments()[0].toString();\n        } else {\n            return null;\n        }\n\n        Class<?> clazz;\n        try {\n            clazz = Class.forName(parameterizedType);\n        } catch (ClassNotFoundException e) {\n            LOG.warn(\"获取field:{}的范型异常。\", field.getName(), e);\n            return null;\n        }\n        return clazz;\n    }\n\n    /**\n     * 根据字段<code>field</code>类型创建对应的集合类。<br/>\n     * <b>仅支持List、Set。</b>\n     *\n     * @param field field\n     * @param size  集合初始大小\n     * @return 对应集合的实现类\n     */\n    private static Collection<Object> createCollection(Field field, int size) {\n        Class<?> fieldType = field.getType();\n        Collection<Object> collection = null;\n        if (fieldType.isAssignableFrom(List.class)) {\n            collection = new ArrayList<>(size);\n        } else if (fieldType.isAssignableFrom(Set.class)) {\n            collection = new HashSet<>(size);\n        }\n        return collection;\n    }\n\n    /**\n     * 将mongodb的数据对象<code>dbObj</code>转换成对应类型<code>clazzToMapper</code>的对象。<br/>\n     * key=fieldName。\n     *\n     * @param dbObj         mongodb数据对象\n     * @param clazzToMapper 目标对象类\n     * @return 转换后的对象\n     */\n    private static Object beanMapper(BasicDBObject dbObj, Class<?> clazzToMapper) {\n        // load all field\n        Map<String, Field> fieldMap = loadFields(clazzToMapper);\n\n        // 将dbObj中的数据映射到beanMap中，如数据包含BasicDBObject则递归映射为对应的bean\n        // k=dbObj中的字段名，v=dbObj中对应的值或对象\n        Map<String, Object> beanMap = new HashMap<>();\n        for (String s : dbObj.keySet()) {\n            Object o = dbObj.get(s);\n            // 嵌套对象\n            if (o instanceof BasicDBObject) {\n                Field field = fieldMap.get(s);\n                o = beanMapper((BasicDBObject) o, field.getType());\n\n                // 钳套对象列表\n            } else if (o instanceof BasicDBList) {\n                Field field = fieldMap.get(s);\n                // 获取对应的范型\n                Class<?> parameterizedClazz = getParameterizedClass(field);\n\n                BasicDBList basicDBs = (BasicDBList) o;\n\n                Collection<Object> collection = createCollection(field, basicDBs.size());\n                for (Object basicDbObj : basicDBs) {\n                    // 基本类型\n                    if (parameterizedClazz.isPrimitive()) {\n                        collection.add(basicDbObj);\n                    } else if (parameterizedClazz.getName().startsWith(\"java.lang\")) {\n                        collection.add(basicDbObj);\n                    } else {\n                        // 对象类型\n                        collection.add(beanMapper((BasicDBObject) basicDbObj, parameterizedClazz));\n                    }\n                }\n                o = collection;\n            }\n\n            beanMap.put(s, o);\n        }\n\n        // create\n        Object instance;\n        try {\n            instance = clazzToMapper.newInstance();\n        } catch (InstantiationException | IllegalAccessException e) {\n            LOG.warn(\"实例化:[{}]对象异常.\", clazzToMapper, e);\n            return null;\n        }\n\n        // 赋值\n        Set<String> fieldNames = fieldMap.keySet();\n        for (String fieldName : fieldNames) {\n            if (beanMap.containsKey(fieldName)) {\n                Field field = fieldMap.get(fieldName);\n                Object value = beanMap.get(fieldName);\n\n                try {\n                    field.set(instance, value);\n                } catch (IllegalAccessException e) {\n                    // 应该不会报\n                    LOG.error(\"为字段:[{}]设置值异常\",\n                            fieldName, e);\n                }\n            }\n        }\n        return instance;\n    }\n\n    /**\n     * 将mongodb的数据对象列表<code>basicDBList</code>转换成对应类型<code>arrayClass</code>的数组。<br/>\n     * 基本类型直接转换，对象类型使用 {@link #beanMapper(BasicDBObject, Class)}。\n     *\n     * @param basicDBList mongodb数据对象列表\n     * @param arrayClass  目标数组对象类\n     * @return 转换后的数组对象\n     * @see MongoEmbeddedObjectProcessor#beanMapper(BasicDBObject, Class)\n     */\n    private static Object arrayMapper(BasicDBList basicDBList, Class<?> arrayClass) {\n        // 具体类\n        Class<?> clazzToMapper;\n        try {\n            clazzToMapper = Class.forName(arrayClass.getName()\n                    .replace(\"[L\", \"\")\n                    .replace(\";\", \"\"));\n        } catch (ClassNotFoundException e) {\n            LOG.warn(\"实例化:[{}]对象异常.\", arrayClass, e);\n            return null;\n        }\n\n        // 创建对应的数组\n        Object array = Array.newInstance(clazzToMapper, basicDBList.size());\n\n        // 数组赋值\n        int i = 0;\n        for (Object basicDbObj : basicDBList) {\n            Object value;\n            // 基本类型\n            if (clazzToMapper.isPrimitive()) {\n                value = basicDbObj;\n            } else if (clazzToMapper.getName().startsWith(\"java.lang\")) {\n                value = basicDbObj;\n            } else {\n                // 对象类型\n                value = beanMapper((BasicDBObject) basicDbObj, clazzToMapper);\n            }\n\n            Array.set(array, i, value);\n            i++;\n        }\n        return array;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/MongoPreparedStatement.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\r\n\r\nimport java.io.InputStream;\r\nimport java.io.Reader;\r\nimport java.math.BigDecimal;\r\nimport java.net.URL;\r\nimport java.sql.Array;\r\nimport java.sql.Blob;\r\nimport java.sql.Clob;\r\nimport java.sql.Date;\r\nimport java.sql.NClob;\r\nimport java.sql.ParameterMetaData;\r\nimport java.sql.PreparedStatement;\r\nimport java.sql.Ref;\r\nimport java.sql.ResultSet;\r\nimport java.sql.ResultSetMetaData;\r\nimport java.sql.RowId;\r\nimport java.sql.SQLException;\r\nimport java.sql.SQLXML;\r\nimport java.sql.Time;\r\nimport java.sql.Timestamp;\r\nimport java.util.ArrayList;\r\nimport java.util.Calendar;\r\nimport java.util.List;\r\n/**  \r\n * 功能详细描述\r\n * @author sohudo[http://blog.csdn.net/wind520]\r\n * @create 2014年12月19日 下午6:50:23 \r\n * @version 0.0.1\r\n */\r\npublic class MongoPreparedStatement extends MongoStatement implements\r\n\t\tPreparedStatement {\r\n\tfinal String _sql;\r\n\tfinal MongoSQLParser _mongosql;\r\n\tList _params = new ArrayList();\r\n\r\n\tpublic MongoPreparedStatement(MongoConnection conn, int type,\r\n\t\t\tint concurrency, int holdability, String sql)\r\n\t\t\tthrows MongoSQLException {\r\n\t\tsuper(conn, type, concurrency, holdability);\r\n\t\tthis._sql = sql;\r\n\t\tthis._mongosql = new MongoSQLParser(conn.getDB(), sql);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic ResultSet executeQuery() throws SQLException {\r\n\t\t\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int executeUpdate() throws SQLException {\r\n\t\t\r\n\t    this._mongosql.setParams(this._params);\r\n\t    return this._mongosql.executeUpdate();\r\n\t}\r\n\t\r\n\tpublic  void setValue(int idx, Object o) {\t  \r\n\t    while (this._params.size() <= idx) {\r\n\t\t\tthis._params.add(null);\r\n\t\t}\r\n\t    this._params.set(idx, o);\r\n\t }\r\n\t  \r\n\t@Override\r\n\tpublic void setNull(int parameterIndex, int sqlType) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBoolean(int parameterIndex, boolean x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Boolean.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setByte(int parameterIndex, byte x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Byte.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setShort(int parameterIndex, short x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Short.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setInt(int parameterIndex, int x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Integer.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setLong(int parameterIndex, long x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Long.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setFloat(int parameterIndex, float x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Float.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setDouble(int parameterIndex, double x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Double.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBigDecimal(int parameterIndex, BigDecimal x)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setString(int parameterIndex, String x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBytes(int parameterIndex, byte[] x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setDate(int parameterIndex, Date x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setTime(int parameterIndex, Time x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setTimestamp(int parameterIndex, Timestamp x)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setAsciiStream(int parameterIndex, InputStream x, int length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setUnicodeStream(int parameterIndex, InputStream x, int length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBinaryStream(int parameterIndex, InputStream x, int length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void clearParameters() throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setObject(int parameterIndex, Object x, int targetSqlType)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setObject(int parameterIndex, Object x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex,x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean execute() throws SQLException {\r\n\t\t\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void addBatch() throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setCharacterStream(int parameterIndex, Reader reader, int length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setRef(int parameterIndex, Ref x) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBlob(int parameterIndex, Blob x) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setClob(int parameterIndex, Clob x) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setArray(int parameterIndex, Array x) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic ResultSetMetaData getMetaData() throws SQLException {\r\n\t\t\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setDate(int parameterIndex, Date x, Calendar cal)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setTime(int parameterIndex, Time x, Calendar cal)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setTimestamp(int parameterIndex, Timestamp x, Calendar cal)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNull(int parameterIndex, int sqlType, String typeName)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setURL(int parameterIndex, URL x) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic ParameterMetaData getParameterMetaData() throws SQLException {\r\n\t\t\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setRowId(int parameterIndex, RowId x) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNString(int parameterIndex, String value)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNCharacterStream(int parameterIndex, Reader value,\r\n\t\t\tlong length) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNClob(int parameterIndex, NClob value) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setClob(int parameterIndex, Reader reader, long length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBlob(int parameterIndex, InputStream inputStream, long length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNClob(int parameterIndex, Reader reader, long length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setSQLXML(int parameterIndex, SQLXML xmlObject)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setObject(int parameterIndex, Object x, int targetSqlType,\r\n\t\t\tint scaleOrLength) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setAsciiStream(int parameterIndex, InputStream x, long length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBinaryStream(int parameterIndex, InputStream x, long length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setCharacterStream(int parameterIndex, Reader reader,\r\n\t\t\tlong length) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setAsciiStream(int parameterIndex, InputStream x)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBinaryStream(int parameterIndex, InputStream x)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setCharacterStream(int parameterIndex, Reader reader)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNCharacterStream(int parameterIndex, Reader value)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setClob(int parameterIndex, Reader reader) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBlob(int parameterIndex, InputStream inputStream)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNClob(int parameterIndex, Reader reader) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/MongoResultSet.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\n\nimport com.mongodb.BasicDBList;\nimport com.mongodb.DBCursor;\nimport com.mongodb.DBObject;\n\nimport java.io.InputStream;\nimport java.io.Reader;\nimport java.math.BigDecimal;\n//import java.net.MalformedURLException;\nimport java.net.URL;\nimport java.sql.Array;\nimport java.sql.Blob;\nimport java.sql.Clob;\nimport java.sql.Date;\nimport java.sql.NClob;\nimport java.sql.Ref;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.RowId;\nimport java.sql.SQLException;\nimport java.sql.SQLWarning;\nimport java.sql.SQLXML;\nimport java.sql.Statement;\nimport java.sql.Time;\nimport java.sql.Timestamp;\nimport java.sql.Types;\nimport java.util.Calendar;\nimport java.util.HashMap;\n//import java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n/**  \n * 功能详细描述\n * @author sohudo[http://blog.csdn.net/wind520]\n * @create 2014年12月19日 下午6:50:23 \n * @version 0.0.1\n */\npublic class MongoResultSet implements ResultSet\n{\n\t private final DBCursor _cursor;\n\t private DBObject _cur;\n\t private int _row = 0;\n\t private boolean _closed = false;\n\t private String[] select;\n\t private int[] fieldtype;\n\t private String _schema;\n\t private String _table;\n\t //支持聚合,包括count,group by \n\t private boolean isSum=false;\n\t //是group by\n\t private boolean isGroupBy=false;\n\t private long _sum=0;\n\t private BasicDBList dblist;\n\t \n\tpublic MongoResultSet(MongoData mongo,String schema) throws SQLException {\n\t    this._cursor = mongo.getCursor();\n\t    this._schema = schema;\n\t    this._table  = mongo.getTable();\n\t    this.isSum   = mongo.getCount()>0;\n\t    this._sum    = mongo.getCount();\n\t    this.isGroupBy= mongo.getType();\n\t    \n\t    if (this.isGroupBy) {\n\t    \tdblist  = mongo.getGrouyBys();\n\t    \tthis.isSum =true;\n\t    }\n\t    if (this._cursor!=null) {\n\t      select = (String[]) _cursor.getKeysWanted().keySet().toArray(new String[0]);\n\t    \n\t      if ( this._cursor.hasNext()){\n\t        _cur= _cursor.next(); \n\t        if (_cur!=null) {\n\t           if (select.length==0) {\n\t    \t      SetFields(_cur.keySet());\n\t           }\t    \t   \n\t          _row=1;\n\t         }\n\t      }  \n\t   \n\t     if (select.length==0){\n\t\t   select =new String[]{\"_id\"};\n\t\t   SetFieldType(true);\n\t    }\n\t    else {\n\t\t    SetFieldType(false);\n\t    }\n\t  }\n\t  else{\n\t\t  SetFields(mongo.getFields().keySet());//new String[]{\"COUNT(*)\"};\t\n\t\t  SetFieldType(mongo.getFields());\n\t    }\n\t}\n\t\n\tpublic void SetFields(Set<String> keySet) {\t\t\n\t\tthis.select = new String[keySet.size()];\n\t\tthis.select = keySet.toArray(this.select);\n\n\t}\n\tpublic void SetFieldType(boolean isid) throws SQLException {\n\t\tif (isid) {\n\t\t  fieldtype= new int[Types.VARCHAR];\n\t\t}\n\t\telse {\n\t\t\tfieldtype = new int[this.select.length];\n\t\t}\n\t\t\n\t\tif (_cur!=null) {\n\t\t  for (int i=0;i<this.select.length;i++){\n\t\t\tObject ob=this.getObject(i+1);\n\t\t\tfieldtype[i]=MongoData.getObjectToType(ob);\n\t\t  }\t\n\t\t}\n\t}\t\n\t\n\tpublic void SetFieldType(HashMap<String,Integer> map) throws SQLException {\n        fieldtype= new int[this.select.length];\n\t\t  for (int i=0;i<this.select.length;i++){\n\t\t\tString ob=map.get(select[i]).toString();\n\t\t\tfieldtype[i]=Integer.parseInt(ob);\n\t\t  }\t\n\n\t}\t\n\t@Override\n\tpublic <T> T unwrap(Class<T> iface) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isWrapperFor(Class<?> iface) throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean next() throws SQLException {\n\t\t\n\t\tif ( isSum){\t\n\t\t\tif (isGroupBy){\n\t\t\t\t_row++;\n\t\t\t\tif (_row<=dblist.size()) {\n\t\t\t\t   return true;\n\t\t\t\t}\n\t\t    \telse {\n\t\t    \t  return false;\t\t\t\t\n\t\t    \t}\n\t\t\t}\n\t\t\telse {\n\t\t\t  if (_row==1) {\n\t\t\t\t  return false;\n\t\t\t  }\n\t\t\t  else {\n\t\t\t\t_row++;\n\t\t\t    return true;\n\t\t\t  }\n\t\t    }\n\t\t}\n\t\telse {\n\t\t\tif (! this._cursor.hasNext()) {\n\t    \t   if (_row==1) {\n\t    \t\t  _row++;\t\n\t    \t\t  return true;\n\t    \t   }\n\t    \t   else {\n\t\t\t\t   return false;\n\t\t\t   }\n\t        }\n\t        else {\n\t           if (_row!=1){\n\t    \t     this._cur = this._cursor.next(); \n\t           }  \n\t           _row++;\n\t           return true;\n\t        }\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close() throws SQLException {\n\t\t\n\t\tthis._closed = true;\n\t}\n\t\n\tpublic String getField(int columnIndex){\n\t   return select[columnIndex-1];\t\n\t}\n\t\n\t@Override\n\tpublic boolean wasNull() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getString(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getString(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic boolean getBoolean(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getBoolean(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic byte getByte(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getByte(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic short getShort(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getShort(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic int getInt(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getInt(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic long getLong(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getLong(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic float getFloat(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getFloat(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic double getDouble(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getDouble(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic BigDecimal getBigDecimal(int columnIndex, int scale)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn getBigDecimal(getField(columnIndex),scale);\n\t}\n\n\t@Override\n\tpublic byte[] getBytes(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getBytes(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic Date getDate(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getDate(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic Time getTime(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getTime(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic Timestamp getTimestamp(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getTimestamp(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic InputStream getAsciiStream(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getAsciiStream(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic InputStream getUnicodeStream(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getUnicodeStream(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic InputStream getBinaryStream(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getBinaryStream(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic String getString(String columnLabel) throws SQLException {\n\t\t\n\t\tObject x = getObject(columnLabel);\n\t\tif (x == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn x.toString();\n\t}\n\n\t@Override\n\tpublic boolean getBoolean(String columnLabel) throws SQLException {\n\t\t\n\t\t//return false;\n\t\tObject x = getObject(columnLabel);\n\t\tif (x == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn ((Boolean)x).booleanValue();\n\t}\n\t\n\tpublic Number getNumber(String columnLabel)\n\t {\n\t   Number x = (Number)this._cur.get(columnLabel);\n\t   if (x == null) {\n\t\t   return Integer.valueOf(0);\n\t   }\n\t   return x;\n\t }\n\t \n\t@Override\n\tpublic byte getByte(String columnLabel) throws SQLException {\n\t\t\n\t\treturn getNumber(columnLabel).byteValue();\n\t}\n\n\t@Override\n\tpublic short getShort(String columnLabel) throws SQLException {\n\t\t\n\t\treturn getNumber(columnLabel).shortValue();\n\t}\n\n\t@Override\n\tpublic int getInt(String columnLabel) throws SQLException {\n\t\t\n\t\treturn getNumber(columnLabel).intValue();\n\t}\n\n\t@Override\n\tpublic long getLong(String columnLabel) throws SQLException {\n\t\t\n\t\treturn getNumber(columnLabel).longValue();\n\t}\n\n\t@Override\n\tpublic float getFloat(String columnLabel) throws SQLException {\n\t\t\n\t\treturn getNumber(columnLabel).floatValue();\n\t}\n\n\t@Override\n\tpublic double getDouble(String columnLabel) throws SQLException {\n\t\t\n\t\treturn getNumber(columnLabel).doubleValue();\n\t}\n\n\t@Override\n\tpublic BigDecimal getBigDecimal(String columnLabel, int scale)\n\t\t\tthrows SQLException {\n\t\t\n\t\tthrow new UnsupportedOperationException();//return null;\n\t}\n\n\t@Override\n\tpublic byte[] getBytes(String columnLabel) throws SQLException {\n\t\t\n\t\treturn (byte[])getObject(columnLabel);\n\t}\n\n\t@Override\n\tpublic Date getDate(String columnLabel) throws SQLException {\n\t\t\n\t\treturn (Date)getObject(columnLabel);\n\t}\n\n\t@Override\n\tpublic Time getTime(String columnLabel) throws SQLException {\n\t\t\n\t\treturn (Time)getObject(columnLabel);\n\t}\n\n\t@Override\n\tpublic Timestamp getTimestamp(String columnLabel) throws SQLException {\n\t\tObject obj = getObject(columnLabel);\n\t\tif(obj instanceof java.util.Date){\n\t\t\tjava.util.Date d= (java.util.Date) obj;\n\t\t\treturn new Timestamp(d.getTime());\n\t\t}\n\t\treturn (Timestamp)obj;//throw new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic InputStream getAsciiStream(String columnLabel) throws SQLException {\n\t\t\n\t\tthrow new UnsupportedOperationException();//return null;\n\t}\n\n\t@Override\n\tpublic InputStream getUnicodeStream(String columnLabel) throws SQLException {\n\t\t\n\t\tthrow new UnsupportedOperationException();//return null;\n\t}\n\n\t@Override\n\tpublic InputStream getBinaryStream(String columnLabel) throws SQLException {\n\t\t\n\t\tthrow new UnsupportedOperationException();//return null;\n\t}\n\n\t@Override\n\tpublic SQLWarning getWarnings() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void clearWarnings() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic String getCursorName() throws SQLException {\n\t\t\n\t\treturn this._cursor.toString();\n\t}\n\n\t@Override\n\tpublic ResultSetMetaData getMetaData() throws SQLException {\n\t\t\n\t\treturn new MongoResultSetMetaData(select,fieldtype,this._schema,this._table);\n\t\t/*\n\t \tif(_cur !=null){\n\t \t\treturn new MongoResultSetMetaData(_cur.keySet(),this._schema);  \n\t     }\n\t \t else{ \t\t\n\t \t\treturn new MongoResultSetMetaData(select,this._schema); \n\t     } \t\t\n\t     */\n\t}\n\n\t@Override\n\tpublic Object getObject(int columnIndex) throws SQLException {\n\t\t\n\t\t if (columnIndex == 0){\n\t\t\t if (isSum) {\n\t\t\t\t return getObject(getField(1)); \n\t\t\t }\n\t\t\t else {\n\t\t\t\t return this._cur;\n\t\t\t }\n\t\t }\n\t\telse {\n\t\t\t return getObject(getField(columnIndex));\n\t\t }\n\t}\n\n\t@Override\n\tpublic Object getObject(String columnLabel) throws SQLException {\n\t\t\n\t\tif (isSum) {\n\t\t   if (isGroupBy){\n\t\t\t  Object ob=dblist.get(_row-1);\n\t\t\t  if (ob instanceof DBObject) {\n\t\t\t\t  return ((DBObject)ob).get(columnLabel);\n\t\t\t  }\n\t\t\t  else {\n\t\t\t\t  return \"0\";  \n\t\t\t  }\n\t\t   }\n\t\t   else{\n\t\t\t   return  this._sum;\n\t\t   }\n\t\t}\n\t\telse {\n\t\t\treturn this._cur.get(columnLabel);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int findColumn(String columnLabel) throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic Reader getCharacterStream(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getCharacterStream(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic Reader getCharacterStream(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic BigDecimal getBigDecimal(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getBigDecimal(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic BigDecimal getBigDecimal(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isBeforeFirst() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isAfterLast() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isFirst() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isLast() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void beforeFirst() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void afterLast() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic boolean first() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean last() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int getRow() throws SQLException {\n\t\t\n\t\treturn this._cursor.count();\n\t}\n\n\t@Override\n\tpublic boolean absolute(int row) throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean relative(int rows) throws SQLException {\n\t\t// 按相对行数（或正或负）移动光标。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean previous() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setFetchDirection(int direction) throws SQLException {\n\t\t\n\t\tif (direction == getFetchDirection()) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int getFetchDirection() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void setFetchSize(int rows) throws SQLException {\n\t\t// 设置此 ResultSet 对象需要更多行时应该从数据库获取的行数。\n\t\t\n\t}\n\n\t@Override\n\tpublic int getFetchSize() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getType() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getConcurrency() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean rowUpdated() throws SQLException {\n\t\t// 获取是否已更新当前行。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean rowInserted() throws SQLException {\n\t\t// 获取当前行是否已有插入。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean rowDeleted() throws SQLException {\n\t\t//获取是否已删除某行。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void updateNull(int columnIndex) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBoolean(int columnIndex, boolean x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateByte(int columnIndex, byte x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateShort(int columnIndex, short x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateInt(int columnIndex, int x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateLong(int columnIndex, long x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateFloat(int columnIndex, float x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateDouble(int columnIndex, double x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBigDecimal(int columnIndex, BigDecimal x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateString(int columnIndex, String x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBytes(int columnIndex, byte[] x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateDate(int columnIndex, Date x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateTime(int columnIndex, Time x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateTimestamp(int columnIndex, Timestamp x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateAsciiStream(int columnIndex, InputStream x, int length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBinaryStream(int columnIndex, InputStream x, int length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateCharacterStream(int columnIndex, Reader x, int length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateObject(int columnIndex, Object x, int scaleOrLength)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateObject(int columnIndex, Object x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNull(String columnLabel) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBoolean(String columnLabel, boolean x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateByte(String columnLabel, byte x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateShort(String columnLabel, short x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateInt(String columnLabel, int x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateLong(String columnLabel, long x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateFloat(String columnLabel, float x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateDouble(String columnLabel, double x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBigDecimal(String columnLabel, BigDecimal x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateString(String columnLabel, String x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBytes(String columnLabel, byte[] x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateDate(String columnLabel, Date x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateTime(String columnLabel, Time x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateTimestamp(String columnLabel, Timestamp x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateAsciiStream(String columnLabel, InputStream x, int length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBinaryStream(String columnLabel, InputStream x, int length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateCharacterStream(String columnLabel, Reader reader,\n\t\t\tint length) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateObject(String columnLabel, Object x, int scaleOrLength)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateObject(String columnLabel, Object x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void insertRow() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateRow() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void deleteRow() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void refreshRow() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void cancelRowUpdates() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void moveToInsertRow() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void moveToCurrentRow() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic Statement getStatement() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Object getObject(int columnIndex, Map<String, Class<?>> map)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Ref getRef(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Blob getBlob(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Clob getClob(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Array getArray(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;//getArray(_find(i));\n\t}\n\n\t@Override\n\tpublic Object getObject(String columnLabel, Map<String, Class<?>> map)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Ref getRef(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Blob getBlob(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Clob getClob(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Array getArray(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Date getDate(int columnIndex, Calendar cal) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Date getDate(String columnLabel, Calendar cal) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Time getTime(int columnIndex, Calendar cal) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Time getTime(String columnLabel, Calendar cal) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Timestamp getTimestamp(int columnIndex, Calendar cal)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Timestamp getTimestamp(String columnLabel, Calendar cal)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic URL getURL(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic URL getURL(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void updateRef(int columnIndex, Ref x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateRef(String columnLabel, Ref x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBlob(int columnIndex, Blob x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBlob(String columnLabel, Blob x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateClob(int columnIndex, Clob x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateClob(String columnLabel, Clob x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateArray(int columnIndex, Array x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateArray(String columnLabel, Array x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic RowId getRowId(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic RowId getRowId(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void updateRowId(int columnIndex, RowId x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateRowId(String columnLabel, RowId x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic int getHoldability() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean isClosed() throws SQLException {\n\t\t\n\t\treturn this._closed;\n\t}\n\n\t@Override\n\tpublic void updateNString(int columnIndex, String nString)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNString(String columnLabel, String nString)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNClob(int columnIndex, NClob nClob) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNClob(String columnLabel, NClob nClob)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic NClob getNClob(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic NClob getNClob(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic SQLXML getSQLXML(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic SQLXML getSQLXML(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void updateSQLXML(int columnIndex, SQLXML xmlObject)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateSQLXML(String columnLabel, SQLXML xmlObject)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic String getNString(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getNString(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Reader getNCharacterStream(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Reader getNCharacterStream(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void updateNCharacterStream(int columnIndex, Reader x, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNCharacterStream(String columnLabel, Reader reader,\n\t\t\tlong length) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateAsciiStream(int columnIndex, InputStream x, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBinaryStream(int columnIndex, InputStream x, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateCharacterStream(int columnIndex, Reader x, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateAsciiStream(String columnLabel, InputStream x, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBinaryStream(String columnLabel, InputStream x,\n\t\t\tlong length) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateCharacterStream(String columnLabel, Reader reader,\n\t\t\tlong length) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBlob(int columnIndex, InputStream inputStream, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBlob(String columnLabel, InputStream inputStream,\n\t\t\tlong length) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateClob(int columnIndex, Reader reader, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateClob(String columnLabel, Reader reader, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNClob(int columnIndex, Reader reader, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNClob(String columnLabel, Reader reader, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNCharacterStream(int columnIndex, Reader x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNCharacterStream(String columnLabel, Reader reader)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateAsciiStream(int columnIndex, InputStream x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBinaryStream(int columnIndex, InputStream x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateCharacterStream(int columnIndex, Reader x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateAsciiStream(String columnLabel, InputStream x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBinaryStream(String columnLabel, InputStream x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateCharacterStream(String columnLabel, Reader reader)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBlob(int columnIndex, InputStream inputStream)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBlob(String columnLabel, InputStream inputStream)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateClob(int columnIndex, Reader reader) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateClob(String columnLabel, Reader reader)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNClob(int columnIndex, Reader reader) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNClob(String columnLabel, Reader reader)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic <T> T getObject(int columnIndex, Class<T> type) throws SQLException {\n\t\tObject value = getObject(columnIndex);\n\t\treturn (T) MongoEmbeddedObjectProcessor.valueMapper(getField(columnIndex), value, type);\n\t}\n\n\t@Override\n\tpublic <T> T getObject(String columnLabel, Class<T> type)\n\t\t\tthrows SQLException {\n\t\tObject value = getObject(columnLabel);\n\t\treturn (T) MongoEmbeddedObjectProcessor.valueMapper(columnLabel, value, type);\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/MongoResultSetMetaData.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\r\n\r\nimport java.sql.ResultSetMetaData;\r\nimport java.sql.SQLException;\r\nimport java.sql.Types;\r\n//import java.util.Arrays;\r\nimport java.util.Set;\r\n/**  \r\n * 功能详细描述\r\n * @author sohudo[http://blog.csdn.net/wind520]\r\n * @create 2014年12月19日 下午6:50:23 \r\n * @version 0.0.1\r\n */\r\n\r\npublic class MongoResultSetMetaData implements ResultSetMetaData {\r\n\t\r\n\tprivate String[] keySet ;\r\n\tprivate int[] keytype ;\r\n\tprivate String _schema;\r\n\tprivate String _table;\r\n\t\r\n\t/*\r\n\tpublic MongoResultSetMetaData(Set<String> keySet,String schema) {\r\n\t\tsuper();\r\n\t\tthis.keySet = new String[keySet.size()];\r\n\t\tthis.keySet = keySet.toArray(this.keySet);\r\n\t\tthis._schema = schema;\r\n\t}\r\n    */\r\n\tpublic MongoResultSetMetaData(String[] select,int [] ftype,String schema,String table) {\r\n\t\tsuper();\r\n\t\tthis.keySet = select;\r\n\t\tthis.keytype=ftype;\r\n\t\tthis._schema = schema;\r\n\t\tthis._table  =table;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic <T> T unwrap(Class<T> iface) throws SQLException {\r\n\t\t\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isWrapperFor(Class<?> iface) throws SQLException {\r\n\t\t\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getColumnCount() throws SQLException {\r\n\t\tif (keySet==null) {\r\n\t\t\treturn 0;\r\n\t\t}\r\n\t\telse {\r\n\t\t\treturn keySet.length;\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isAutoIncrement(int column) throws SQLException {\r\n\t\t// 是否为自动编号的字段\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isCaseSensitive(int column) throws SQLException {\r\n\t\t//指示列的大小写是否有关系\r\n\t\treturn true;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isSearchable(int column) throws SQLException {\r\n\t\t//指示是否可以在 where 子句中使用指定的列\r\n\t\treturn true;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isCurrency(int column) throws SQLException {\r\n\t\t// 指示指定的列是否是一个哈希代码值\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int isNullable(int column) throws SQLException {\r\n\t\t// 指示指定列中的值是否可以为 null。\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isSigned(int column) throws SQLException {\r\n\t\t// 指示指定列中的值是否带正负号\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getColumnDisplaySize(int column) throws SQLException {\r\n\t\t\r\n\t\treturn 50;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getColumnLabel(int column) throws SQLException {\r\n\t\treturn keySet[column-1];\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getColumnName(int column) throws SQLException {\r\n\t\treturn keySet[column-1];\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getSchemaName(int column) throws SQLException {\r\n\t\t\r\n\t\treturn this._schema;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getPrecision(int column) throws SQLException {\r\n\t\t//获取指定列的指定列宽\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getScale(int column) throws SQLException {\r\n\t\t// 检索指定参数的小数点右边的位数。\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getTableName(int column) throws SQLException {\r\n\t\t\r\n\t\treturn this._table;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getCatalogName(int column) throws SQLException {\r\n\t\t\r\n\t\treturn this._schema;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getColumnType(int column) throws SQLException {\r\n\t\t// 字段类型\r\n\t\treturn keytype[column-1];//Types.VARCHAR;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getColumnTypeName(int column) throws SQLException {\r\n\t\t// 数据库特定的类型名称\r\n\t\tswitch (keytype[column-1]){\r\n\t\tcase  Types.INTEGER: return \"INTEGER\";\r\n\t\tcase  Types.BOOLEAN:  return \"BOOLEAN\";\r\n\t\tcase  Types.BIT: return \"BITT\"; \r\n\t\tcase  Types.FLOAT: return \"FLOAT\";\r\n\t\tcase  Types.BIGINT: return \"BIGINT\";\r\n\t\tcase  Types.DOUBLE:  return \"DOUBLE\";\r\n\t\tcase  Types.DATE: return \"DATE\"; \r\n\t\tcase  Types.TIME: return \"TIME\";\r\n\t\tcase  Types.TIMESTAMP: return \"TIMESTAMP\";\r\n\t\tdefault: return \"varchar\";\r\n\t   }\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isReadOnly(int column) throws SQLException {\r\n\t\t//指示指定的列是否明确不可写入\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isWritable(int column) throws SQLException {\r\n\t\t\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isDefinitelyWritable(int column) throws SQLException {\r\n\t\t\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getColumnClassName(int column) throws SQLException {\r\n\t\t// 如果调用方法 ResultSet.getObject 从列中获取值，则返回构造其实例的 Java 类的完全限定名称\r\n\t\treturn \"Object\";\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/MongoSQLException.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\n\nimport java.sql.SQLException;\n\n@SuppressWarnings(\"serial\")\npublic class MongoSQLException extends SQLException\n{\n\n\tpublic MongoSQLException(String msg)\n    {\n        super(msg);\n    }\n\n    public static class ErrorSQL extends MongoSQLException\n    {\n\n\t\tErrorSQL(String sql)\n        {\n            super(sql);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/MongoSQLParser.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\r\n\r\n\r\n\r\nimport java.sql.Types;\r\nimport java.util.List;\r\n\r\n\r\nimport com.mongodb.*;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport com.alibaba.druid.sql.ast.SQLExpr;\r\nimport com.alibaba.druid.sql.ast.SQLOrderingSpecification;\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\n\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\r\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\r\nimport com.alibaba.druid.sql.ast.statement.*;\r\nimport com.alibaba.druid.sql.ast.expr.*;\r\nimport com.alibaba.druid.sql.ast.*;\r\n/**  \r\n * 功能详细描述\r\n * @author sohudo[http://blog.csdn.net/wind520]\r\n * @create 2014年12月19日 下午6:50:23 \r\n * @version 0.0.1\r\n */\r\npublic class MongoSQLParser {\r\n    private static final Logger LOGGER = LoggerFactory.getLogger(MongoSQLParser.class);\r\n\tprivate   final DB _db;\r\n//\tprivate   final String _sql;\r\n\tprivate   final SQLStatement statement;\t\r\n\tprivate   List _params;\r\n\tprivate   int _pos;\t\r\n\tpublic MongoSQLParser(DB db, String sql)  throws MongoSQLException\r\n\t   {\r\n\t     this._db = db;\r\n\t //    this._sql = sql;\r\n\t     this.statement = parser(sql);\r\n\t   }\r\n\t\r\n\tpublic SQLStatement parser(String s) throws MongoSQLException\r\n\t   {\r\n\t     s = s.trim();\r\n\t     try\r\n\t     {\r\n\t        MySqlStatementParser parser = new MySqlStatementParser(s);\r\n\t        return parser.parseStatement();\r\n\t     }\r\n\t     catch (Exception e)\r\n\t     {\r\n\t         LOGGER.error(\"MongoSQLParser.parserError\", e);\r\n\t    }\r\n\t     throw new MongoSQLException.ErrorSQL(s);\r\n\t   }\t\r\n\t\r\n\tpublic  void setParams(List params)\r\n\t   {\r\n\t     this._pos = 1;\r\n\t     this._params = params;\r\n\t   }\r\n\t   \r\n\tpublic MongoData query() throws MongoSQLException{\r\n        if (!(statement instanceof SQLSelectStatement)) {\r\n        \t//return null;\r\n        \tthrow new IllegalArgumentException(\"not a query sql statement\");\r\n        }\r\n        MongoData mongo=new MongoData();\r\n        DBCursor c=null;\r\n        SQLSelectStatement selectStmt = (SQLSelectStatement)statement;\r\n        SQLSelectQuery sqlSelectQuery =selectStmt.getSelect().getQuery();\t\r\n        int icount=0;\r\n\t\tif(sqlSelectQuery instanceof MySqlSelectQueryBlock) {\r\n\t\t\tMySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock)selectStmt.getSelect().getQuery();\r\n\t\t\t\r\n\t\t\tBasicDBObject fields = new BasicDBObject();\r\n\t\t\t//显示的字段\r\n\t\t\tfor(SQLSelectItem item : mysqlSelectQuery.getSelectList()) {\r\n\t\t\t\t//System.out.println(item.toString());\r\n\t\t\t\tif (!(item.getExpr() instanceof SQLAllColumnExpr)) {\r\n\t\t\t\t\tif (item.getExpr() instanceof SQLAggregateExpr) {\r\n\t\t\t\t\t\tSQLAggregateExpr expr =(SQLAggregateExpr)item.getExpr();\r\n\t\t\t\t\t\tif (expr.getMethodName().equals(\"COUNT\")) {\r\n\t\t\t\t\t\t   icount=1;\r\n\t\t\t\t\t\t   mongo.setField(getExprFieldName(expr), Types.BIGINT);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tfields.put(getExprFieldName(expr), Integer.valueOf(1));\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse {\t\t\t\t\t\r\n\t\t\t\t\t   fields.put(getFieldName(item), Integer.valueOf(1));\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t}\t\r\n\t\t\t\r\n\t\t\t//表名\r\n\t\t\tSQLTableSource table=mysqlSelectQuery.getFrom();\r\n\t\t\tDBCollection coll =this._db.getCollection(table.toString());\r\n\t\t\tmongo.setTable(table.toString());\r\n\t\t\t\r\n\t\t\tSQLExpr expr=mysqlSelectQuery.getWhere();\t\r\n\t\t\tDBObject query = parserWhere(expr);\r\n\t\t\t//System.out.println(query);\r\n\t\t\tSQLSelectGroupByClause groupby=mysqlSelectQuery.getGroupBy();\r\n\t\t\tBasicDBObject gbkey = new BasicDBObject();\r\n\t\t\tif (groupby!=null) {\r\n\t\t\t  for (SQLExpr gbexpr:groupby.getItems()){\r\n\t\t\t\tif (gbexpr instanceof SQLIdentifierExpr) {\r\n\t\t\t\t\tString name=((SQLIdentifierExpr) gbexpr).getName();\r\n\t\t\t\t\tgbkey.put(name, Integer.valueOf(1));\r\n\t\t\t\t}\r\n\t\t\t  }\r\n\t\t\t  icount=2;\r\n\t\t\t}\t\r\n\t\t\tint limitoff=0;\r\n\t\t\tint limitnum=0;\r\n\t\t\tif (mysqlSelectQuery.getLimit()!=null) {\r\n\t\t\t  limitoff=getSQLExprToInt(mysqlSelectQuery.getLimit().getOffset());\t\t\t\r\n\t\t\t  limitnum=getSQLExprToInt(mysqlSelectQuery.getLimit().getRowCount());\r\n\t\t\t}\t\r\n\t\t\t\r\n\t\t\tif (icount==1) {\r\n\t\t\t\tmongo.setCount(coll.count(query));\t\t\t\t\t\t\r\n\t\t\t}\r\n\t\t\telse if (icount==2){\r\n\t\t\t\tBasicDBObject initial = new BasicDBObject();\r\n\t\t\t\tinitial.put(\"num\", 0);\r\n\t\t\t\tString reduce=\"function (obj, prev) { \"\r\n\t\t\t\t\t\t+\"  prev.num++}\";\r\n\t\t\t\tmongo.setGrouyBy(coll.group(gbkey, query, initial, reduce));\t\t\t\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t  if ((limitoff>0) || (limitnum>0)) {\r\n\t\t\t    c = coll.find(query, fields).skip(limitoff).limit(limitnum);\r\n\t\t\t  }\r\n\t\t\t  else {\r\n\t\t\t   c = coll.find(query, fields);\r\n\t\t\t  }\r\n\t\t\t\r\n\t\t\t  SQLOrderBy orderby=mysqlSelectQuery.getOrderBy();\r\n\t\t\t  if (orderby != null ){\r\n\t\t\t\tBasicDBObject order = new BasicDBObject();\r\n\t\t\t    for (int i = 0; i < orderby.getItems().size(); i++)\r\n\t\t\t    {\r\n\t\t\t    \tSQLSelectOrderByItem orderitem = orderby.getItems().get(i);\r\n\t\t\t       order.put(orderitem.getExpr().toString(), Integer.valueOf(getSQLExprToAsc(orderitem.getType())));\r\n\t\t\t    }\r\n\t\t\t    c.sort(order); \r\n\t\t\t   // System.out.println(order);\r\n\t\t\t  }\r\n\t\t   }\r\n\t\t   mongo.setCursor(c);\r\n\t\t}\r\n\t\treturn  mongo;\t\t\r\n\t}\r\n\t\r\n\tpublic int executeUpdate() throws MongoSQLException {\r\n        if (statement instanceof SQLInsertStatement) {\r\n        \treturn InsertData((SQLInsertStatement)statement);\r\n        }\t\r\n        if (statement instanceof SQLUpdateStatement) {\r\n        \treturn UpData((SQLUpdateStatement)statement);\r\n        }\r\n        if (statement instanceof SQLDropTableStatement) {\r\n        \treturn dropTable((SQLDropTableStatement)statement);\r\n        }\r\n        if (statement instanceof SQLDeleteStatement) {\r\n        \treturn DeleteDate((SQLDeleteStatement)statement);\r\n        }\r\n        if (statement instanceof SQLCreateTableStatement) {\r\n        \treturn 1;\r\n        }          \r\n\t\treturn 1;\r\n\t\t\r\n\t}\r\n\tprivate int InsertData(SQLInsertStatement state) {\r\n\t\tif (state.getValues().getValues().size() ==0 ){\r\n\t\t\tthrow new RuntimeException(\"number of  columns error\");\r\n\t\t}\t\t\r\n\t\tif (state.getValues().getValues().size() != state.getColumns().size()){\r\n\t\t\tthrow new RuntimeException(\"number of values and columns have to match\");\r\n\t\t}\r\n\t\tSQLTableSource table=state.getTableSource();\r\n\t\tBasicDBObject[] oList = new BasicDBObject[state.getValuesList().size()];\r\n\t\tint i = 0;\r\n\t\tfor(SQLInsertStatement.ValuesClause values : state.getValuesList()){\r\n\t\t\tint j = 0;\r\n\t\t\tBasicDBObject o = new BasicDBObject();\r\n\t\t\toList[i++] = o ;\r\n\t\t\tfor(SQLExpr col : state.getColumns()) {\r\n\t\t\t\to.put(getFieldName2(col), getExpValue(values.getValues().get(j++)));\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tDBCollection coll =this._db.getCollection(table.toString());\r\n\t\tWriteResult result = coll.insert(oList);\r\n\t\treturn i; // 这里result.getN 总是返回0 , 所以按插入数据量返回影响行数\r\n\t}\r\n\tprivate int UpData(SQLUpdateStatement state) {\r\n\t\tSQLTableSource table=state.getTableSource();\r\n\t\tDBCollection coll =this._db.getCollection(table.toString());\r\n\t\t\r\n\t\tSQLExpr expr=state.getWhere();\r\n\t\tDBObject query = parserWhere(expr);\r\n\t\t\r\n\t\tBasicDBObject set = new BasicDBObject();\r\n\t\tfor(SQLUpdateSetItem col : state.getItems()){\r\n\t\t\tset.put(getFieldName2(col.getColumn()), getExpValue(col.getValue()));\t\r\n\t\t}\r\n\t\tDBObject mod = new BasicDBObject(\"$set\", set);\r\n\t\tWriteResult result = coll.updateMulti(query, mod);\r\n\t\t//System.out.println(\"changs count:\"+coll.getStats().size());\r\n\t\treturn result.getN();\r\n\t}\r\n\tprivate int DeleteDate(SQLDeleteStatement state) {\r\n\t\tSQLTableSource table=state.getTableSource();\r\n\t\tDBCollection coll =this._db.getCollection(table.toString());\r\n\t\t\r\n\t\tSQLExpr expr=state.getWhere();\r\n\t\tif (expr==null) {\r\n\t\t\tthrow new RuntimeException(\"not where of sql\");\r\n\t\t}\r\n\t\tDBObject query = parserWhere(expr);\r\n\r\n\t\tWriteResult result = coll.remove(query);\r\n\t\treturn result.getN();\r\n\t}\r\n\tprivate int dropTable(SQLDropTableStatement state) {\t\t\r\n\t\tfor (SQLTableSource table : state.getTableSources()){\r\n\t\t\tDBCollection coll =this._db.getCollection(table.toString());\r\n\t\t\tcoll.drop();\r\n\t\t}\r\n\t\treturn 1;\r\n\t\t\r\n\t}\r\n\t\r\n\tprivate int getSQLExprToInt(SQLExpr expr){\r\n\t\tif (expr instanceof SQLIntegerExpr){\r\n\t\t\treturn ((SQLIntegerExpr)expr).getNumber().intValue();\r\n\t\t}\r\n\t\treturn 0;\t\t\r\n\t}\r\n\tprivate int getSQLExprToAsc(SQLOrderingSpecification ASC){\r\n\t\tif (ASC==null ) {\r\n\t\t\treturn 1;\r\n\t\t}\r\n\t\tif (ASC==SQLOrderingSpecification.DESC){\r\n\t\t\treturn -1;\r\n\t\t}\r\n\t\telse {\r\n\t\t\treturn 1;\t\t\r\n\t\t}\r\n\t}\t\r\n\tpublic String remove(String resource,char ch)   \r\n    {   \r\n        StringBuffer buffer=new StringBuffer();   \r\n        int position=0;   \r\n        char currentChar;   \r\n  \r\n        while(position<resource.length())   \r\n        {   \r\n            currentChar=resource.charAt(position++);   \r\n            if(currentChar!=ch) {\r\n\t\t\t\tbuffer.append(currentChar);\r\n\t\t\t}\r\n        } \r\n        return buffer.toString();   \r\n    }  \r\n\tprivate Object getExpValue(SQLExpr expr){\r\n\t\tif (expr instanceof SQLIntegerExpr){\r\n\t\t\treturn ((SQLIntegerExpr)expr).getNumber().intValue();\r\n\t\t}\r\n\t\tif (expr instanceof SQLNumberExpr){\r\n\t\t\treturn ((SQLNumberExpr)expr).getNumber().doubleValue();\r\n\t\t}\t\t\r\n\t\tif (expr instanceof SQLCharExpr){\r\n\t\t\tString va=((SQLCharExpr)expr).toString();\r\n\t\t\treturn remove(va,'\\'');\r\n\t\t}\r\n\t\tif (expr instanceof SQLBooleanExpr){\t\t\t\r\n\t\t\treturn ((SQLBooleanExpr)expr).getValue();\r\n\t\t}\t\t\t\r\n\t\tif (expr instanceof SQLNullExpr){\r\n\t\t\treturn null;\r\n\t\t}\r\n\t    if (expr instanceof SQLVariantRefExpr) {\r\n\t       return this._params.get(this._pos++);\r\n\t    }\t\t\r\n\t\treturn expr;\r\n\t\t\r\n\t}\r\n\tprivate String getExprFieldName(SQLAggregateExpr expr){\r\n\t\tString field=\"\";\r\n\t\tfor (SQLExpr item :expr.getArguments()){\r\n\t\t\tfield+=item.toString();\r\n\t\t}\t\t\r\n\t\treturn expr.getMethodName()+\"(\"+field+\")\";\r\n\t\t\r\n\t}\r\n\tprivate String getFieldName2(SQLExpr item){\r\n\t\treturn item.toString();\r\n\t}\r\n\t\r\n\tprivate String getFieldName(SQLSelectItem item){\r\n\t\treturn item.toString();\r\n\t}\t\r\n\tprivate DBObject parserWhere(SQLExpr expr){\r\n\t    BasicDBObject o = new BasicDBObject();\r\n\t    parserWhere(expr,o);\r\n\t    return o;\r\n\t}\r\n\t\r\n\t\r\n\t\r\n\tprivate void parserDBObject(BasicDBObject ob,String akey, String aop,Object aval){\r\n\t\tboolean isok=false;\r\n\t\tif (!(ob.keySet().isEmpty())) {\r\n          for (String field : ob.keySet()) {            \r\n            if (akey.equals(field)){\r\n               Object val = ob.get(field);\t\r\n              if (val instanceof BasicDBObject) {\r\n            \t ((BasicDBObject) val).put(aop, aval);\r\n            \t ob.put(field, (BasicDBObject) val); \r\n            \t isok=true;\r\n            \t break;\r\n              } else if (val instanceof BasicDBList) {\r\n              //   newobj.put(field, ((BasicDBList)val).copy());\r\n               }\r\n            }  \r\n          }    \r\n        }    \r\n\t\tif (isok==false) {\r\n\t\t\tBasicDBObject xo = new BasicDBObject();\r\n\t\t\txo.put(aop, aval);\r\n\t\t\tob.put(akey,xo);\t\r\n\t\t}\r\n\t    \r\n\t}\r\n\t\r\n\t@SuppressWarnings(\"unused\")\r\n\tprivate void opSQLExpr(SQLBinaryOpExpr expr,BasicDBObject o) {\r\n\t\t   SQLExpr exprL=expr.getLeft();\r\n\t\t   if (!(exprL instanceof SQLBinaryOpExpr))\r\n\t\t   {\r\n\t\t\t  if (expr.getOperator().getName().equals(\"=\")) {  \r\n\t\t        o.put(exprL.toString(), getExpValue(expr.getRight()));\r\n\t\t\t  }\r\n\t\t\t  else {\r\n\t\t\t\t  //BasicDBObject xo = new BasicDBObject();\r\n\t\t\t\t  String op=\"\";\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"<\")) {\r\n\t\t\t\t\t  op = \"$lt\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"<=\")) {\r\n\t\t\t\t\t  op = \"$lte\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\">\")) {\r\n\t\t\t\t\t  op = \"$gt\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\">=\")) {\r\n\t\t\t\t\t  op = \"$gte\";\r\n\t\t\t\t  }\r\n\t\t\t\t  \r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"!=\")) {\r\n\t\t\t\t\t  op = \"$ne\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"<>\")) {\r\n\t\t\t\t\t  op = \"$ne\";\r\n\t\t\t\t  }\r\n\t\t\t\t  //xo.put(op, getExpValue(expr.getRight()));\r\n\t\t\t\t // o.put(exprL.toString(),xo);\r\n\t\t\t\t  parserDBObject(o,exprL.toString(),op, getExpValue(expr.getRight()));\r\n\t\t\t  }\r\n\t\t   }\t\t\r\n\t}\r\n\r\n\tprivate void parserWhere(SQLExpr aexpr,BasicDBObject o){\r\n\r\n\t\tif(aexpr instanceof SQLBinaryOpExpr){\r\n\t\t\tSQLBinaryOpExpr expr=(SQLBinaryOpExpr)aexpr;\r\n\t\t\t//处理AND和OR\r\n\t\t\tif (expr.getOperator().getName().equals(\"AND\")) {\r\n\t\t\t\tparserWhere(expr.getLeft(),o);\r\n\t\t\t\tparserWhere(expr.getRight(),o);\r\n\t\t\t}\r\n\t\t\telse if (expr.getOperator().getName().equals(\"OR\")) {\r\n\t\t\t\torWhere(expr.getLeft(),expr.getRight(),o);\r\n\t\t\t} else {\r\n\t\t\t\tSQLExpr exprL=expr.getLeft();\r\n\t\t\t\tif (!(exprL instanceof SQLBinaryOpExpr)){\r\n\t\t\t\t\tif (expr.getOperator().getName().equals(\"=\")) {\r\n\t\t\t\t\t\to.put(exprL.toString(), getExpValue(expr.getRight()));\r\n\r\n\t\t\t\t\t}else if((\"like\").equals(expr.getOperator().getName().toLowerCase())){\r\n\t\t\t\t\t\t//处理like以及正则转换\r\n\t\t\t\t\t\tString likeString=\"\";\r\n\t\t\t\t\t\ttry{\r\n\t\t\t\t\t\t\tlikeString=(\"%\"+String.valueOf(getExpValue(expr.getRight()))+\"%\")\r\n\t\t\t\t\t\t\t\t\t.replace(\"%%\",\"\")\r\n\t\t\t\t\t\t\t\t\t.replace(\"%\",\"^\");\r\n\t\t\t\t\t\t}catch (Exception e){\r\n\t\t\t\t\t\t\tthrow new RuntimeException(\"like SQL error\");\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tparserDBObject(o,exprL.toString(),\"$regex\", likeString);\r\n\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tString op=\"\";\r\n\t\t\t\t\t\tif (expr.getOperator().getName().equals(\"<\")) {\r\n\t\t\t\t\t\t\top = \"$lt\";\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (expr.getOperator().getName().equals(\"<=\")) {\r\n\t\t\t\t\t\t\top = \"$lte\";\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (expr.getOperator().getName().equals(\">\")) {\r\n\t\t\t\t\t\t\top = \"$gt\";\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (expr.getOperator().getName().equals(\">=\")) {\r\n\t\t\t\t\t\t\top = \"$gte\";\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (expr.getOperator().getName().equals(\"!=\")) {\r\n\t\t\t\t\t\t\top = \"$ne\";\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (expr.getOperator().getName().equals(\"<>\")) {\r\n\t\t\t\t\t\t\top = \"$ne\";\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tparserDBObject(o,exprL.toString(),op, getExpValue(expr.getRight()));\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}else if(aexpr instanceof  SQLInListExpr){\r\n\t\t\t//处理IN和NOT IN\r\n\t\t\tSQLInListExpr expr= (SQLInListExpr)aexpr;\r\n\t\t\tList<SQLExpr> exprList= expr.getTargetList();\r\n\t\t\tBasicDBList dbObject= new BasicDBList();\r\n\t\t\tfor(SQLExpr e:exprList){\r\n\t\t\t\tdbObject.add(getExpValue(e));\r\n\t\t\t}\r\n\t\t\tString op=\"$in\";\r\n\t\t\tif(expr.isNot()){\r\n\t\t\t\top=\"$nin\";\r\n\t\t\t}\r\n\t\t\tparserDBObject(o,expr.getExpr().toString(),op, dbObject);\r\n\t\t}\r\n\t}\r\n\r\n\r\n\tprivate void parserWhereOLD(SQLExpr aexpr,BasicDBObject o){\r\n\t     if(aexpr instanceof SQLBinaryOpExpr){\r\n\t\t   SQLBinaryOpExpr expr=(SQLBinaryOpExpr)aexpr;  \r\n\t\t   SQLExpr exprL=expr.getLeft();\r\n\t\t   if (!(exprL instanceof SQLBinaryOpExpr))\r\n\t\t   {\r\n\t\t\t   //opSQLExpr((SQLBinaryOpExpr)aexpr,o);\t\t\t   \r\n\t\t\t  if (expr.getOperator().getName().equals(\"=\")) {  \r\n\t\t        o.put(exprL.toString(), getExpValue(expr.getRight()));\r\n\t\t\t  }\r\n\t\t\t  else {\r\n\t\t\t\t  String op=\"\";\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"<\")) {\r\n\t\t\t\t\t  op = \"$lt\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"<=\")) {\r\n\t\t\t\t\t  op = \"$lte\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\">\")) {\r\n\t\t\t\t\t  op = \"$gt\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\">=\")) {\r\n\t\t\t\t\t  op = \"$gte\";\r\n\t\t\t\t  }\r\n\t\t\t\t  \r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"!=\")) {\r\n\t\t\t\t\t  op = \"$ne\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"<>\")) {\r\n\t\t\t\t\t  op = \"$ne\";\r\n\t\t\t\t  }\r\n\r\n\t\t\t\t  parserDBObject(o,exprL.toString(),op, getExpValue(expr.getRight()));\r\n\t\t\t  }\r\n\t\t\t  \r\n\t\t   }\r\n\t\t   else {\r\n\t\t\t if (expr.getOperator().getName().equals(\"AND\")) {  \r\n\t\t\t   parserWhere(exprL,o); \r\n\t\t\t   parserWhere(expr.getRight(),o);\r\n\t\t\t }\r\n\t\t\t else if (expr.getOperator().getName().equals(\"OR\")) {  \r\n\t\t\t\torWhere(exprL,expr.getRight(),o); \t\t\t\t\r\n\t\t\t }\r\n\t\t\t else {\r\n\t\t\t\t throw new RuntimeException(\"Can't identify the operation of  of where\"); \r\n\t\t\t }\r\n\t\t   }\r\n\t   }\r\n\t  \r\n\t}\r\n\t\r\n   \r\n   private void orWhere(SQLExpr exprL,SQLExpr exprR ,BasicDBObject ob){ \r\n\t   BasicDBObject xo = new BasicDBObject(); \r\n\t   BasicDBObject yo = new BasicDBObject(); \r\n\t   parserWhere(exprL,xo);\r\n\t   parserWhere(exprR,yo);\r\n\t   ob.put(\"$or\",new Object[]{xo,yo});\r\n   }\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/MongoStatement.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\n\nimport com.mongodb.DBCursor;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.SQLWarning;\nimport java.sql.Statement;\n/**  \n * 功能详细描述\n * @author sohudo[http://blog.csdn.net/wind520]\n * @create 2014年12月19日 下午6:50:23 \n * @version 0.0.1\n */\npublic class MongoStatement implements Statement\n{\n\tprivate MongoConnection _conn;\n    private final int _type;\n    private final int _concurrency;\n    private final int _holdability;\n    private int _fetchSize = 0;\n    //int _maxRows = 0;\n    private MongoResultSet _last;\n\n    public MongoStatement(MongoConnection conn, int type, int concurrency, int holdability)\n    {\n        this._conn = conn;\n        this._type = type;\n        this._concurrency = concurrency;\n        this._holdability = holdability;\n\n        if (this._type != 0) {\n\t\t\tthrow new UnsupportedOperationException(\"type not supported yet\");\n\t\t}\n        if (this._concurrency != 0) {\n\t\t\tthrow new UnsupportedOperationException(\"concurrency not supported yet\");\n\t\t}\n        if (this._holdability != 0) {\n\t\t\tthrow new UnsupportedOperationException(\"holdability not supported yet\");\n\t\t}\n    }\n\n\t@Override\n\tpublic <T> T unwrap(Class<T> iface) throws SQLException {\n\t\t\n\t\tthrow new UnsupportedOperationException();//return null;\n\t}\n\n\t@Override\n\tpublic boolean isWrapperFor(Class<?> iface) throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic ResultSet executeQuery(String sql) throws SQLException {\n\t\t  \n\t\tMongoData mongo= new MongoSQLParser(this._conn.getDB(), sql).query();\t\t\n        if ((this._fetchSize > 0)\n\t\t\t\t&& (mongo.getCursor()!=null)) {\n        \t//设置每次网络请求的最大记录数\n        \tmongo.getCursor().batchSize(this._fetchSize);\n        }\n        /* \n        if (this._maxRows > 0)\n        {\n            cursor.limit(this._maxRows);\n        }\n        */\n        this._last = new MongoResultSet(mongo,this._conn.getSchema());\n\t\treturn this._last;\n\t}\n    \n\t@Override\n\tpublic int executeUpdate(String sql) throws SQLException {\n\t\t// 执行更新语句\n\t\treturn new MongoSQLParser(this._conn.getDB(), sql).executeUpdate();\n\t}\n\n\t@Override\n\tpublic void close() throws SQLException {\n\t\t\n\t    this._conn = null;\n\t}\n\n\t@Override\n\tpublic int getMaxFieldSize() throws SQLException {\n\t\t// 获取可以为此 Statement 对象所生成 ResultSet 对象中的字符和二进制列值返回的最大字节数。\n\t\treturn 0;//this._fetchSize;\n\t}\n\n\t@Override\n\tpublic void setMaxFieldSize(int max) throws SQLException {\n\t\t\n\t\t//this._fetchSize=max;\n\t}\n\n\t@Override\n\tpublic int getMaxRows() throws SQLException {\n\t\t// 获取由此 Statement 对象生成的 ResultSet 对象可以包含的最大行数。\n\t\treturn 0;//this._maxRows;\n\t}\n\n\t@Override\n\tpublic void setMaxRows(int max) throws SQLException {\n\t\t\n\t\t//this._maxRows = max;\n\t}\n\n\t@Override\n\tpublic void setEscapeProcessing(boolean enable) throws SQLException {\n\t\t// 将转义处理设置为开或关。\n\t\t\n\t}\n\n\t@Override\n\tpublic int getQueryTimeout() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void setQueryTimeout(int seconds) throws SQLException {\n\t\t// Statement 对象执行的秒数设置，超时设置。\n\t\t\n\t}\n\n\t@Override\n\tpublic void cancel() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic SQLWarning getWarnings() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void clearWarnings() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void setCursorName(String name) throws SQLException {\n\t\t// 将 SQL 指针名称设置为给定的 String，后续 Statement 对象的 execute 方法将使用此字符串。\n\t\t\n\t}\n\n\t@Override\n\tpublic boolean execute(String sql) throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic ResultSet getResultSet() throws SQLException {\n\t\t\n\t\treturn this._last;\n\t}\n\n\t@Override\n\tpublic int getUpdateCount() throws SQLException {\n\t\t// 记录变更的数量，ResultSet 对象或没有更多结果，则返回 -1\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean getMoreResults() throws SQLException {\n\t\t// 移动到此 Statement 对象的下一个结果，如果其为 ResultSet 对象，则返回 true，并隐式关闭利用方法 getResultSet 获取的所有当前 ResultSet 对象。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setFetchDirection(int direction) throws SQLException {\n\t\t// 此 Statement 对象创建的 ResultSet 对象中将按该方向处理行。\n\t\t\n\t}\n\n\t@Override\n\tpublic int getFetchDirection() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void setFetchSize(int rows) throws SQLException {\n\t\t// 获取结果集合的行数，该数是根据此 Statement 对象生成的 ResultSet 对象的默认获取大小。\n\t\tthis._fetchSize=rows;\n\t}\n\n\t@Override\n\tpublic int getFetchSize() throws SQLException {\n\t\t// 获取结果集合的行数，该数是根据此 Statement 对象生成的 ResultSet 对象的默认获取大小。\n\t\treturn this._fetchSize;\n\t}\n\n\t@Override\n\tpublic int getResultSetConcurrency() throws SQLException {\n\t\t// 对象生成的 ResultSet 对象的结果集合并发性\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getResultSetType() throws SQLException {\n\t\t// 对象生成的 ResultSet 对象的结果集合类型。\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void addBatch(String sql) throws SQLException {\n\t\t// 新增批处理\n\t   throw new UnsupportedOperationException(\"batch not supported\");\n\t}\n\n\t@Override\n\tpublic void clearBatch() throws SQLException {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic int[] executeBatch() throws SQLException {\n\t\t// 将一批命令提交给数据库来执行，如果全部命令执行成功，则返回更新计数组成的数组。\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic Connection getConnection() throws SQLException {\n\t\t\n\t\treturn this._conn;\n\t}\n\n\t@Override\n\tpublic boolean getMoreResults(int current) throws SQLException {\n\t\t// 将此 Statement 对象移动到下一个结果，根据给定标志指定的指令处理所有当前 ResultSet 对象；如果下一个结果为 ResultSet 对象，则返回 true。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic ResultSet getGeneratedKeys() throws SQLException {\n\t\t// 获取由于执行此 Statement 对象而创建的所有自动生成的键。\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic int executeUpdate(String sql, int autoGeneratedKeys)\n\t\t\tthrows SQLException {\n\t\treturn executeUpdate(sql);\n\t}\n\n\t@Override\n\tpublic int executeUpdate(String sql, int[] columnIndexes)\n\t\t\tthrows SQLException {\n\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic int executeUpdate(String sql, String[] columnNames)\n\t\t\tthrows SQLException {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean execute(String sql, int autoGeneratedKeys)\n\t\t\tthrows SQLException {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean execute(String sql, int[] columnIndexes) throws SQLException {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean execute(String sql, String[] columnNames)\n\t\t\tthrows SQLException {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic int getResultSetHoldability() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean isClosed() throws SQLException {\n\t\t\n\t\treturn this._conn == null;\n\t}\n\n\t@Override\n\tpublic void setPoolable(boolean poolable) throws SQLException {\n\t\t// 请求将 Statement 池化或非池化\n\t\t\n\t}\n\n\t@Override\n\tpublic boolean isPoolable() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void closeOnCompletion() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic boolean isCloseOnCompletion() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/mongodb/StringUtils.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\r\n\r\n\r\npublic class StringUtils {\r\n\t\r\n\r\n\tpublic static boolean startsWithIgnoreCase(String searchIn, int startAt,\r\n\t\t\tString searchFor) {\r\n\t\treturn searchIn.regionMatches(true, startAt, searchFor, 0, searchFor\r\n\t\t\t\t.length());\r\n\t}\r\n\r\n\tpublic static boolean startsWithIgnoreCase(String searchIn, String searchFor) {\r\n\t\treturn startsWithIgnoreCase(searchIn, 0, searchFor);\r\n\t}\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/sequoiadb/DriverPropertyInfoHelper.java",
    "content": "package io.mycat.backend.jdbc.sequoiadb;\r\n\r\nimport java.sql.DriverPropertyInfo;\r\nimport java.util.ArrayList;\r\n\r\n\r\npublic class DriverPropertyInfoHelper{\r\n\t\r\n\tpublic static final String AUTO_CONNECT_RETRY = \"autoConnectRetry\";\r\n\r\n\tpublic static final String CONNECTIONS_PER_HOST = \"connecionsPerHost\";\r\n\r\n\tpublic static final String CONNECT_TIMEOUT = \"connectTimeout\";\r\n\r\n\tpublic static final String CURSOR_FINALIZER_ENABLED = \"cursorFinalizerEnabled\";\r\n\r\n\tpublic static final String MAX_AUTO_CONNECT_RETRY_TIME = \"maxAutoConnectRetryTime\";\r\n\r\n\tpublic static final String READ_PREFERENCE = \"readPreference\";\r\n\r\n\tpublic static final String SOCKET_TIMEOUT = \"socketTimeout\";\r\n\r\n\tpublic DriverPropertyInfo[] getPropertyInfo()\r\n\t{\r\n\t\tArrayList<DriverPropertyInfo> propInfos = new ArrayList<DriverPropertyInfo>();\r\n\r\n\t\taddPropInfo(\r\n\t\t\tpropInfos,\r\n\t\t\tAUTO_CONNECT_RETRY,\r\n\t\t\t\"false\",\r\n\t\t\t\"If true, the driver will keep trying to connect to the same server in case that the socket \"\r\n\t\t\t\t+ \"cannot be established. There is maximum amount of time to keep retrying, which is 15s by \"\r\n\t\t\t\t+ \"default.\", null);\r\n\r\n\t\taddPropInfo(propInfos, CONNECTIONS_PER_HOST, \"10\", \"The maximum number of connections allowed per \"\r\n\t\t\t+ \"host for this Mongo instance. Those connections will be kept in a pool when idle.\", null);\r\n\r\n\t\taddPropInfo(propInfos, CONNECT_TIMEOUT, \"10000\", \"The connection timeout in milliseconds. \", null);\r\n\r\n\t\taddPropInfo(propInfos, CURSOR_FINALIZER_ENABLED, \"true\", \"Sets whether there is a a finalize \"\r\n\t\t\t+ \"method created that cleans up instances of DBCursor that the client does not close.\",\r\n\t\t\tnull);\r\n\r\n\t\taddPropInfo(propInfos, MAX_AUTO_CONNECT_RETRY_TIME, \"0\",\r\n\t\t\t\"The maximum amount of time in MS to spend retrying to open connection to the same server.\"\r\n\t\t\t\t+ \"Default is 0, which means to use the default 15s if autoConnectRetry is on.\", null);\r\n\r\n\t\taddPropInfo(propInfos, READ_PREFERENCE, \"primary\",\r\n\t\t\t\"represents preferred replica set members to which a query or command can be sent\", new String[] {\r\n\t\t\t\t\t\"primary\", \"primary preferred\", \"secondary\", \"secondary preferred\", \"nearest\" });\r\n\r\n\t\taddPropInfo(propInfos, SOCKET_TIMEOUT, \"0\", \"The socket timeout in milliseconds It is used for \"\r\n\t\t\t+ \"I/O socket read and write operations \"\r\n\t\t\t+ \"Socket.setSoTimeout(int) Default is 0 and means no timeout.\", null);\r\n\r\n\t\treturn propInfos.toArray(new DriverPropertyInfo[propInfos.size()]);\r\n\t}\r\n\r\n\tprivate void addPropInfo(final ArrayList<DriverPropertyInfo> propInfos, final String propName,\r\n\t\tfinal String defaultVal, final String description, final String[] choices)\r\n\t{\r\n\t\tDriverPropertyInfo newProp = new DriverPropertyInfo(propName, defaultVal);\r\n\t\tnewProp.description = description;\r\n\t\tif (choices != null)\r\n\t\t{\r\n\t\t\tnewProp.choices = choices;\r\n\t\t}\r\n\t\tpropInfos.add(newProp);\r\n\t}\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/sequoiadb/SequoiaConnection.java",
    "content": "package io.mycat.backend.jdbc.sequoiadb;\n\nimport java.net.UnknownHostException;\nimport java.sql.Array;\nimport java.sql.Blob;\nimport java.sql.CallableStatement;\nimport java.sql.Clob;\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.NClob;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLClientInfoException;\nimport java.sql.SQLException;\nimport java.sql.SQLWarning;\nimport java.sql.SQLXML;\nimport java.sql.Savepoint;\nimport java.sql.Statement;\nimport java.sql.Struct;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.concurrent.Executor;\n\nimport com.sequoiadb.base.Sequoiadb;\nimport com.sequoiadb.base.CollectionSpace;\nimport com.sequoiadb.exception.BaseException;\n/**  \n * 功能详细描述\n * @author sohudo[http://blog.csdn.net/wind520]\n * @create 2014年12月19日 下午6:50:23 \n * @version 0.0.1\n */\n\npublic class SequoiaConnection implements Connection {\n\t\n\t//private String url = null;\n\tprivate Sequoiadb mc = null;\t\n\tprivate boolean isClosed = false;\t\n\tprivate String _schema;\n\tprivate Properties _clientInfo;\n\n\tpublic SequoiaConnection(String url, String db) throws UnknownHostException {\n\t//\tthis.url = url;\n\t\tthis._schema = db;\t\t\n\t\ttry {\n\t\tmc = new Sequoiadb(url, \"\", \"\");\n\t\t} catch (BaseException e) {\n\t\t\tthrow new IllegalArgumentException(\"Failed to connect to database: \" + url\n\t\t\t\t\t+ \", error description\" + e.getErrorType());\n\t\t}\t\t\n\t}\n\t\n\tpublic CollectionSpace getDB()  {\n\t\tif (this._schema!=null) {\n\t\t\tif (mc.isCollectionSpaceExist(this._schema)) {\n\t\t\t\treturn this.mc.getCollectionSpace(this._schema);\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn this.mc.createCollectionSpace(this._schema);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\t   \n\t@Override\n\tpublic <T> T unwrap(Class<T> iface) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isWrapperFor(Class<?> iface) throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String nativeSQL(String sql) throws SQLException {\n\t\t\n\t\treturn sql;\n\t}\n\n\t@Override\n\tpublic void setAutoCommit(boolean autoCommit) throws SQLException {\n\t\t\n\t    if (!autoCommit) {\n\t\t\tthrow new RuntimeException(\"autoCommit has to be on\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean getAutoCommit() throws SQLException {\n\t\t\n\t\treturn true;//return false;\n\t}\n\n\t@Override\n\tpublic void commit() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void rollback() throws SQLException {\n\t\t\n\t\t//throw new RuntimeException(\"can't rollback\");\n\t}\n\n\t@Override\n\tpublic void close() throws SQLException {\n\t\t\n\t\tthis.mc=null;\n\t    isClosed=true;\t\n\t}\n\n\t@Override\n\tpublic boolean isClosed() throws SQLException {\n\t\t\n\t\treturn isClosed;//return false;\n\t}\n\n\t@Override\n\tpublic DatabaseMetaData getMetaData() throws SQLException {\n\t\t// 获取一个 DatabaseMetaData 对象，该对象包含关于此 Connection 对象所连接的数据库的元数据。\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setReadOnly(boolean readOnly) throws SQLException {\n\t\t\n\t\t//if (readOnly)\n\t\t//    throw new RuntimeException(\"no read only mode\");\t\t\n\t}\n\n\t@Override\n\tpublic boolean isReadOnly() throws SQLException {\n\t\t// 查询此 Connection 对象是否处于只读模式。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setCatalog(String catalog) throws SQLException {\n\t\t\n\t\tthis._schema=catalog;\n\t}\n\n\t@Override\n\tpublic String getCatalog() throws SQLException {\n\t\t// 获取此 Connection 对象的当前目录名称\n\t\treturn this._schema;\n\t}\n\n\t@Override\n\tpublic void setTransactionIsolation(int level) throws SQLException {\n\t\t\n\t   //throw new RuntimeException(\"no TransactionIsolation\");\n\t}\n\n\t@Override\n\tpublic int getTransactionIsolation() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic SQLWarning getWarnings() throws SQLException {\n\t\t\n\t\treturn null;//throw new RuntimeException(\"should do get last error\");\n\t}\n\n\t@Override\n\tpublic void clearWarnings() throws SQLException {\n\t\t\n\t   \n\t}\n\n\t@Override\n\tpublic Map<String, Class<?>> getTypeMap() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setTypeMap(Map<String, Class<?>> map) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void setHoldability(int holdability) throws SQLException {\n\t\t// 将使用此 Connection 对象创建的 ResultSet 对象的默认可保存性 (holdability) 更改为给定可保存性。\n\t\t\n\t}\n\n\t@Override\n\tpublic int getHoldability() throws SQLException {\n\t\t// 获取使用此 Connection 对象创建的 ResultSet 对象的当前可保存性。\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic Savepoint setSavepoint() throws SQLException {\n\t\t\n\t\treturn null;//throw new RuntimeException(\"no savepoints\");\n\t}\n\n\t@Override\n\tpublic Savepoint setSavepoint(String name) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void rollback(Savepoint savepoint) throws SQLException {\n\t\t\n\t\t// throw new RuntimeException(\"can't rollback\");\n\t}\n\n\t@Override\n\tpublic void releaseSavepoint(Savepoint savepoint) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic Statement createStatement() throws SQLException {\n\t\t// 创建一个 Statement 对象来将 SQL 语句发送到数据库。\n\t\treturn createStatement(0, 0, 0);\n\t}\n\n\t@Override\n\tpublic Statement createStatement(int resultSetType, int resultSetConcurrency)\n\t\t\tthrows SQLException {\n\t\t// 创建一个 Statement 对象，该对象将生成具有给定类型和并发性的 ResultSet 对象。\n\t\treturn createStatement(resultSetType, resultSetConcurrency, 0);\n\t}\t\n\t\n\t@Override\n\tpublic Statement createStatement(int resultSetType,\n\t\t\tint resultSetConcurrency, int resultSetHoldability)\n\t\t\tthrows SQLException {\n\t\t// 创建一个 Statement 对象，该对象将生成具有给定类型、并发性和可保存性的 ResultSet 对象。\n\t\treturn new SequoiaStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability);\n\t}\n\t\n\t@Override\n\tpublic CallableStatement prepareCall(String sql) throws SQLException {\n\t\t\n\t\treturn prepareCall(sql, 0, 0, 0);\n\t}\n\t\n\t@Override\n\tpublic CallableStatement prepareCall(String sql, int resultSetType,\n\t\t\tint resultSetConcurrency) throws SQLException {\n\t\t\n\t\treturn prepareCall(sql, resultSetType, resultSetConcurrency, 0);\n\t}\n\n\t@Override\n\tpublic CallableStatement prepareCall(String sql, int resultSetType,\n\t\t\tint resultSetConcurrency, int resultSetHoldability)\n\t\t\tthrows SQLException {\n\t\t\n\t\t//return null;\n\t\tthrow new RuntimeException(\"CallableStatement not supported\");\n\t}\n\t\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql) throws SQLException {\n\t\t\n\t\treturn prepareStatement(sql, 0, 0, 0);\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, int resultSetType,\n\t\t\tint resultSetConcurrency) throws SQLException {\n\t\t\n\t\treturn prepareStatement(sql, resultSetType, resultSetConcurrency, 0);\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, int resultSetType,\n\t\t\tint resultSetConcurrency, int resultSetHoldability)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn new SequoiaPreparedStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability,sql);\n\t}\n\t\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, int[] columnIndexes)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, String[] columnNames)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Clob createClob() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Blob createBlob() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic NClob createNClob() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic SQLXML createSQLXML() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isValid(int timeout) throws SQLException {\n\t\t\n\t\treturn getDB() != null;\n\t}\n\n\t@Override\n\tpublic void setClientInfo(String name, String value)\n\t\t\tthrows SQLClientInfoException {\n\t\t\n\t\tthis._clientInfo.put(name, value);\n\t}\n\n\t@Override\n\tpublic void setClientInfo(Properties properties)\n\t\t\tthrows SQLClientInfoException {\n\t\t\n\t\tthis._clientInfo = properties;\n\t}\n\n\t@Override\n\tpublic String getClientInfo(String name) throws SQLException {\n\t\t// 返回通过名称指定的客户端信息属性的值。\n\t\treturn (String)this._clientInfo.get(name);\n\t}\n\n\t@Override\n\tpublic Properties getClientInfo() throws SQLException {\n\t\t// 返回一个列表，它包含驱动程序支持的每个客户端信息属性的名称和当前值。\n\t\treturn this._clientInfo;\n\t}\n\n\t@Override\n\tpublic Array createArrayOf(String typeName, Object[] elements)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Struct createStruct(String typeName, Object[] attributes)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setSchema(String schema) throws SQLException {\n\t\t\n\t\t//this._schema=schema;\n\t}\n\n\t@Override\n\tpublic String getSchema() throws SQLException {\n\t\t\n\t\treturn this._schema;\n\t}\n\n\t@Override\n\tpublic void abort(Executor executor) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void setNetworkTimeout(Executor executor, int milliseconds)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic int getNetworkTimeout() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n }\n\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/sequoiadb/SequoiaData.java",
    "content": "package io.mycat.backend.jdbc.sequoiadb;\r\n\r\nimport java.sql.Date;\r\nimport java.sql.Time;\r\nimport java.sql.Timestamp;\r\nimport java.sql.Types;\r\nimport java.util.HashMap;\r\n\r\nimport org.bson.BSONObject;\r\nimport org.bson.BasicBSONObject;\r\nimport org.bson.types.BasicBSONList;\r\nimport com.sequoiadb.base.DBCursor;\r\n\r\npublic class SequoiaData {\r\n\t\r\n   private DBCursor cursor;\r\n   private long count;\r\n   private String table;\r\n   private BSONObject groupby;\r\n   \r\n   private HashMap<String,Integer> map = new HashMap<String,Integer>(); \r\n   private boolean type=false;\r\n   \r\n   public SequoiaData(){\r\n\t this.count=0;\r\n\t this.cursor=null;\r\n   }\r\n   \r\n   public long getCount() {\r\n\t  return this.count;\r\n   } \r\n   \r\n   \r\n   public void setCount(long count)  {\r\n\t  this.count=count;\t\t\r\n   } \r\n   \r\n   public String getTable() {\r\n\t  return this.table;\r\n   }   \r\n   \r\n   public void setTable(String table)  {\r\n\t  this.table=table;\t\t\r\n   } \r\n   \r\n   public BSONObject getGrouyBy() {\r\n\t  return this.groupby;\r\n   }   \r\n  \r\n   public BasicBSONList getGrouyBys() {\r\n\t   if (this.groupby instanceof BasicBSONList) {\r\n\t\t  return (BasicBSONList)this.groupby;  \r\n\t   }\t     \r\n\t   else {\r\n\t     return null;\r\n\t   }\r\n   }    \r\n   public void setGrouyBy(BSONObject gb)  {\r\n\t  this.groupby=gb;\t\r\n\t  this.type=true;\r\n\t  if (gb instanceof BasicBSONList) {\r\n\t\tObject gb2=((BasicBSONList)gb).get(0);\r\n\t\tif (gb2 instanceof BSONObject) { \r\n        for (String field :((BSONObject)gb2).keySet()) {            \r\n          Object val = ((BSONObject)gb2).get(field);\t\r\n          setField(field,getObjectToType(val));\r\n        }\r\n\t   }\r\n\t  }\r\n   } \r\n\r\n   public static int getObjectToType(Object ob){\r\n\t\tif (ob instanceof Integer) {\r\n\t\t\treturn Types.INTEGER;\r\n\t\t}\r\n\t\telse if (ob instanceof Boolean) {\r\n\t\t\treturn Types.BOOLEAN;\r\n\t\t}\r\n\t\telse if (ob instanceof Byte) {\r\n\t\t\treturn Types.BIT;\r\n\t\t}\t\r\n\t\telse if (ob instanceof Short) {\r\n\t\t\treturn Types.INTEGER;\r\n\t\t}\t\r\n\t\telse if (ob instanceof Float) {\r\n\t\t\treturn Types.FLOAT;\r\n\t\t}\t\t\t\r\n\t\telse if (ob instanceof Long) {\r\n\t\t\treturn Types.BIGINT;\r\n\t\t}\r\n\t\telse if (ob instanceof Double) {\r\n\t\t\treturn Types.DOUBLE;\r\n\t\t}\t\t\t\r\n\t\telse if (ob instanceof Date) {\r\n\t\t\treturn Types.DATE;\r\n\t\t}\t\r\n\t\telse if (ob instanceof Time) {\r\n\t\t\treturn Types.TIME;\r\n\t\t}\t\r\n\t\telse if (ob instanceof Timestamp) {\r\n\t\t\treturn Types.TIMESTAMP;\r\n\t\t}\r\n\t\telse if (ob instanceof String) {\r\n\t\t\treturn Types.VARCHAR;\r\n\t\t}\t\t\t\r\n\t\telse  {\r\n\t\t\treturn Types.VARCHAR;\r\n\t\t}\t   \r\n   }\r\n      \r\n   public void setField(String field,int ftype)  {\r\n\t   map.put(field, ftype);\r\n   } \r\n   \r\n   public HashMap<String,Integer> getFields()  {\r\n\t   return this.map;\r\n   } \r\n   \r\n   public boolean getType() {\r\n\t  return this.type;\r\n   }  \r\n   \r\n   public DBCursor getCursor() {\r\n\t  return this.cursor;\r\n   }  \r\n  \r\n   public DBCursor setCursor(DBCursor cursor)  {\r\n\t   return this.cursor=cursor;\t\t\r\n   }    \r\n   \r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/sequoiadb/SequoiaDriver.java",
    "content": "package io.mycat.backend.jdbc.sequoiadb;\n\nimport java.sql.Connection;\nimport java.sql.Driver;\nimport java.sql.DriverManager;\nimport java.sql.DriverPropertyInfo;\nimport java.sql.SQLException;\nimport java.sql.SQLFeatureNotSupportedException;\nimport java.util.Properties;\nimport java.util.logging.Logger;\n\nimport org.slf4j.LoggerFactory;\n\n/**  \n * 功能详细描述\n * @author sohudo[http://blog.csdn.net/wind520]\n * @create 2014年12月19日 下午6:50:23 \n * @version 0.0.1\n */\npublic class SequoiaDriver implements Driver\n\n{\n    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(SequoiaDriver.class);\n    static final String PREFIX = \"sequoiadb://\";\n    private DriverPropertyInfoHelper propertyInfoHelper = new DriverPropertyInfoHelper();\n    \n\tstatic{\n\t\ttry{\n\t\t\tDriverManager.registerDriver(new SequoiaDriver());\n\t\t}catch (SQLException e){\n\t\t    LOGGER.error(\"initError\",e);\n\t\t}\n\t}\n\n\n\t@Override\n\tpublic Connection connect(String url, Properties info) throws SQLException {\n\t\tif (url == null) {\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tif (!StringUtils.startsWithIgnoreCase(url, PREFIX)) {\t\n\t\t\treturn null;//throw new IllegalArgumentException(\"uri needs to start with \" + PREFIX);//return null;\n\t\t}\n\t\tString uri=url;\n        uri = uri.substring(PREFIX.length());\n\n        String serverPart;\n        String nsPart;\n        String optionsPart;\n        {\n            int idx = uri.lastIndexOf(\"/\");\n            if (idx < 0) {\n                if (uri.contains(\"?\")) {\n                    throw new IllegalArgumentException(\"URI contains options without trailing slash\");\n                }\n                serverPart = uri;\n                nsPart = null;\n                optionsPart = \"\";\n            } else {\n                serverPart = uri.substring(0, idx);\n                nsPart = uri.substring(idx + 1);\n\n                idx = nsPart.indexOf(\"?\");\n                if (idx >= 0) {\n                    optionsPart = nsPart.substring(idx + 1);\n                    nsPart = nsPart.substring(0, idx);\n                } else {\n                    optionsPart = \"\";\n                }\n\n            }\n        }\t\t\n\t\tSequoiaConnection result = null;\n\t\t//System.out.print(info);\n\t\ttry{\n\t\t\tresult = new SequoiaConnection(serverPart, nsPart);\n\t\t}catch (Exception e){\n\t\t\tthrow new SQLException(\"Unexpected exception: \" + e.getMessage(), e);\n\t\t}\n\t\t\n\t\treturn result;\n\t}\n\t\n\n\t\n\t@Override\n\tpublic boolean acceptsURL(String url) throws SQLException {\n\t\tif (!StringUtils.startsWithIgnoreCase(url, PREFIX)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t@Override\n\tpublic DriverPropertyInfo[] getPropertyInfo(String url, Properties info)\n\t\t\tthrows SQLException {\n\n\t\treturn propertyInfoHelper.getPropertyInfo();\n\t}\t\n\t\n\n\t@Override\n\tpublic int getMajorVersion() {\n\t\treturn 1;\n\t}\n\n\t@Override\n\tpublic int getMinorVersion() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean jdbcCompliant() {\n\t\treturn true;\n\t}\n\t@Override  \n\tpublic Logger getParentLogger() throws SQLFeatureNotSupportedException{\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/sequoiadb/SequoiaPreparedStatement.java",
    "content": "package io.mycat.backend.jdbc.sequoiadb;\r\n\r\nimport java.io.InputStream;\r\nimport java.io.Reader;\r\nimport java.math.BigDecimal;\r\nimport java.net.URL;\r\nimport java.sql.Array;\r\nimport java.sql.Blob;\r\nimport java.sql.Clob;\r\nimport java.sql.Date;\r\nimport java.sql.NClob;\r\nimport java.sql.ParameterMetaData;\r\nimport java.sql.PreparedStatement;\r\nimport java.sql.Ref;\r\nimport java.sql.ResultSet;\r\nimport java.sql.ResultSetMetaData;\r\nimport java.sql.RowId;\r\nimport java.sql.SQLException;\r\nimport java.sql.SQLXML;\r\nimport java.sql.Time;\r\nimport java.sql.Timestamp;\r\nimport java.util.ArrayList;\r\nimport java.util.Calendar;\r\nimport java.util.List;\r\n/**  \r\n * 功能详细描述\r\n * @author sohudo[http://blog.csdn.net/wind520]\r\n * @create 2014年12月19日 下午6:50:23 \r\n * @version 0.0.1\r\n */\r\npublic class SequoiaPreparedStatement extends SequoiaStatement implements\r\n\t\tPreparedStatement {\r\n\tfinal String _sql;\r\n\tfinal SequoiaSQLParser _mongosql;\r\n\tList _params = new ArrayList();\r\n\r\n\tpublic SequoiaPreparedStatement(SequoiaConnection conn, int type,\r\n\t\t\tint concurrency, int holdability, String sql)\r\n\t\t\tthrows SequoiaSQLException {\r\n\t\tsuper(conn, type, concurrency, holdability);\r\n\t\tthis._sql = sql;\r\n\t\tthis._mongosql = new SequoiaSQLParser(conn.getDB(), sql);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic ResultSet executeQuery() throws SQLException {\r\n\t\t\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int executeUpdate() throws SQLException {\r\n\t\t\r\n\t    this._mongosql.setParams(this._params);\r\n\t    return this._mongosql.executeUpdate();\r\n\t}\r\n\t\r\n\tpublic  void setValue(int idx, Object o) {\t  \r\n\t    while (this._params.size() <= idx) {\r\n\t\t\tthis._params.add(null);\r\n\t\t}\r\n\t    this._params.set(idx, o);\r\n\t }\r\n\t  \r\n\t@Override\r\n\tpublic void setNull(int parameterIndex, int sqlType) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBoolean(int parameterIndex, boolean x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Boolean.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setByte(int parameterIndex, byte x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Byte.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setShort(int parameterIndex, short x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Short.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setInt(int parameterIndex, int x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Integer.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setLong(int parameterIndex, long x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Long.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setFloat(int parameterIndex, float x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Float.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setDouble(int parameterIndex, double x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, Double.valueOf(x));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBigDecimal(int parameterIndex, BigDecimal x)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setString(int parameterIndex, String x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBytes(int parameterIndex, byte[] x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setDate(int parameterIndex, Date x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setTime(int parameterIndex, Time x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setTimestamp(int parameterIndex, Timestamp x)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex, x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setAsciiStream(int parameterIndex, InputStream x, int length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setUnicodeStream(int parameterIndex, InputStream x, int length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBinaryStream(int parameterIndex, InputStream x, int length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void clearParameters() throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setObject(int parameterIndex, Object x, int targetSqlType)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setObject(int parameterIndex, Object x) throws SQLException {\r\n\t\t\r\n\t\tsetValue(parameterIndex,x);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean execute() throws SQLException {\r\n\t\t\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void addBatch() throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setCharacterStream(int parameterIndex, Reader reader, int length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setRef(int parameterIndex, Ref x) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBlob(int parameterIndex, Blob x) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setClob(int parameterIndex, Clob x) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setArray(int parameterIndex, Array x) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic ResultSetMetaData getMetaData() throws SQLException {\r\n\t\t\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setDate(int parameterIndex, Date x, Calendar cal)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setTime(int parameterIndex, Time x, Calendar cal)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setTimestamp(int parameterIndex, Timestamp x, Calendar cal)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNull(int parameterIndex, int sqlType, String typeName)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setURL(int parameterIndex, URL x) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic ParameterMetaData getParameterMetaData() throws SQLException {\r\n\t\t\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setRowId(int parameterIndex, RowId x) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNString(int parameterIndex, String value)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNCharacterStream(int parameterIndex, Reader value,\r\n\t\t\tlong length) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNClob(int parameterIndex, NClob value) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setClob(int parameterIndex, Reader reader, long length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBlob(int parameterIndex, InputStream inputStream, long length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNClob(int parameterIndex, Reader reader, long length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setSQLXML(int parameterIndex, SQLXML xmlObject)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setObject(int parameterIndex, Object x, int targetSqlType,\r\n\t\t\tint scaleOrLength) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setAsciiStream(int parameterIndex, InputStream x, long length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBinaryStream(int parameterIndex, InputStream x, long length)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setCharacterStream(int parameterIndex, Reader reader,\r\n\t\t\tlong length) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setAsciiStream(int parameterIndex, InputStream x)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBinaryStream(int parameterIndex, InputStream x)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setCharacterStream(int parameterIndex, Reader reader)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNCharacterStream(int parameterIndex, Reader value)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setClob(int parameterIndex, Reader reader) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setBlob(int parameterIndex, InputStream inputStream)\r\n\t\t\tthrows SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setNClob(int parameterIndex, Reader reader) throws SQLException {\r\n\t\t\r\n\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/sequoiadb/SequoiaResultSet.java",
    "content": "package io.mycat.backend.jdbc.sequoiadb;\n\n\nimport com.sequoiadb.base.DBCursor;\nimport org.bson.BSONObject;\nimport org.bson.types.BasicBSONList;\n\nimport java.io.InputStream;\nimport java.io.Reader;\nimport java.math.BigDecimal;\n//import java.net.MalformedURLException;\nimport java.net.URL;\nimport java.sql.Array;\nimport java.sql.Blob;\nimport java.sql.Clob;\nimport java.sql.Date;\nimport java.sql.NClob;\nimport java.sql.Ref;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.RowId;\nimport java.sql.SQLException;\nimport java.sql.SQLWarning;\nimport java.sql.SQLXML;\nimport java.sql.Statement;\nimport java.sql.Time;\nimport java.sql.Timestamp;\nimport java.sql.Types;\nimport java.util.Calendar;\nimport java.util.HashMap;\n//import java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n/**  \n * 功能详细描述\n * @author sohudo[http://blog.csdn.net/wind520]\n * @create 2014年12月19日 下午6:50:23 \n * @version 0.0.1\n */\npublic class SequoiaResultSet implements ResultSet\n{\n\t private final DBCursor _cursor;\n\t private BSONObject _cur;//DBObject _cur;\n\t private int _row = 0;\n\t private boolean _closed = false;\n\t private String[] select;\n\t private int[] fieldtype;\n\t private String _schema;\n\t private String _table;\n\t //支持聚合,包括count,group by \n\t private boolean isSum=false;\n\t //是group by\n\t private boolean isGroupBy=false;\n\t private long _sum=0;\n\t private BasicBSONList dblist;\n\t \n\tpublic SequoiaResultSet(SequoiaData mongo,String schema) throws SQLException {\n\t    this._cursor = mongo.getCursor();\n\t    this._schema = schema;\n\t    this._table  = mongo.getTable();\n\t    this.isSum   = mongo.getCount()>0;\n\t    this._sum    = mongo.getCount();\n\t    this.isGroupBy= mongo.getType();\n\t    \n\t    if (this.isGroupBy) {\n\t    \tdblist  = mongo.getGrouyBys();\n\t    \tthis.isSum =true;\n\t    }\n\t    if (this._cursor!=null) {\n\t     // select = (String[]) _cursor.getKeysWanted().keySet().toArray(new String[0]);\n\t    \n\t      if ( this._cursor.hasNext()){\n\t        _cur= _cursor.getNext(); \n\t        if (_cur!=null) {\n\t           if (select==null) {\n\t    \t      SetFields(_cur.keySet());\n\t           }\t    \t   \n\t          _row=1;\n\t         }\n\t      }  \n\t   \n\t     if (select==null){\n\t\t   select =new String[]{\"_id\"};\n\t\t   SetFieldType(true);\n\t    }\n\t    else {\n\t\t    SetFieldType(false);\n\t    }\n\t  }\n\t  else{\n\t\t  SetFields(mongo.getFields().keySet());//new String[]{\"COUNT(*)\"};\t\n\t\t  SetFieldType(mongo.getFields());\n\t    }\n\t}\n\t\n\tpublic void SetFields(Set<String> keySet) {\t\t\n\t\tthis.select = new String[keySet.size()];\n\t\tthis.select = keySet.toArray(this.select);\n\n\t}\n\tpublic void SetFieldType(boolean isid) throws SQLException {\n\t\tif (isid) {\n\t\t  fieldtype= new int[Types.VARCHAR];\n\t\t}\n\t\telse {\n\t\t\tfieldtype = new int[this.select.length];\n\t\t}\n\t\t\n\t\tif (_cur!=null) {\n\t\t  for (int i=0;i<this.select.length;i++){\n\t\t\tObject ob=this.getObject(i+1);\n\t\t\tfieldtype[i]=SequoiaData.getObjectToType(ob);\n\t\t  }\t\n\t\t}\n\t}\t\n\t\n\tpublic void SetFieldType(HashMap<String,Integer> map) throws SQLException {\n        fieldtype= new int[this.select.length];\n\t\t  for (int i=0;i<this.select.length;i++){\n\t\t\tString ob=map.get(select[i]).toString();\n\t\t\tfieldtype[i]=Integer.parseInt(ob);\n\t\t  }\t\n\n\t}\t\n\t@Override\n\tpublic <T> T unwrap(Class<T> iface) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isWrapperFor(Class<?> iface) throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean next() throws SQLException {\n\t\t\n\t\tif ( isSum){\t\n\t\t\tif (isGroupBy){\n\t\t\t\t_row++;\n\t\t\t\tif (_row<=dblist.size()) {\n\t\t\t\t   return true;\n\t\t\t\t}\n\t\t    \telse {\n\t\t    \t  return false;\t\t\t\t\n\t\t    \t}\n\t\t\t}\n\t\t\telse {\n\t\t\t  if (_row==1) {\n\t\t\t\t  return false;\n\t\t\t  }\n\t\t\t  else {\n\t\t\t\t_row++;\n\t\t\t    return true;\n\t\t\t  }\n\t\t    }\n\t\t}\n\t\telse {\n\t\t\tif (! this._cursor.hasNext()) {\n\t    \t   if (_row==1) {\n\t    \t\t  _row++;\t\n\t    \t\t  return true;\n\t    \t   }\n\t    \t   else {\n\t\t\t\t   return false;\n\t\t\t   }\n\t        }\n\t        else {\n\t           if (_row!=1){\n\t    \t     this._cur = this._cursor.getNext();\n\t           }  \n\t           _row++;\n\t           return true;\n\t        }\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close() throws SQLException {\n\t\t\n\t\tthis._closed = true;\n\t}\n\t\n\tpublic String getField(int columnIndex){\n\t   return select[columnIndex-1];\t\n\t}\n\t\n\t@Override\n\tpublic boolean wasNull() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getString(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getString(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic boolean getBoolean(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getBoolean(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic byte getByte(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getByte(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic short getShort(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getShort(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic int getInt(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getInt(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic long getLong(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getLong(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic float getFloat(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getFloat(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic double getDouble(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getDouble(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic BigDecimal getBigDecimal(int columnIndex, int scale)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn getBigDecimal(getField(columnIndex),scale);\n\t}\n\n\t@Override\n\tpublic byte[] getBytes(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getBytes(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic Date getDate(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getDate(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic Time getTime(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getTime(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic Timestamp getTimestamp(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getTimestamp(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic InputStream getAsciiStream(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getAsciiStream(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic InputStream getUnicodeStream(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getUnicodeStream(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic InputStream getBinaryStream(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getBinaryStream(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic String getString(String columnLabel) throws SQLException {\n\t\t\n\t\tObject x = getObject(columnLabel);\n\t\tif (x == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn x.toString();\n\t}\n\n\t@Override\n\tpublic boolean getBoolean(String columnLabel) throws SQLException {\n\t\t\n\t\t//return false;\n\t\tObject x = getObject(columnLabel);\n\t\tif (x == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn ((Boolean)x).booleanValue();\n\t}\n\t\n\tpublic Number getNumber(String columnLabel)\n\t {\n\t   Number x = (Number)this._cur.get(columnLabel);\n\t   if (x == null) {\n\t\t   return Integer.valueOf(0);\n\t   }\n\t   return x;\n\t }\n\t \n\t@Override\n\tpublic byte getByte(String columnLabel) throws SQLException {\n\t\t\n\t\treturn getNumber(columnLabel).byteValue();\n\t}\n\n\t@Override\n\tpublic short getShort(String columnLabel) throws SQLException {\n\t\t\n\t\treturn getNumber(columnLabel).shortValue();\n\t}\n\n\t@Override\n\tpublic int getInt(String columnLabel) throws SQLException {\n\t\t\n\t\treturn getNumber(columnLabel).intValue();\n\t}\n\n\t@Override\n\tpublic long getLong(String columnLabel) throws SQLException {\n\t\t\n\t\treturn getNumber(columnLabel).longValue();\n\t}\n\n\t@Override\n\tpublic float getFloat(String columnLabel) throws SQLException {\n\t\t\n\t\treturn getNumber(columnLabel).floatValue();\n\t}\n\n\t@Override\n\tpublic double getDouble(String columnLabel) throws SQLException {\n\t\t\n\t\treturn getNumber(columnLabel).doubleValue();\n\t}\n\n\t@Override\n\tpublic BigDecimal getBigDecimal(String columnLabel, int scale)\n\t\t\tthrows SQLException {\n\t\t\n\t\tthrow new UnsupportedOperationException();//return null;\n\t}\n\n\t@Override\n\tpublic byte[] getBytes(String columnLabel) throws SQLException {\n\t\t\n\t\treturn (byte[])getObject(columnLabel);\n\t}\n\n\t@Override\n\tpublic Date getDate(String columnLabel) throws SQLException {\n\t\t\n\t\treturn (Date)getObject(columnLabel);\n\t}\n\n\t@Override\n\tpublic Time getTime(String columnLabel) throws SQLException {\n\t\t\n\t\treturn (Time)getObject(columnLabel);\n\t}\n\n\t@Override\n\tpublic Timestamp getTimestamp(String columnLabel) throws SQLException {\n\t\t\n\t\treturn (Timestamp)getObject(columnLabel);//throw new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic InputStream getAsciiStream(String columnLabel) throws SQLException {\n\t\t\n\t\tthrow new UnsupportedOperationException();//return null;\n\t}\n\n\t@Override\n\tpublic InputStream getUnicodeStream(String columnLabel) throws SQLException {\n\t\t\n\t\tthrow new UnsupportedOperationException();//return null;\n\t}\n\n\t@Override\n\tpublic InputStream getBinaryStream(String columnLabel) throws SQLException {\n\t\t\n\t\tthrow new UnsupportedOperationException();//return null;\n\t}\n\n\t@Override\n\tpublic SQLWarning getWarnings() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void clearWarnings() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic String getCursorName() throws SQLException {\n\t\t\n\t\treturn this._cursor.toString();\n\t}\n\n\t@Override\n\tpublic ResultSetMetaData getMetaData() throws SQLException {\n\t\t\n\t\treturn new SequoiaResultSetMetaData(select,fieldtype,this._schema,this._table);\n\t\t/*\n\t \tif(_cur !=null){\n\t \t\treturn new MongoResultSetMetaData(_cur.keySet(),this._schema);  \n\t     }\n\t \t else{ \t\t\n\t \t\treturn new MongoResultSetMetaData(select,this._schema); \n\t     } \t\t\n\t     */\n\t}\n\n\t@Override\n\tpublic Object getObject(int columnIndex) throws SQLException {\n\t\t\n\t\t if (columnIndex == 0){\n\t\t\t if (isSum) {\n\t\t\t\t return getObject(getField(1)); \n\t\t\t }\n\t\t\t else {\n\t\t\t\t return this._cur;\n\t\t\t }\n\t\t }\n\t\telse {\n\t\t\t return getObject(getField(columnIndex));\n\t\t }\n\t}\n\n\t@Override\n\tpublic Object getObject(String columnLabel) throws SQLException {\n\t\t\n\t\tif (isSum) {\n\t\t   if (isGroupBy){\n\t\t\t  Object ob=dblist.get(_row-1);\n\t\t\t  if (ob instanceof BSONObject) {\n\t\t\t\t  return ((BSONObject)ob).get(columnLabel);\n\t\t\t  }\n\t\t\t  else {\n\t\t\t\t  return \"0\";  \n\t\t\t  }\n\t\t   }\n\t\t   else{\n\t\t\t   return  this._sum;\n\t\t   }\n\t\t}\n\t\telse {\n\t\t\treturn this._cur.get(columnLabel);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int findColumn(String columnLabel) throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic Reader getCharacterStream(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getCharacterStream(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic Reader getCharacterStream(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic BigDecimal getBigDecimal(int columnIndex) throws SQLException {\n\t\t\n\t\treturn getBigDecimal(getField(columnIndex));\n\t}\n\n\t@Override\n\tpublic BigDecimal getBigDecimal(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isBeforeFirst() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isAfterLast() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isFirst() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isLast() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void beforeFirst() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void afterLast() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic boolean first() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean last() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int getRow() throws SQLException {\n\t\t\n\t\treturn 0;//this._cursor.count();\n\t}\n\n\t@Override\n\tpublic boolean absolute(int row) throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean relative(int rows) throws SQLException {\n\t\t// 按相对行数（或正或负）移动光标。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean previous() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setFetchDirection(int direction) throws SQLException {\n\t\t\n//\t\tif (direction == getFetchDirection())\n//\t\t     return;\n\t}\n\n\t@Override\n\tpublic int getFetchDirection() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void setFetchSize(int rows) throws SQLException {\n\t\t// 设置此 ResultSet 对象需要更多行时应该从数据库获取的行数。\n\t\t\n\t}\n\n\t@Override\n\tpublic int getFetchSize() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getType() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getConcurrency() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean rowUpdated() throws SQLException {\n\t\t// 获取是否已更新当前行。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean rowInserted() throws SQLException {\n\t\t// 获取当前行是否已有插入。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean rowDeleted() throws SQLException {\n\t\t//获取是否已删除某行。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void updateNull(int columnIndex) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBoolean(int columnIndex, boolean x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateByte(int columnIndex, byte x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateShort(int columnIndex, short x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateInt(int columnIndex, int x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateLong(int columnIndex, long x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateFloat(int columnIndex, float x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateDouble(int columnIndex, double x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBigDecimal(int columnIndex, BigDecimal x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateString(int columnIndex, String x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBytes(int columnIndex, byte[] x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateDate(int columnIndex, Date x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateTime(int columnIndex, Time x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateTimestamp(int columnIndex, Timestamp x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateAsciiStream(int columnIndex, InputStream x, int length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBinaryStream(int columnIndex, InputStream x, int length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateCharacterStream(int columnIndex, Reader x, int length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateObject(int columnIndex, Object x, int scaleOrLength)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateObject(int columnIndex, Object x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNull(String columnLabel) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBoolean(String columnLabel, boolean x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateByte(String columnLabel, byte x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateShort(String columnLabel, short x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateInt(String columnLabel, int x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateLong(String columnLabel, long x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateFloat(String columnLabel, float x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateDouble(String columnLabel, double x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBigDecimal(String columnLabel, BigDecimal x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateString(String columnLabel, String x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBytes(String columnLabel, byte[] x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateDate(String columnLabel, Date x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateTime(String columnLabel, Time x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateTimestamp(String columnLabel, Timestamp x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateAsciiStream(String columnLabel, InputStream x, int length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBinaryStream(String columnLabel, InputStream x, int length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateCharacterStream(String columnLabel, Reader reader,\n\t\t\tint length) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateObject(String columnLabel, Object x, int scaleOrLength)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateObject(String columnLabel, Object x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void insertRow() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateRow() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void deleteRow() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void refreshRow() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void cancelRowUpdates() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void moveToInsertRow() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void moveToCurrentRow() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic Statement getStatement() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Object getObject(int columnIndex, Map<String, Class<?>> map)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Ref getRef(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Blob getBlob(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Clob getClob(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Array getArray(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;//getArray(_find(i));\n\t}\n\n\t@Override\n\tpublic Object getObject(String columnLabel, Map<String, Class<?>> map)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Ref getRef(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Blob getBlob(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Clob getClob(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Array getArray(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Date getDate(int columnIndex, Calendar cal) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Date getDate(String columnLabel, Calendar cal) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Time getTime(int columnIndex, Calendar cal) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Time getTime(String columnLabel, Calendar cal) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Timestamp getTimestamp(int columnIndex, Calendar cal)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Timestamp getTimestamp(String columnLabel, Calendar cal)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic URL getURL(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic URL getURL(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void updateRef(int columnIndex, Ref x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateRef(String columnLabel, Ref x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBlob(int columnIndex, Blob x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBlob(String columnLabel, Blob x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateClob(int columnIndex, Clob x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateClob(String columnLabel, Clob x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateArray(int columnIndex, Array x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateArray(String columnLabel, Array x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic RowId getRowId(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic RowId getRowId(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void updateRowId(int columnIndex, RowId x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateRowId(String columnLabel, RowId x) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic int getHoldability() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean isClosed() throws SQLException {\n\t\t\n\t\treturn this._closed;\n\t}\n\n\t@Override\n\tpublic void updateNString(int columnIndex, String nString)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNString(String columnLabel, String nString)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNClob(int columnIndex, NClob nClob) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNClob(String columnLabel, NClob nClob)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic NClob getNClob(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic NClob getNClob(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic SQLXML getSQLXML(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic SQLXML getSQLXML(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void updateSQLXML(int columnIndex, SQLXML xmlObject)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateSQLXML(String columnLabel, SQLXML xmlObject)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic String getNString(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getNString(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Reader getNCharacterStream(int columnIndex) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Reader getNCharacterStream(String columnLabel) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void updateNCharacterStream(int columnIndex, Reader x, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNCharacterStream(String columnLabel, Reader reader,\n\t\t\tlong length) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateAsciiStream(int columnIndex, InputStream x, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBinaryStream(int columnIndex, InputStream x, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateCharacterStream(int columnIndex, Reader x, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateAsciiStream(String columnLabel, InputStream x, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBinaryStream(String columnLabel, InputStream x,\n\t\t\tlong length) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateCharacterStream(String columnLabel, Reader reader,\n\t\t\tlong length) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBlob(int columnIndex, InputStream inputStream, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBlob(String columnLabel, InputStream inputStream,\n\t\t\tlong length) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateClob(int columnIndex, Reader reader, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateClob(String columnLabel, Reader reader, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNClob(int columnIndex, Reader reader, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNClob(String columnLabel, Reader reader, long length)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNCharacterStream(int columnIndex, Reader x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNCharacterStream(String columnLabel, Reader reader)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateAsciiStream(int columnIndex, InputStream x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBinaryStream(int columnIndex, InputStream x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateCharacterStream(int columnIndex, Reader x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateAsciiStream(String columnLabel, InputStream x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBinaryStream(String columnLabel, InputStream x)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateCharacterStream(String columnLabel, Reader reader)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBlob(int columnIndex, InputStream inputStream)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateBlob(String columnLabel, InputStream inputStream)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateClob(int columnIndex, Reader reader) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateClob(String columnLabel, Reader reader)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNClob(int columnIndex, Reader reader) throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void updateNClob(String columnLabel, Reader reader)\n\t\t\tthrows SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic <T> T getObject(int columnIndex, Class<T> type) throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic <T> T getObject(String columnLabel, Class<T> type)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn null;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/sequoiadb/SequoiaResultSetMetaData.java",
    "content": "package io.mycat.backend.jdbc.sequoiadb;\r\n\r\nimport java.sql.ResultSetMetaData;\r\nimport java.sql.SQLException;\r\nimport java.sql.Types;\r\n\r\n/**  \r\n * 功能详细描述\r\n * @author sohudo[http://blog.csdn.net/wind520]\r\n * @create 2014年12月19日 下午6:50:23 \r\n * @version 0.0.1\r\n */\r\n\r\npublic class SequoiaResultSetMetaData implements ResultSetMetaData {\r\n\t\r\n\tprivate String[] keySet ;\r\n\tprivate int[] keytype ;\r\n\tprivate String _schema;\r\n\tprivate String _table;\r\n\t\r\n\t/*\r\n\tpublic MongoResultSetMetaData(Set<String> keySet,String schema) {\r\n\t\tsuper();\r\n\t\tthis.keySet = new String[keySet.size()];\r\n\t\tthis.keySet = keySet.toArray(this.keySet);\r\n\t\tthis._schema = schema;\r\n\t}\r\n    */\r\n\tpublic SequoiaResultSetMetaData(String[] select,int [] ftype,String schema,String table) {\r\n\t\tsuper();\r\n\t\tthis.keySet = select;\r\n\t\tthis.keytype=ftype;\r\n\t\tthis._schema = schema;\r\n\t\tthis._table  =table;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic <T> T unwrap(Class<T> iface) throws SQLException {\r\n\t\t\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isWrapperFor(Class<?> iface) throws SQLException {\r\n\t\t\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getColumnCount() throws SQLException {\r\n\t\tif (keySet==null) {\r\n\t\t\treturn 0;\r\n\t\t}\r\n\t\telse {\r\n\t\t\treturn keySet.length;\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isAutoIncrement(int column) throws SQLException {\r\n\t\t// 是否为自动编号的字段\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isCaseSensitive(int column) throws SQLException {\r\n\t\t//指示列的大小写是否有关系\r\n\t\treturn true;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isSearchable(int column) throws SQLException {\r\n\t\t//指示是否可以在 where 子句中使用指定的列\r\n\t\treturn true;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isCurrency(int column) throws SQLException {\r\n\t\t// 指示指定的列是否是一个哈希代码值\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int isNullable(int column) throws SQLException {\r\n\t\t// 指示指定列中的值是否可以为 null。\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isSigned(int column) throws SQLException {\r\n\t\t// 指示指定列中的值是否带正负号\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getColumnDisplaySize(int column) throws SQLException {\r\n\t\t\r\n\t\treturn 50;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getColumnLabel(int column) throws SQLException {\r\n\t\treturn keySet[column-1];\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getColumnName(int column) throws SQLException {\r\n\t\treturn keySet[column-1];\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getSchemaName(int column) throws SQLException {\r\n\t\t\r\n\t\treturn this._schema;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getPrecision(int column) throws SQLException {\r\n\t\t//获取指定列的指定列宽\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getScale(int column) throws SQLException {\r\n\t\t// 检索指定参数的小数点右边的位数。\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getTableName(int column) throws SQLException {\r\n\t\t\r\n\t\treturn this._table;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getCatalogName(int column) throws SQLException {\r\n\t\t\r\n\t\treturn this._schema;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getColumnType(int column) throws SQLException {\r\n\t\t// 字段类型\r\n\t\treturn keytype[column-1];//Types.VARCHAR;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getColumnTypeName(int column) throws SQLException {\r\n\t\t// 数据库特定的类型名称\r\n\t\tswitch (keytype[column-1]){\r\n\t\tcase  Types.INTEGER: return \"INTEGER\";\r\n\t\tcase  Types.BOOLEAN:  return \"BOOLEAN\";\r\n\t\tcase  Types.BIT: return \"BITT\"; \r\n\t\tcase  Types.FLOAT: return \"FLOAT\";\r\n\t\tcase  Types.BIGINT: return \"BIGINT\";\r\n\t\tcase  Types.DOUBLE:  return \"DOUBLE\";\r\n\t\tcase  Types.DATE: return \"DATE\"; \r\n\t\tcase  Types.TIME: return \"TIME\";\r\n\t\tcase  Types.TIMESTAMP: return \"TIMESTAMP\";\r\n\t\tdefault: return \"varchar\";\r\n\t   }\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isReadOnly(int column) throws SQLException {\r\n\t\t//指示指定的列是否明确不可写入\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isWritable(int column) throws SQLException {\r\n\t\t\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isDefinitelyWritable(int column) throws SQLException {\r\n\t\t\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getColumnClassName(int column) throws SQLException {\r\n\t\t// 如果调用方法 ResultSet.getObject 从列中获取值，则返回构造其实例的 Java 类的完全限定名称\r\n\t\treturn \"Object\";\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/sequoiadb/SequoiaSQLException.java",
    "content": "package io.mycat.backend.jdbc.sequoiadb;\n\nimport java.sql.SQLException;\n\n@SuppressWarnings(\"serial\")\npublic class SequoiaSQLException extends SQLException\n{\n\n\tpublic SequoiaSQLException(String msg)\n    {\n        super(msg);\n    }\n\n    public static class ErrorSQL extends SequoiaSQLException\n    {\n\n\t\tErrorSQL(String sql)\n        {\n            super(sql);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/sequoiadb/SequoiaSQLParser.java",
    "content": "package io.mycat.backend.jdbc.sequoiadb;\r\n\r\n\r\n\r\nimport java.sql.Types;\r\nimport java.util.List;\r\n\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport com.sequoiadb.base.CollectionSpace;\r\nimport com.sequoiadb.base.DBCollection;\r\nimport com.sequoiadb.base.DBCursor;\r\n\r\nimport org.bson.BSONObject;\r\nimport org.bson.BasicBSONObject;\r\nimport org.bson.types.BasicBSONList;\r\n\r\nimport com.alibaba.druid.sql.ast.SQLExpr;\r\nimport com.alibaba.druid.sql.ast.SQLOrderingSpecification;\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\n\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\r\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\r\nimport com.alibaba.druid.sql.ast.statement.*;\r\nimport com.alibaba.druid.sql.ast.expr.*;\r\nimport com.alibaba.druid.sql.ast.*;\r\n/**  \r\n * 功能详细描述\r\n * @author sohudo[http://blog.csdn.net/wind520]\r\n * @create 2014年12月19日 下午6:50:23 \r\n * @version 0.0.1\r\n */\r\npublic class SequoiaSQLParser {\r\n    private static final Logger LOGGER = LoggerFactory.getLogger(SequoiaSQLParser.class);\r\n\tprivate   final CollectionSpace _db;\r\n//\tprivate   final String _sql;\r\n\tprivate   final SQLStatement statement;\t\r\n\tprivate   List _params;\r\n\tprivate   int _pos;\t\r\n\tpublic SequoiaSQLParser(CollectionSpace db, String sql)  throws SequoiaSQLException\r\n\t   {\r\n\t     this._db = db;\r\n\t //    this._sql = sql;\r\n\t     this.statement = parser(sql);\r\n\t   }\r\n\t\r\n\tpublic SQLStatement parser(String s) throws SequoiaSQLException\r\n\t   {\r\n\t     s = s.trim();\r\n\t     try\r\n\t     {\r\n\t        MySqlStatementParser parser = new MySqlStatementParser(s);\r\n\t        return parser.parseStatement();\r\n\t     }\r\n\t     catch (Exception e)\r\n\t     {\r\n\t         LOGGER.error(\"MongoSQLParser.parserError\", e);\r\n\t    }\r\n\t     throw new SequoiaSQLException.ErrorSQL(s);\r\n\t   }\t\r\n\t\r\n\tpublic  void setParams(List params)\r\n\t   {\r\n\t     this._pos = 1;\r\n\t     this._params = params;\r\n\t   }\r\n\t   \r\n\tpublic SequoiaData query() throws SequoiaSQLException{\r\n        if (!(statement instanceof SQLSelectStatement)) {\r\n        \t//return null;\r\n        \tthrow new IllegalArgumentException(\"not a query sql statement\");\r\n        }\r\n        SequoiaData mongo=new SequoiaData();\r\n        DBCursor c=null;\r\n        SQLSelectStatement selectStmt = (SQLSelectStatement)statement;\r\n        SQLSelectQuery sqlSelectQuery =selectStmt.getSelect().getQuery();\t\r\n        int icount=0;\r\n\t\tif(sqlSelectQuery instanceof MySqlSelectQueryBlock) {\r\n\t\t\tMySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock)selectStmt.getSelect().getQuery();\r\n\t\t\t\r\n\t\t\tBasicBSONObject fields = new BasicBSONObject();\r\n\t\t\t//显示的字段\r\n\t\t\tfor(SQLSelectItem item : mysqlSelectQuery.getSelectList()) {\r\n\t\t\t\t//System.out.println(item.toString());\r\n\t\t\t\tif (!(item.getExpr() instanceof SQLAllColumnExpr)) {\r\n\t\t\t\t\tif (item.getExpr() instanceof SQLAggregateExpr) {\r\n\t\t\t\t\t\tSQLAggregateExpr expr =(SQLAggregateExpr)item.getExpr();\r\n\t\t\t\t\t\tif (expr.getMethodName().equals(\"COUNT\")) {\r\n\t\t\t\t\t\t   icount=1;\r\n\t\t\t\t\t\t   mongo.setField(getExprFieldName(expr), Types.BIGINT);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tfields.put(getExprFieldName(expr), Integer.valueOf(1));\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse {\t\t\t\t\t\r\n\t\t\t\t\t   fields.put(getFieldName(item), Integer.valueOf(1));\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t}\t\r\n\t\t\t\r\n\t\t\t//表名\r\n\t\t\tSQLTableSource table=mysqlSelectQuery.getFrom();\r\n\t\t\tDBCollection coll =this._db.getCollection(table.toString());\r\n\t\t\tmongo.setTable(table.toString());\r\n\t\t\t\r\n\t\t\tSQLExpr expr=mysqlSelectQuery.getWhere();\t\r\n\t\t\tBSONObject query = parserWhere(expr);\r\n\t\t\t//System.out.println(query);\r\n\t\t\tSQLSelectGroupByClause groupby=mysqlSelectQuery.getGroupBy();\r\n\t\t\tBasicBSONObject gbkey = new BasicBSONObject();\r\n\t\t\tif (groupby!=null) {\r\n\t\t\t  for (SQLExpr gbexpr:groupby.getItems()){\r\n\t\t\t\tif (gbexpr instanceof SQLIdentifierExpr) {\r\n\t\t\t\t\tString name =((SQLIdentifierExpr) gbexpr).getName();\r\n\t\t\t\t\tgbkey.put(name, Integer.valueOf(1));\r\n\t\t\t\t}\r\n\t\t\t  }\r\n\t\t\t  icount=2;\r\n\t\t\t}\t\r\n\t\t\tint limitoff=0;\r\n\t\t\tint limitnum=0;\r\n\t\t\tif (mysqlSelectQuery.getLimit()!=null) {\r\n\t\t\t  limitoff=getSQLExprToInt(mysqlSelectQuery.getLimit().getOffset());\t\t\t\r\n\t\t\t  limitnum=getSQLExprToInt(mysqlSelectQuery.getLimit().getRowCount());\r\n\t\t\t}\t\r\n\t\t\t\r\n\t\t\t  SQLOrderBy orderby=mysqlSelectQuery.getOrderBy();\r\n\t\t\t  BasicBSONObject order = new BasicBSONObject();\r\n\t\t\t  if (orderby != null ){\t\t\t\t\r\n\t\t\t    for (int i = 0; i < orderby.getItems().size(); i++)\r\n\t\t\t    {\r\n\t\t\t    \tSQLSelectOrderByItem orderitem = orderby.getItems().get(i);\r\n\t\t\t       order.put(orderitem.getExpr().toString(), Integer.valueOf(getSQLExprToAsc(orderitem.getType())));\r\n\t\t\t    }\r\n\t\t\t  //  c.sort(order); \r\n\t\t\t   // System.out.println(order);\r\n\t\t\t  }\r\n\t\t\t  \r\n\t\t\tif (icount==1) {\r\n\t\t\t\tmongo.setCount(coll.getCount(query));\t\t\t\t\t\t\r\n\t\t\t}\r\n\t\t\telse if (icount==2){\r\n\t\t\t\tBasicBSONObject initial = new BasicBSONObject();\r\n\t\t\t\tinitial.put(\"num\", 0);\r\n\t\t\t\tString reduce=\"function (obj, prev) { \"\r\n\t\t\t\t\t\t+\"  prev.num++}\";\r\n\t\t\t\t//mongo.setGrouyBy(coll.group(gbkey, query, initial, reduce));\t\t\t\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t  if ((limitoff>0) || (limitnum>0)) {\r\n\t\t\t    c = coll.query(query, fields, order,null, limitoff, limitnum);//.skip(limitoff).limit(limitnum);\r\n\t\t\t  }\r\n\t\t\t  else {\r\n\t\t\t   c = coll.query(query, fields, order,null, 0, -1);\r\n\t\t\t  }\r\n\t\t\t\r\n\r\n\t\t   }\r\n\t\t   mongo.setCursor(c);\r\n\t\t}\r\n\t\treturn  mongo;\t\t\r\n\t}\r\n\t\r\n\tpublic int executeUpdate() throws SequoiaSQLException {\r\n        if (statement instanceof SQLInsertStatement) {\r\n        \treturn InsertData((SQLInsertStatement)statement);\r\n        }\t\r\n        if (statement instanceof SQLUpdateStatement) {\r\n        \treturn UpData((SQLUpdateStatement)statement);\r\n        }\r\n        if (statement instanceof SQLDropTableStatement) {\r\n        \treturn dropTable((SQLDropTableStatement)statement);\r\n        }\r\n        if (statement instanceof SQLDeleteStatement) {\r\n        \treturn DeleteDate((SQLDeleteStatement)statement);\r\n        }\r\n        if (statement instanceof SQLCreateTableStatement) {\r\n        \treturn createTable((SQLCreateTableStatement)statement);\r\n        }          \r\n\t\treturn 1;\r\n\t\t\r\n\t}\r\n\tprivate int InsertData(SQLInsertStatement state) {\r\n\t\tif (state.getValues().getValues().size() ==0 ){\r\n\t\t\tthrow new RuntimeException(\"number of  columns error\");\r\n\t\t}\t\t\r\n\t\tif (state.getValues().getValues().size() != state.getColumns().size()){\r\n\t\t\tthrow new RuntimeException(\"number of values and columns have to match\");\r\n\t\t}\r\n\t\tSQLTableSource table=state.getTableSource();\r\n\t\tBSONObject o = new BasicBSONObject();\r\n\t\tint i=0;\r\n\t\tfor(SQLExpr col : state.getColumns()) {\r\n\t\t\to.put(getFieldName2(col), getExpValue(state.getValues().getValues().get(i)));\r\n\t\t\ti++;\r\n\t\t}\t\t\r\n\t\tDBCollection coll =this._db.getCollection(table.toString());\r\n\t\t//coll.insert(new DBObject[] { o });\r\n\t\tcoll.insert(o);\r\n\t\treturn 1;\r\n\t}\r\n\tprivate int UpData(SQLUpdateStatement state) {\r\n\t\tSQLTableSource table=state.getTableSource();\r\n\t\tDBCollection coll =this._db.getCollection(table.toString());\r\n\t\t\r\n\t\tSQLExpr expr=state.getWhere();\r\n\t\tBSONObject query = parserWhere(expr);\r\n\t\t\r\n\t\tBasicBSONObject set = new BasicBSONObject();\r\n\t\tfor(SQLUpdateSetItem col : state.getItems()){\r\n\t\t\tset.put(getFieldName2(col.getColumn()), getExpValue(col.getValue()));\t\r\n\t\t}\r\n\t\tBSONObject mod = new BasicBSONObject(\"$set\", set);\r\n\t\t//coll.updateMulti(query, mod);\r\n\t\tcoll.update(query, mod, null);\r\n\t\t//System.out.println(\"changs count:\"+coll.getStats().size());\r\n\t\treturn 1;\t\t\r\n\t}\r\n\tprivate int DeleteDate(SQLDeleteStatement state) {\r\n\t\tSQLTableSource table=state.getTableSource();\r\n\t\tDBCollection coll =this._db.getCollection(table.toString());\r\n\t\t\r\n\t\tSQLExpr expr=state.getWhere();\r\n\t\tif (expr==null) {\r\n\t\t\tthrow new RuntimeException(\"not where of sql\");\r\n\t\t}\r\n\t\tBSONObject query = parserWhere(expr);\r\n\t\t\r\n\t\t//coll.remove(query);\r\n\t\tcoll.delete(query);\r\n\t\treturn 1;\r\n\t\t\r\n\t}\r\n\tprivate int dropTable(SQLDropTableStatement state) {\t\t\r\n\t\tfor (SQLTableSource table : state.getTableSources()){\r\n\t\t\t//DBCollection coll =this._db.getCollection(table.toString());\r\n\t\t\t//coll.drop();\r\n\t\t\tthis._db.dropCollection(table.toString());\r\n\t\t}\r\n\t\treturn 1;\r\n\t\t\r\n\t}\r\n\t\r\n\tprivate int createTable(SQLCreateTableStatement state) {\t\t\r\n\t\t//for (SQLTableSource table : state.getTableSource()){\r\n\t\t\tif (!this._db.isCollectionExist(state.getTableSource().toString())) {\r\n\t\t\t\tthis._db.createCollection(state.getTableSource().toString());\r\n\t\t\t}\r\n\t\treturn 1;\t\t\r\n\t}\t\r\n\t\r\n\tprivate int getSQLExprToInt(SQLExpr expr){\r\n\t\tif (expr instanceof SQLIntegerExpr){\r\n\t\t\treturn ((SQLIntegerExpr)expr).getNumber().intValue();\r\n\t\t}\r\n\t\treturn 0;\t\t\r\n\t}\r\n\tprivate int getSQLExprToAsc(SQLOrderingSpecification ASC){\r\n\t\tif (ASC==null ) {\r\n\t\t\treturn 1;\r\n\t\t}\r\n\t\tif (ASC==SQLOrderingSpecification.DESC){\r\n\t\t\treturn -1;\r\n\t\t}\r\n\t\telse {\r\n\t\t\treturn 1;\t\t\r\n\t\t}\r\n\t}\t\r\n\tpublic String remove(String resource,char ch)   \r\n    {   \r\n        StringBuffer buffer=new StringBuffer();   \r\n        int position=0;   \r\n        char currentChar;   \r\n  \r\n        while(position<resource.length())   \r\n        {   \r\n            currentChar=resource.charAt(position++);   \r\n            if(currentChar!=ch) {\r\n\t\t\t\tbuffer.append(currentChar);\r\n\t\t\t}\r\n        } \r\n        return buffer.toString();   \r\n    }  \r\n\tprivate Object getExpValue(SQLExpr expr){\r\n\t\tif (expr instanceof SQLIntegerExpr){\r\n\t\t\treturn ((SQLIntegerExpr)expr).getNumber().intValue();\r\n\t\t}\r\n\t\tif (expr instanceof SQLNumberExpr){\r\n\t\t\treturn ((SQLNumberExpr)expr).getNumber().doubleValue();\r\n\t\t}\t\t\r\n\t\tif (expr instanceof SQLCharExpr){\r\n\t\t\tString va=((SQLCharExpr)expr).toString();\r\n\t\t\treturn remove(va,'\\'');\r\n\t\t}\r\n\t\tif (expr instanceof SQLBooleanExpr){\t\t\t\r\n\t\t\treturn ((SQLBooleanExpr)expr).getValue();\r\n\t\t}\t\t\t\r\n\t\tif (expr instanceof SQLNullExpr){\r\n\t\t\treturn null;\r\n\t\t}\r\n\t    if (expr instanceof SQLVariantRefExpr) {\r\n\t       return this._params.get(this._pos++);\r\n\t    }\t\t\r\n\t\treturn expr;\r\n\t\t\r\n\t}\r\n\tprivate String getExprFieldName(SQLAggregateExpr expr){\r\n\t\tString field=\"\";\r\n\t\tfor (SQLExpr item :expr.getArguments()){\r\n\t\t\tfield+=item.toString();\r\n\t\t}\t\t\r\n\t\treturn expr.getMethodName()+\"(\"+field+\")\";\r\n\t\t\r\n\t}\r\n\tprivate String getFieldName2(SQLExpr item){\r\n\t\treturn item.toString();\r\n\t}\r\n\t\r\n\tprivate String getFieldName(SQLSelectItem item){\r\n\t\treturn item.toString();\r\n\t}\t\r\n\tprivate BSONObject parserWhere(SQLExpr expr){\r\n\t    BasicBSONObject o = new BasicBSONObject();\r\n\t    parserWhere(expr,o);\r\n\t    return o;\r\n\t}\r\n\t\r\n\t\r\n\t\r\n\tprivate void parserDBObject(BasicBSONObject ob,String akey, String aop,Object aval){\r\n\t\tboolean isok=false;\r\n\t\tif (!(ob.keySet().isEmpty())) {\r\n          for (String field : ob.keySet()) {            \r\n            if (akey.equals(field)){\r\n               Object val = ob.get(field);\t\r\n              if (val instanceof BasicBSONObject) {\r\n            \t ((BasicBSONObject) val).put(aop, aval);\r\n            \t ob.put(field, (BasicBSONObject) val); \r\n            \t isok=true;\r\n            \t break;\r\n              } else if (val instanceof BasicBSONList) {\r\n              //   newobj.put(field, ((BasicDBList)val).copy());\r\n               }\r\n            }  \r\n          }    \r\n        }    \r\n\t\tif (isok==false) {\r\n\t\t\tBasicBSONObject xo = new BasicBSONObject();\r\n\t\t\txo.put(aop, aval);\r\n\t\t\tob.put(akey,xo);\t\r\n\t\t}\r\n\t    \r\n\t}\r\n\t\r\n\t@SuppressWarnings(\"unused\")\r\n\tprivate void opSQLExpr(SQLBinaryOpExpr expr,BasicBSONObject o) {\r\n\t\t   SQLExpr exprL=expr.getLeft();\r\n\t\t   if (!(exprL instanceof SQLBinaryOpExpr))\r\n\t\t   {\r\n\t\t\t  if (expr.getOperator().getName().equals(\"=\")) {  \r\n\t\t        o.put(exprL.toString(), getExpValue(expr.getRight()));\r\n\t\t\t  }\r\n\t\t\t  else {\r\n\t\t\t\t  //BasicBSONObject xo = new BasicBSONObject();\r\n\t\t\t\t  String op=\"\";\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"<\")) {\r\n\t\t\t\t\t  op=\"$lt\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"<=\")) {\r\n\t\t\t\t\t  op = \"$lte\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\">\")) {\r\n\t\t\t\t\t  op = \"$gt\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\">=\")) {\r\n\t\t\t\t\t  op = \"$gte\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"!=\")) {\r\n\t\t\t\t\t  op = \"$ne\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"<>\")) {\r\n\t\t\t\t\t  op = \"$ne\";\r\n\t\t\t\t  }\r\n\t\t\t\t  //xo.put(op, getExpValue(expr.getRight()));\r\n\t\t\t\t // o.put(exprL.toString(),xo);\r\n\t\t\t\t  parserDBObject(o,exprL.toString(),op, getExpValue(expr.getRight()));\r\n\t\t\t  }\r\n\t\t   }\t\t\r\n\t}\r\n\tprivate void parserWhere(SQLExpr aexpr,BasicBSONObject o){   \r\n\t     if(aexpr instanceof SQLBinaryOpExpr){\r\n\t\t   SQLBinaryOpExpr expr=(SQLBinaryOpExpr)aexpr;  \r\n\t\t   SQLExpr exprL=expr.getLeft();\r\n\t\t   if (!(exprL instanceof SQLBinaryOpExpr))\r\n\t\t   {\r\n\t\t\t   //opSQLExpr((SQLBinaryOpExpr)aexpr,o);\t\t\t   \r\n\t\t\t  if (expr.getOperator().getName().equals(\"=\")) {  \r\n\t\t        o.put(exprL.toString(), getExpValue(expr.getRight()));\r\n\t\t\t  }\r\n\t\t\t  else {\r\n\t\t\t\t  String op=\"\";\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"<\")) {\r\n\t\t\t\t\t  op = \"$lt\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"<=\")) {\r\n\t\t\t\t\t  op = \"$lte\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\">\")) {\r\n\t\t\t\t\t  op = \"$gt\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\">=\")) {\r\n\t\t\t\t\t  op = \"$gte\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"!=\")) {\r\n\t\t\t\t\t  op = \"$ne\";\r\n\t\t\t\t  }\r\n\t\t\t\t  if (expr.getOperator().getName().equals(\"<>\")) {\r\n\t\t\t\t\t  op = \"$ne\";\r\n\t\t\t\t  }\r\n\r\n\t\t\t\t  parserDBObject(o,exprL.toString(),op, getExpValue(expr.getRight()));\r\n\t\t\t  }\r\n\t\t\t  \r\n\t\t   }\r\n\t\t   else {\r\n\t\t\t if (expr.getOperator().getName().equals(\"AND\")) {  \r\n\t\t\t   parserWhere(exprL,o); \r\n\t\t\t   parserWhere(expr.getRight(),o);\r\n\t\t\t }\r\n\t\t\t else if (expr.getOperator().getName().equals(\"OR\")) {  \r\n\t\t\t\torWhere(exprL,expr.getRight(),o); \t\t\t\t\r\n\t\t\t }\r\n\t\t\t else {\r\n\t\t\t\t throw new RuntimeException(\"Can't identify the operation of  of where\"); \r\n\t\t\t }\r\n\t\t   }\r\n\t   }\r\n\t  \r\n\t}\r\n\t\r\n   \r\n   private void orWhere(SQLExpr exprL,SQLExpr exprR ,BasicBSONObject ob){ \r\n\t   BasicBSONObject xo = new BasicBSONObject(); \r\n\t   BasicBSONObject yo = new BasicBSONObject(); \r\n\t   parserWhere(exprL,xo);\r\n\t   parserWhere(exprR,yo);\r\n\t   ob.put(\"$or\",new Object[]{xo,yo});\r\n   }\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/sequoiadb/SequoiaStatement.java",
    "content": "package io.mycat.backend.jdbc.sequoiadb;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.SQLWarning;\nimport java.sql.Statement;\n/**  \n * 功能详细描述\n * @author sohudo[http://blog.csdn.net/wind520]\n * @create 2014年12月19日 下午6:50:23 \n * @version 0.0.1\n */\npublic class SequoiaStatement implements Statement\n{\n\tprivate SequoiaConnection _conn;\n    private final int _type;\n    private final int _concurrency;\n    private final int _holdability;\n    private int _fetchSize = 0;\n    //int _maxRows = 0;\n    private SequoiaResultSet _last;\n\n    public SequoiaStatement(SequoiaConnection conn, int type, int concurrency, int holdability)\n    {\n        this._conn = conn;\n        this._type = type;\n        this._concurrency = concurrency;\n        this._holdability = holdability;\n\n        if (this._type != 0) {\n\t\t\tthrow new UnsupportedOperationException(\"type not supported yet\");\n\t\t}\n        if (this._concurrency != 0) {\n\t\t\tthrow new UnsupportedOperationException(\"concurrency not supported yet\");\n\t\t}\n        if (this._holdability != 0) {\n\t\t\tthrow new UnsupportedOperationException(\"holdability not supported yet\");\n\t\t}\n    }\n\n\t@Override\n\tpublic <T> T unwrap(Class<T> iface) throws SQLException {\n\t\t\n\t\tthrow new UnsupportedOperationException();//return null;\n\t}\n\n\t@Override\n\tpublic boolean isWrapperFor(Class<?> iface) throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic ResultSet executeQuery(String sql) throws SQLException {\n\t\t  \n\t\tSequoiaData mongo= new SequoiaSQLParser(this._conn.getDB(), sql).query();\t\t\n//        if (this._fetchSize > 0) {\n//        \t//设置每次网络请求的最大记录数\n//        \tif (mongo.getCursor()!=null) {\n//        \t//mongo.getCursor().batchSize(this._fetchSize);\n//        \t}\n//        }\n        /* \n        if (this._maxRows > 0)\n        {\n            cursor.limit(this._maxRows);\n        }\n        */\n        this._last = new SequoiaResultSet(mongo,this._conn.getSchema());\n\t\treturn this._last;\n\t}\n    \n\t@Override\n\tpublic int executeUpdate(String sql) throws SQLException {\n\t\t// 执行更新语句\n\t\treturn new SequoiaSQLParser(this._conn.getDB(), sql).executeUpdate();\n\t}\n\n\t@Override\n\tpublic void close() throws SQLException {\n\t\t\n\t    this._conn = null;\n\t}\n\n\t@Override\n\tpublic int getMaxFieldSize() throws SQLException {\n\t\t// 获取可以为此 Statement 对象所生成 ResultSet 对象中的字符和二进制列值返回的最大字节数。\n\t\treturn 0;//this._fetchSize;\n\t}\n\n\t@Override\n\tpublic void setMaxFieldSize(int max) throws SQLException {\n\t\t\n\t\t//this._fetchSize=max;\n\t}\n\n\t@Override\n\tpublic int getMaxRows() throws SQLException {\n\t\t// 获取由此 Statement 对象生成的 ResultSet 对象可以包含的最大行数。\n\t\treturn 0;//this._maxRows;\n\t}\n\n\t@Override\n\tpublic void setMaxRows(int max) throws SQLException {\n\t\t\n\t\t//this._maxRows = max;\n\t}\n\n\t@Override\n\tpublic void setEscapeProcessing(boolean enable) throws SQLException {\n\t\t// 将转义处理设置为开或关。\n\t\t\n\t}\n\n\t@Override\n\tpublic int getQueryTimeout() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void setQueryTimeout(int seconds) throws SQLException {\n\t\t// Statement 对象执行的秒数设置，超时设置。\n\t\t\n\t}\n\n\t@Override\n\tpublic void cancel() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic SQLWarning getWarnings() throws SQLException {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void clearWarnings() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void setCursorName(String name) throws SQLException {\n\t\t// 将 SQL 指针名称设置为给定的 String，后续 Statement 对象的 execute 方法将使用此字符串。\n\t\t\n\t}\n\n\t@Override\n\tpublic boolean execute(String sql) throws SQLException {\n\t\tint i=0;//new SequoiaSQLParser(this._conn.getDB(), sql).executeUpdate();\n\t\treturn i>0;\n\t}\n\n\t@Override\n\tpublic ResultSet getResultSet() throws SQLException {\n\t\t\n\t\treturn this._last;\n\t}\n\n\t@Override\n\tpublic int getUpdateCount() throws SQLException {\n\t\t// 记录变更的数量，ResultSet 对象或没有更多结果，则返回 -1\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean getMoreResults() throws SQLException {\n\t\t// 移动到此 Statement 对象的下一个结果，如果其为 ResultSet 对象，则返回 true，并隐式关闭利用方法 getResultSet 获取的所有当前 ResultSet 对象。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setFetchDirection(int direction) throws SQLException {\n\t\t// 此 Statement 对象创建的 ResultSet 对象中将按该方向处理行。\n\t\t\n\t}\n\n\t@Override\n\tpublic int getFetchDirection() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void setFetchSize(int rows) throws SQLException {\n\t\t// 获取结果集合的行数，该数是根据此 Statement 对象生成的 ResultSet 对象的默认获取大小。\n\t\tthis._fetchSize=rows;\n\t}\n\n\t@Override\n\tpublic int getFetchSize() throws SQLException {\n\t\t// 获取结果集合的行数，该数是根据此 Statement 对象生成的 ResultSet 对象的默认获取大小。\n\t\treturn this._fetchSize;\n\t}\n\n\t@Override\n\tpublic int getResultSetConcurrency() throws SQLException {\n\t\t// 对象生成的 ResultSet 对象的结果集合并发性\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getResultSetType() throws SQLException {\n\t\t// 对象生成的 ResultSet 对象的结果集合类型。\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void addBatch(String sql) throws SQLException {\n\t\t// 新增批处理\n\t   throw new UnsupportedOperationException(\"batch not supported\");\n\t}\n\n\t@Override\n\tpublic void clearBatch() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic int[] executeBatch() throws SQLException {\n\t\t// 将一批命令提交给数据库来执行，如果全部命令执行成功，则返回更新计数组成的数组。\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Connection getConnection() throws SQLException {\n\t\t\n\t\treturn this._conn;\n\t}\n\n\t@Override\n\tpublic boolean getMoreResults(int current) throws SQLException {\n\t\t// 将此 Statement 对象移动到下一个结果，根据给定标志指定的指令处理所有当前 ResultSet 对象；如果下一个结果为 ResultSet 对象，则返回 true。\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic ResultSet getGeneratedKeys() throws SQLException {\n\t\t// 获取由于执行此 Statement 对象而创建的所有自动生成的键。\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic int executeUpdate(String sql, int autoGeneratedKeys)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn 0;\n\t\t//throw new RuntimeException(\"executeUpdate not done\");\n\t}\n\n\t@Override\n\tpublic int executeUpdate(String sql, int[] columnIndexes)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn 0;\n\t\t//throw new RuntimeException(\"executeUpdate not done\");\n\t}\n\n\t@Override\n\tpublic int executeUpdate(String sql, String[] columnNames)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn 0;\n\t\t//throw new RuntimeException(\"executeUpdate not done\");\n\t}\n\n\t@Override\n\tpublic boolean execute(String sql, int autoGeneratedKeys)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean execute(String sql, int[] columnIndexes) throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean execute(String sql, String[] columnNames)\n\t\t\tthrows SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int getResultSetHoldability() throws SQLException {\n\t\t\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean isClosed() throws SQLException {\n\t\t\n\t\treturn this._conn == null;\n\t}\n\n\t@Override\n\tpublic void setPoolable(boolean poolable) throws SQLException {\n\t\t// 请求将 Statement 池化或非池化\n\t\t\n\t}\n\n\t@Override\n\tpublic boolean isPoolable() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void closeOnCompletion() throws SQLException {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic boolean isCloseOnCompletion() throws SQLException {\n\t\t\n\t\treturn false;\n\t}\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/jdbc/sequoiadb/StringUtils.java",
    "content": "package io.mycat.backend.jdbc.sequoiadb;\r\n\r\n\r\npublic class StringUtils {\r\n\t\r\n\r\n\tpublic static boolean startsWithIgnoreCase(String searchIn, int startAt,\r\n\t\t\tString searchFor) {\r\n\t\treturn searchIn.regionMatches(true, startAt, searchFor, 0, searchFor\r\n\t\t\t\t.length());\r\n\t}\r\n\r\n\tpublic static boolean startsWithIgnoreCase(String searchIn, String searchFor) {\r\n\t\treturn startsWithIgnoreCase(searchIn, 0, searchFor);\r\n\t}\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/loadbalance/LeastActiveLoadBalance.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.loadbalance;\n\nimport io.mycat.backend.datasource.PhysicalDatasource;\n\nimport java.util.ArrayList;\nimport java.util.concurrent.ThreadLocalRandom;\n\npublic class LeastActiveLoadBalance implements LoadBalance {\n\n    @Override\n    public PhysicalDatasource doSelect(String hostName, ArrayList<PhysicalDatasource> okSources) {\n        boolean sameWeight = true;\n        int length = okSources.size();\n        int[] leastIndexes = new int[length];\n        int leastActive = -1;\n        int leastCount = 0;\n        int[] weights = new int[length];\n        int totalWeight = 0;\n        int firstWeight = 0;\n\n        for (int i = 0; i < length; i++) {\n            PhysicalDatasource okSource = okSources.get(i);\n            int active = okSource.getActiveCount();\n            int weight = okSource.getConfig().getWeight();\n            if (weight == 0) {\n                continue;\n            }\n            weights[i] = weight;\n            if (leastActive == -1 || active < leastActive) {\n                sameWeight = true;\n                leastIndexes[0] = i;\n                leastActive = active;\n                leastCount = 1;\n                totalWeight = weight;\n                firstWeight = weight;\n            } else if (active == leastActive) {\n                leastIndexes[leastCount++] = i;\n                totalWeight += weight;\n                if (sameWeight && i > 0 && weight != firstWeight) {\n                    sameWeight = false;\n                }\n            }\n        }\n\n        if (leastCount == 0) {\n            return okSources.get(ThreadLocalRandom.current().nextInt(okSources.size()));\n        }\n\n        if (leastCount == 1) {\n            return okSources.get(leastIndexes[0]);\n        }\n\n        if (!sameWeight && totalWeight > 0) {\n            int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);\n            for (int i = 0; i < leastCount; i++) {\n                int leastIndex = leastIndexes[i];\n                offsetWeight -= weights[leastIndex];\n                if (offsetWeight < 0) {\n                    return okSources.get(leastIndex);\n                }\n            }\n        }\n\n        return okSources.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/loadbalance/LoadBalance.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.loadbalance;\n\nimport io.mycat.backend.datasource.PhysicalDatasource;\n\nimport java.util.ArrayList;\n\npublic interface LoadBalance {\n\n    PhysicalDatasource doSelect(String hostName, ArrayList<PhysicalDatasource> okSources);\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/loadbalance/RandomLoadBalance.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.loadbalance;\n\nimport io.mycat.backend.datasource.PhysicalDatasource;\n\nimport java.util.ArrayList;\nimport java.util.concurrent.ThreadLocalRandom;\n\npublic class RandomLoadBalance implements LoadBalance {\n\n    @Override\n    public PhysicalDatasource doSelect(String hostName, ArrayList<PhysicalDatasource> okSources) {\n        boolean sameWeight = true;\n        int length = okSources.size();\n        int[] weights = new int[length];\n        int firstWeight = okSources.get(0).getConfig().getWeight();\n        weights[0] = firstWeight;\n        int totalWeight = firstWeight;\n\n        for (int i = 1; i < length; i++) {\n            int weight = okSources.get(i).getConfig().getWeight();\n            weights[i] = weight;\n            totalWeight += weight;\n            if (sameWeight && weight != firstWeight) {\n                sameWeight = false;\n            }\n        }\n\n        if (!sameWeight && totalWeight > 0) {\n            int offset = ThreadLocalRandom.current().nextInt(totalWeight);\n            for (int i = 0; i < length; i++) {\n                offset -= weights[i];\n                if (offset < 0) {\n                    return okSources.get(i);\n                }\n            }\n        }\n\n        return okSources.get(ThreadLocalRandom.current().nextInt(length));\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/loadbalance/WeightedRoundRobinLoadBalance.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.loadbalance;\n\nimport io.mycat.backend.datasource.PhysicalDatasource;\n\nimport java.util.ArrayList;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class WeightedRoundRobinLoadBalance implements LoadBalance {\n\n    private Map<String, Map<String, WeightedRoundRobin>> weightedRoundRobinMap = new ConcurrentHashMap<>();\n\n    protected static class WeightedRoundRobin {\n\n        private int weight;\n        private AtomicInteger current = new AtomicInteger(0);\n\n        public int getWeight() {\n            return weight;\n        }\n\n        public void setWeight(int weight) {\n            this.weight = weight;\n            current.set(0);\n        }\n\n        public int increaseCurrent() {\n            return current.addAndGet(weight);\n        }\n\n        public void select(int total) {\n            current.addAndGet(-1 * total);\n        }\n\n    }\n\n    @Override\n    public PhysicalDatasource doSelect(String hostName, ArrayList<PhysicalDatasource> okSources) {\n        Map<String, WeightedRoundRobin> map = weightedRoundRobinMap.get(hostName);\n        if (map == null) {\n            Map<String, WeightedRoundRobin> newMap = new ConcurrentHashMap<>();\n            map = weightedRoundRobinMap.putIfAbsent(hostName, newMap);\n            if (map == null) {\n                map = newMap;\n            }\n        }\n\n        int totalWeight = 0;\n        int maxCurrent = Integer.MIN_VALUE;\n        PhysicalDatasource selectedOkSource = null;\n        WeightedRoundRobin selectedWeightedRoundRobin = null;\n\n        for (PhysicalDatasource okSource : okSources) {\n            String name = okSource.getName();\n            WeightedRoundRobin weightedRoundRobin = map.get(name);\n            int weight = okSource.getConfig().getWeight();\n            if (weight <= 0) {\n                continue;\n            }\n            if (weightedRoundRobin == null) {\n                weightedRoundRobin = new WeightedRoundRobin();\n                weightedRoundRobin.setWeight(weight);\n                map.putIfAbsent(name, weightedRoundRobin);\n            }\n            int current = weightedRoundRobin.increaseCurrent();\n            if (current > maxCurrent) {\n                maxCurrent = current;\n                selectedOkSource = okSource;\n                selectedWeightedRoundRobin = weightedRoundRobin;\n            }\n            totalWeight += weight;\n        }\n\n        if (selectedOkSource == null) {\n            return okSources.get(ThreadLocalRandom.current().nextInt(okSources.size()));\n        }\n\n        selectedWeightedRoundRobin.select(totalWeight);\n        return selectedOkSource;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/BindValue.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql;\n\n/**\n * @author mycat\n */\npublic class BindValue {\n\n    public boolean isNull; /* NULL indicator */\n    public boolean isLongData; /* long data indicator */\n    public boolean isSet; /* has this parameter been set */\n\n    public long length; /* Default length of data */\n    public int type; /* data type */\n    public byte scale;\n\n    /** 数据值 **/\n    public byte byteBinding;\n    public short shortBinding;\n    public int intBinding;\n    public float floatBinding;\n    public long longBinding;\n    public double doubleBinding;\n    public Object value; /* Other value to store */\n\n    public void reset() {\n        this.isNull = false;\n        this.isLongData = false;\n        this.isSet = false;\n\n        this.length = 0;\n        this.type = 0;\n        this.scale = 0;\n\n        this.byteBinding = 0;\n        this.shortBinding = 0;\n        this.intBinding = 0;\n        this.floatBinding = 0;\n        this.longBinding = 0L;\n        this.doubleBinding = 0D;\n        this.value = null;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/BindValueUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql;\n\nimport io.mycat.config.Fields;\n\nimport java.io.UnsupportedEncodingException;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.CharacterCodingException;\nimport java.nio.charset.Charset;\nimport java.nio.charset.CodingErrorAction;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * @author mycat\n */\npublic class BindValueUtil {\n\n    public static final void read(MySQLMessage mm, BindValue bv, String charsetName) throws UnsupportedEncodingException {\n        switch (bv.type & 0xff) {\n        case Fields.FIELD_TYPE_BIT:\n            bv.value = mm.readBytesWithLength();\n            break;\n        case Fields.FIELD_TYPE_TINY:\n            bv.byteBinding = mm.read();\n            break;\n        case Fields.FIELD_TYPE_SHORT:\n            bv.shortBinding = (short) mm.readUB2();\n            break;\n        case Fields.FIELD_TYPE_LONG:\n            bv.intBinding = mm.readInt();\n            break;\n        case Fields.FIELD_TYPE_LONGLONG:\n            bv.longBinding = mm.readLong();\n            break;\n        case Fields.FIELD_TYPE_FLOAT:\n            bv.floatBinding = mm.readFloat();\n            break;\n        case Fields.FIELD_TYPE_DOUBLE:\n            bv.doubleBinding = mm.readDouble();\n            break;\n        case Fields.FIELD_TYPE_TIME:\n            bv.value = mm.readTime();\n            break;\n        case Fields.FIELD_TYPE_DATE:\n        case Fields.FIELD_TYPE_DATETIME:\n        case Fields.FIELD_TYPE_TIMESTAMP:\n            bv.value = mm.readDate();\n            break;\n        case Fields.FIELD_TYPE_VAR_STRING:\n        case Fields.FIELD_TYPE_STRING:\n        case Fields.FIELD_TYPE_VARCHAR:\n        case Fields.FIELD_TYPE_DECIMAL:\n        case Fields.FIELD_TYPE_NEW_DECIMAL:\n        case Fields.FIELD_TYPE_BLOB:\n            byte[] vv = mm.readBytesWithLength();\n            if (vv == null) {\n                bv.isNull = true;\n            } else {\n                if (charsetName == null) {\n                    charsetName = StandardCharsets.UTF_8.name();\n                }\n                Charset charset = Charset.forName(charsetName);\n                try {\n                    bv.value = charset.newDecoder().onMalformedInput(CodingErrorAction.REPORT).decode(ByteBuffer.wrap(vv));\n                } catch (CharacterCodingException e) {\n                    bv.value = vv;\n                }\n            }\n        \tbreak;\n        default:\n            throw new IllegalArgumentException(\"bindValue error,unsupported type:\" + bv.type);\n        }\n        bv.isSet = true;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/BufferUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql;\n\nimport java.nio.ByteBuffer;\n\n/**\n * @author mycat\n */\npublic class BufferUtil {\n\n    public static final void writeUB2(ByteBuffer buffer, int i) {\n        buffer.put((byte) (i & 0xff));\n        buffer.put((byte) (i >>> 8));\n    }\n\n    public static final void writeUB3(ByteBuffer buffer, int i) {\n        buffer.put((byte) (i & 0xff));\n        buffer.put((byte) (i >>> 8));\n        buffer.put((byte) (i >>> 16));\n    }\n\n    public static final void writeInt(ByteBuffer buffer, int i) {\n        buffer.put((byte) (i & 0xff));\n        buffer.put((byte) (i >>> 8));\n        buffer.put((byte) (i >>> 16));\n        buffer.put((byte) (i >>> 24));\n    }\n\n    public static final void writeFloat(ByteBuffer buffer, float f) {\n        writeInt(buffer, Float.floatToIntBits(f));\n    }\n\n    public static final void writeUB4(ByteBuffer buffer, long l) {\n        buffer.put((byte) (l & 0xff));\n        buffer.put((byte) (l >>> 8));\n        buffer.put((byte) (l >>> 16));\n        buffer.put((byte) (l >>> 24));\n    }\n\n    public static final void writeLong(ByteBuffer buffer, long l) {\n        buffer.put((byte) (l & 0xff));\n        buffer.put((byte) (l >>> 8));\n        buffer.put((byte) (l >>> 16));\n        buffer.put((byte) (l >>> 24));\n        buffer.put((byte) (l >>> 32));\n        buffer.put((byte) (l >>> 40));\n        buffer.put((byte) (l >>> 48));\n        buffer.put((byte) (l >>> 56));\n    }\n\n    public static final void writeDouble(ByteBuffer buffer, double d) {\n        writeLong(buffer, Double.doubleToLongBits(d));\n    }\n\n    public static final void writeLength(ByteBuffer buffer, long l) {\n    \tif(l < 0) {\n    \t\t buffer.put((byte) 254);\n             writeLong(buffer, l);\n     \t} else  if (l < 251) {\n            buffer.put((byte) l);\n        } else if (l < 0x10000L) {\n            buffer.put((byte) 252);\n            writeUB2(buffer, (int) l);\n        } else if (l < 0x1000000L) {\n            buffer.put((byte) 253);\n            writeUB3(buffer, (int) l);\n        } else {\n            buffer.put((byte) 254);\n            writeLong(buffer, l);\n        }\n    }\n\n    public static final void writeWithNull(ByteBuffer buffer, byte[] src) {\n        buffer.put(src);\n        buffer.put((byte) 0);\n    }\n\n    public static final void writeWithLength(ByteBuffer buffer, byte[] src) {\n        int length = src.length;\n        if (length < 251) {\n            buffer.put((byte) length);\n        } else if (length < 0x10000L) {\n            buffer.put((byte) 252);\n            writeUB2(buffer, length);\n        } else if (length < 0x1000000L) {\n            buffer.put((byte) 253);\n            writeUB3(buffer, length);\n        } else {\n            buffer.put((byte) 254);\n            writeLong(buffer, length);\n        }\n        buffer.put(src);\n    }\n\n    public static final void writeWithLength(ByteBuffer buffer, byte[] src, byte nullValue) {\n        if (src == null) {\n            buffer.put(nullValue);\n        } else {\n            writeWithLength(buffer, src);\n        }\n    }\n\n    public static final int getLength(long length) {\n    \tif(length < 0){\n    \t\treturn 9;\n    \t} else if (length < 251) {\n            return 1;\n        } else if (length < 0x10000L) {\n            return 3;\n        } else if (length < 0x1000000L) {\n            return 4;\n        } else {\n            return 9;\n        }\n    }\n\n    public static final int getLength(byte[] src) {\n        int length = src.length;\n        if (length < 251) {\n            return 1 + length;\n        } else if (length < 0x10000L) {\n            return 3 + length;\n        } else if (length < 0x1000000L) {\n            return 4 + length;\n        } else {\n            return 9 + length;\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/ByteUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql;\n\n/**\n * @author mycat\n */\npublic class ByteUtil {\n\n    public static int readUB2(byte[] data, int offset) {\n        int i = data[offset] & 0xff;\n        i |= (data[++offset] & 0xff) << 8;\n        return i;\n    }\n\n    public static int readUB3(byte[] data, int offset) {\n        int i = data[offset] & 0xff;\n        i |= (data[++offset] & 0xff) << 8;\n        i |= (data[++offset] & 0xff) << 16;\n        return i;\n    }\n\n    public static long readUB4(byte[] data, int offset) {\n        long l = data[offset] & 0xff;\n        l |= (data[++offset] & 0xff) << 8;\n        l |= (data[++offset] & 0xff) << 16;\n        l |= (data[++offset] & 0xff) << 24;\n        return l;\n    }\n\n    public static long readLong(byte[] data, int offset) {\n        long l = (long) (data[offset] & 0xff);\n        l |= (long) (data[++offset] & 0xff) << 8;\n        l |= (long) (data[++offset] & 0xff) << 16;\n        l |= (long) (data[++offset] & 0xff) << 24;\n        l |= (long) (data[++offset] & 0xff) << 32;\n        l |= (long) (data[++offset] & 0xff) << 40;\n        l |= (long) (data[++offset] & 0xff) << 48;\n        l |= (long) (data[++offset] & 0xff) << 56;\n        return l;\n    }\n\n    public static long readLength(byte[] data, int offset) {\n        int length = data[offset++] & 0xff;\n        switch (length) {\n        case 251:\n            return MySQLMessage.NULL_LENGTH;\n        case 252:\n            return readUB2(data, offset);\n        case 253:\n            return readUB3(data, offset);\n        case 254:\n            return readLong(data, offset);\n        default:\n            return length;\n        }\n    }\n\n    public static int lengthToZero(byte[] data, int offset) {\n        int start = offset;\n        for (int i = start; i < data.length; i++) {\n            if (data[i] == 0) {\n                return (i - start);\n            }\n        }\n        int remaining = data.length - start;\n        return remaining > 0 ? remaining : 0;\n    }\n\n    public static int decodeLength(byte[] src) {\n        int length = src.length;\n        if (length < 251) {\n            return 1 + length;\n        } else if (length < 0x10000L) {\n            return 3 + length;\n        } else if (length < 0x1000000L) {\n            return 4 + length;\n        } else {\n            return 9 + length;\n        }\n    }\n\n    public static int decodeLength(long length) {\n        if (length < 251) {\n            return 1;\n        } else if (length < 0x10000L) {\n            return 3;\n        } else if (length < 0x1000000L) {\n            return 4;\n        } else {\n            return 9;\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/CharsetUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.FileInputStream;\nimport java.util.*;\n\n/**\n * @author mycat\n */\npublic class CharsetUtil {\n    public static final Logger logger = LoggerFactory\n            .getLogger(CharsetUtil.class);\n    private static final Map<Integer,String> INDEX_TO_CHARSET = new HashMap<>();\n    private static final Map<String, Integer> CHARSET_TO_INDEX = new HashMap<>();\n    static {\n\n        // index_to_charset.properties\n        INDEX_TO_CHARSET.put(1,\"big5\");\n        INDEX_TO_CHARSET.put(8,\"latin1\");\n        INDEX_TO_CHARSET.put(9,\"latin2\");\n        INDEX_TO_CHARSET.put(14,\"cp1251\");\n        INDEX_TO_CHARSET.put(28,\"gbk\");\n        INDEX_TO_CHARSET.put(24,\"gb2312\");\n        INDEX_TO_CHARSET.put(33,\"utf8\");\n        INDEX_TO_CHARSET.put(45,\"utf8mb4\");\n\n        String filePath = Thread.currentThread().getContextClassLoader()\n                .getResource(\"\").getPath().replaceAll(\"%20\", \" \")\n                + \"index_to_charset.properties\";\n        Properties prop = new Properties();\n        try {\n            prop.load(new FileInputStream(filePath));\n            for (Object index : prop.keySet()){\n               INDEX_TO_CHARSET.put(Integer.parseInt((String) index), prop.getProperty((String) index));\n            }\n        } catch (Exception e) {\n            logger.error(\"error:\",e);\n        }\n        \n        // charset --> index\n        for(Integer key : INDEX_TO_CHARSET.keySet()){\n        \tString charset = INDEX_TO_CHARSET.get(key);\n        \tif(charset != null && CHARSET_TO_INDEX.get(charset) == null){\n        \t\tCHARSET_TO_INDEX.put(charset, key);\n        \t}\n        }\n\n        CHARSET_TO_INDEX.put(\"iso-8859-1\", 14);\n        CHARSET_TO_INDEX.put(\"iso_8859_1\", 14);\n        CHARSET_TO_INDEX.put(\"utf-8\", 33);\n    }\n\n    public static final String getCharset(int index) {\n        return INDEX_TO_CHARSET.get(index);\n    }\n\n    public static final int getIndex(String charset) {\n        if (charset == null || charset.length() == 0) {\n            return 0;\n        } else {\n            Integer i = CHARSET_TO_INDEX.get(charset.toLowerCase());\n            return (i == null) ? 0 : i;\n        }\n    }\n\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/DataType.java",
    "content": "package io.mycat.backend.mysql;\n\n/**\n * 定义返回的数据类型\n * @author huangyiming\n *\n */\npublic enum  DataType {\n\n\tSTRING(\"String\"),DOUBLE(\"Double\"),FLOAT(\"Float\"),DATE(\"Date\"),INT(\"Int\");\n\tprivate String type;\n\tprivate DataType(String type){\n\t\tthis.type = type;\n\t}\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/LoadDataUtil.java",
    "content": "package io.mycat.backend.mysql;\n\nimport java.io.*;\nimport java.util.List;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.net.BackendAIOConnection;\nimport io.mycat.net.mysql.BinaryPacket;\nimport io.mycat.net.mysql.CommandPacket;\nimport io.mycat.net.mysql.MySQLPacket;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.sqlengine.mpp.LoadData;\n\n/**\n * Created by nange on 2015/3/31.\n */\npublic class LoadDataUtil\n{\n    public static void requestFileDataResponse(byte[] data, BackendConnection conn)\n    {\n\n        byte packId= data[3];\n        BackendAIOConnection backendAIOConnection= (BackendAIOConnection) conn;\n        RouteResultsetNode rrn= (RouteResultsetNode) conn.getAttachment();\n        LoadData loadData= rrn.getLoadData();\n        List<String> loadDataData = loadData.getData();\n        try\n        {\n            if(loadDataData !=null&&loadDataData.size()>0)\n            {\n                ByteArrayOutputStream bos = new ByteArrayOutputStream();\n                for (int i = 0, loadDataDataSize = loadDataData.size(); i < loadDataDataSize; i++)\n                {\n                    String line = loadDataData.get(i);\n\n\n                    String s =(i==loadDataDataSize-1)?line: line + loadData.getLineTerminatedBy();\n                    byte[] bytes = s.getBytes(loadData.getCharset());\n                    bos.write(bytes);\n\n\n                }\n\n                packId=   writeToBackConnection(packId,new ByteArrayInputStream(bos.toByteArray()),backendAIOConnection);\n\n            }   else\n            {\n                //从文件读取\n                packId=   writeToBackConnection(packId,new BufferedInputStream(new FileInputStream(loadData.getFileName())),backendAIOConnection);\n\n            }\n        }catch (IOException e)\n        {\n\n            throw new RuntimeException(e);\n        }  finally\n        {\n            //结束必须发空包\n            byte[] empty = new byte[] { 0, 0, 0,3 };\n            empty[3]=++packId;\n            backendAIOConnection.write(empty);\n        }\n\n\n\n\n    }\n\n    public static byte writeToBackConnection(byte packID,InputStream inputStream,BackendAIOConnection backendAIOConnection) throws IOException\n    {\n        try\n        {\n            int packSize = MycatServer.getInstance().getConfig().getSystem().getBufferPoolChunkSize() - 5;\n            // int packSize = backendAIOConnection.getMaxPacketSize() / 32;\n            //  int packSize=65530;\n            byte[] buffer = new byte[packSize];\n            int len = -1;\n\n            while ((len = inputStream.read(buffer)) != -1)\n            {\n                byte[] temp = null;\n                if (len == packSize)\n                {\n                    temp = buffer;\n                } else\n                {\n                    temp = new byte[len];\n                    System.arraycopy(buffer, 0, temp, 0, len);\n                }\n                BinaryPacket packet = new BinaryPacket();\n                packet.packetId = ++packID;\n                packet.data = temp;\n                packet.write(backendAIOConnection);\n            }\n\n        }\n        finally\n        {\n            inputStream.close();\n        }\n\n\n        return  packID;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/MySQLMessage.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql;\n\nimport java.io.UnsupportedEncodingException;\nimport java.math.BigDecimal;\nimport java.sql.Time;\nimport java.sql.Timestamp;\nimport java.util.Arrays;\nimport java.util.Calendar;\n\n/**\n * @author mycat\n */\npublic class MySQLMessage {\n    public static final long NULL_LENGTH = -1;\n    private static final byte[] EMPTY_BYTES = new byte[0];\n\n    private final byte[] data;\n    private final int length;\n    private int position;\n\n    public MySQLMessage(byte[] data) {\n        this.data = data;\n        this.length = data.length;\n        this.position = 0;\n    }\n\n    public int length() {\n        return length;\n    }\n\n    public int position() {\n        return position;\n    }\n\n    public byte[] bytes() {\n        return data;\n    }\n\n    public void move(int i) {\n        position += i;\n    }\n\n    public void position(int i) {\n        this.position = i;\n    }\n\n    public boolean hasRemaining() {\n        return length > position;\n    }\n\n    public byte read(int i) {\n        return data[i];\n    }\n\n    public byte read() {\n        return data[position++];\n    }\n\n    public int readUB2() {\n        final byte[] b = this.data;\n        int i = b[position++] & 0xff;\n        i |= (b[position++] & 0xff) << 8;\n        return i;\n    }\n\n    public int readUB3() {\n        final byte[] b = this.data;\n        int i = b[position++] & 0xff;\n        i |= (b[position++] & 0xff) << 8;\n        i |= (b[position++] & 0xff) << 16;\n        return i;\n    }\n\n    public long readUB4() {\n        final byte[] b = this.data;\n        long l = (long) (b[position++] & 0xff);\n        l |= (long) (b[position++] & 0xff) << 8;\n        l |= (long) (b[position++] & 0xff) << 16;\n        l |= (long) (b[position++] & 0xff) << 24;\n        return l;\n    }\n\n    public int readInt() {\n        final byte[] b = this.data;\n        int i = b[position++] & 0xff;\n        i |= (b[position++] & 0xff) << 8;\n        i |= (b[position++] & 0xff) << 16;\n        i |= (b[position++] & 0xff) << 24;\n        return i;\n    }\n\n    public float readFloat() {\n        return Float.intBitsToFloat(readInt());\n    }\n\n    public long readLong() {\n        final byte[] b = this.data;\n        long l = (long) (b[position++] & 0xff);\n        l |= (long) (b[position++] & 0xff) << 8;\n        l |= (long) (b[position++] & 0xff) << 16;\n        l |= (long) (b[position++] & 0xff) << 24;\n        l |= (long) (b[position++] & 0xff) << 32;\n        l |= (long) (b[position++] & 0xff) << 40;\n        l |= (long) (b[position++] & 0xff) << 48;\n        l |= (long) (b[position++] & 0xff) << 56;\n        return l;\n    }\n\n    public double readDouble() {\n        return Double.longBitsToDouble(readLong());\n    }\n\n    public long readLength() {\n        int length = data[position++] & 0xff;\n        switch (length) {\n        case 251:\n            return NULL_LENGTH;\n        case 252:\n            return readUB2();\n        case 253:\n            return readUB3();\n        case 254:\n            return readLong();\n        default:\n            return length;\n        }\n    }\n\n    public byte[] readBytes() {\n        if (position >= length) {\n            return EMPTY_BYTES;\n        }\n        byte[] ab = new byte[length - position];\n        System.arraycopy(data, position, ab, 0, ab.length);\n        position = length;\n        return ab;\n    }\n\n\n\n    public byte[] readBytes(int length) {\n        byte[] ab = new byte[length];\n        System.arraycopy(data, position, ab, 0, length);\n        position += length;\n        return ab;\n    }\n\n    public byte[] readBytesWithNull() {\n        final byte[] b = this.data;\n        if (position >= length) {\n            return EMPTY_BYTES;\n        }\n        int offset = -1;\n        for (int i = position; i < length; i++) {\n            if (b[i] == 0) {\n                offset = i;\n                break;\n            }\n        }\n        switch (offset) {\n        case -1:\n            byte[] ab1 = new byte[length - position];\n            System.arraycopy(b, position, ab1, 0, ab1.length);\n            position = length;\n            return ab1;\n        case 0:\n            position++;\n            return EMPTY_BYTES;\n        default:\n            byte[] ab2 = new byte[offset - position];\n            System.arraycopy(b, position, ab2, 0, ab2.length);\n            position = offset + 1;\n            return ab2;\n        }\n    }\n    \n    public int getRowLength(int fileldCount) {\n    \tint size = 0;\n    \tint bak_position = position;\n    \tposition += 4;\n    \tfor(int i = 0 ; i < fileldCount; i++) {\n            int length = (int) readLength();\n            if(length == NULL_LENGTH || length <= 0)\n            {\n            \tcontinue;\n            }\n\n            position += length;\n            size += length;\n    \t}\n    \tposition = bak_position;\n    \treturn size;\n    }\n    \n    public byte[] readBytesWithLength() {\n        int length = (int) readLength();\n        if(length==NULL_LENGTH)\n        {\n            return null;\n        }\n        if (length <= 0) {\n            return EMPTY_BYTES;\n        }\n\n        byte[] ab = new byte[length];\n        System.arraycopy(data, position, ab, 0, ab.length);\n        position += length;\n        return ab;\n    }\n\n    public String readString() {\n        if (position >= length) {\n            return null;\n        }\n        String s = new String(data, position, length - position);\n        position = length;\n        return s;\n    }\n\n    public String readString(String charset) throws UnsupportedEncodingException {\n        if (position >= length) {\n            return null;\n        }\n        \n        String s = new String(data, position, length - position, charset);\n        position = length;\n        return s;\n    }\n\n    public String readStringWithNull() {\n        final byte[] b = this.data;\n        if (position >= length) {\n            return null;\n        }\n        int offset = -1;\n        for (int i = position; i < length; i++) {\n            if (b[i] == 0) {\n                offset = i;\n                break;\n            }\n        }\n        if (offset == -1) {\n            String s = new String(b, position, length - position);\n            position = length;\n            return s;\n        }\n        if (offset > position) {\n            String s = new String(b, position, offset - position);\n            position = offset + 1;\n            return s;\n        } else {\n            position++;\n            return null;\n        }\n    }\n\n    public String readStringWithNull(String charset) throws UnsupportedEncodingException {\n        final byte[] b = this.data;\n        if (position >= length) {\n            return null;\n        }\n        int offset = -1;\n        for (int i = position; i < length; i++) {\n            if (b[i] == 0) {\n                offset = i;\n                break;\n            }\n        }\n        switch (offset) {\n        case -1:\n            String s1 = new String(b, position, length - position, charset);\n            position = length;\n            return s1;\n        case 0:\n            position++;\n            return null;\n        default:\n            String s2 = new String(b, position, offset - position, charset);\n            position = offset + 1;\n            return s2;\n        }\n    }\n\n    public String readStringWithLength() {\n        int length = (int) readLength();\n        if (length <= 0) {\n            return null;\n        }\n        String s = new String(data, position, length);\n        position += length;\n        return s;\n    }\n\n    public String readStringWithLength(String charset) throws UnsupportedEncodingException {\n        int length = (int) readLength();\n//        if (length <= 0) {\n//            return null;\n//        }\n        String s = new String(data, position, length, charset);\n        position += length;\n        return s;\n    }\n\n    public java.sql.Time readTime() {\n        move(6);\n        int hour = read();\n        int minute = read();\n        int second = read();\n        Calendar cal = getLocalCalendar();\n        cal.set(0, 0, 0, hour, minute, second);\n        return new Time(cal.getTimeInMillis());\n    }\n\n    public java.util.Date readDate() {\n        byte length = read();\n        int year = readUB2();\n        byte month = read();\n        byte date = read();\n        int hour = read();\n        int minute = read();\n        int second = read();\n        if (length == 11) {\n            long nanos = readUB4();\n            Calendar cal = getLocalCalendar();\n            cal.set(year, --month, date, hour, minute, second);\n            Timestamp time = new Timestamp(cal.getTimeInMillis());\n            time.setNanos((int) nanos);\n            return time;\n        } else {\n            Calendar cal = getLocalCalendar();\n            cal.set(year, --month, date, hour, minute, second);\n            return new java.sql.Date(cal.getTimeInMillis());\n        }\n    }\n\n    public BigDecimal readBigDecimal() {\n        String src = readStringWithLength();\n        return src == null ? null : new BigDecimal(src);\n    }\n\n    public String toString() {\n        return new StringBuilder().append(Arrays.toString(data)).toString();\n    }\n\n    private static final ThreadLocal<Calendar> localCalendar = new ThreadLocal<Calendar>();\n\n    private static final Calendar getLocalCalendar() {\n        Calendar cal = localCalendar.get();\n        if (cal == null) {\n            cal = Calendar.getInstance();\n            localCalendar.set(cal);\n        }\n        return cal;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/PacketUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql;\n\nimport java.io.UnsupportedEncodingException;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.net.mysql.BinaryPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\n\n/**\n * @author mycat\n */\npublic class PacketUtil {\n    private static final String CODE_PAGE_1252 = \"Cp1252\";\n\n    public static final ResultSetHeaderPacket getHeader(int fieldCount) {\n        ResultSetHeaderPacket packet = new ResultSetHeaderPacket();\n        packet.packetId = 1;\n        packet.fieldCount = fieldCount;\n        return packet;\n    }\n\n    public static byte[] encode(String src, String charset) {\n        if (src == null) {\n            return null;\n        }\n        try {\n            return src.getBytes(charset);\n        } catch (UnsupportedEncodingException e) {\n            return src.getBytes();\n        }\n    }\n\n    public static final FieldPacket getField(String name, String orgName, int type) {\n        FieldPacket packet = new FieldPacket();\n        packet.charsetIndex = CharsetUtil.getIndex(CODE_PAGE_1252);\n        packet.name = encode(name, CODE_PAGE_1252);\n        packet.orgName = encode(orgName, CODE_PAGE_1252);\n        packet.type = (byte) type;\n        return packet;\n    }\n\n    public static final FieldPacket getField(String name, int type) {\n        FieldPacket packet = new FieldPacket();\n        packet.charsetIndex = CharsetUtil.getIndex(CODE_PAGE_1252);\n        packet.name = encode(name, CODE_PAGE_1252);\n        packet.type = (byte) type;\n        return packet;\n    }\n\n    public static final ErrorPacket getShutdown() {\n        ErrorPacket error = new ErrorPacket();\n        error.packetId = 1;\n        error.errno = ErrorCode.ER_SERVER_SHUTDOWN;\n        error.message = \"The server has been shutdown\".getBytes();\n        return error;\n    }\n\n    public static final FieldPacket getField(BinaryPacket src, String fieldName) {\n        FieldPacket field = new FieldPacket();\n        field.read(src);\n        field.name = encode(fieldName, CODE_PAGE_1252);\n        field.packetLength = field.calcPacketSize();\n        return field;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/PreparedStatement.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.parser.SQLParserUtils;\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\nimport com.alibaba.druid.util.JdbcUtils;\n\nimport io.mycat.net.mysql.FieldPacket;\n\n/**\n * @author mycat, CrazyPig\n */\npublic class PreparedStatement {\n    private static final Logger LOGGER = LoggerFactory.getLogger(PreparedStatement.class);\n    private long id;\n    private String statement;\n    private String[] columnNames;\n    private int parametersNumber;\n    private int[] parametersType;\n    private FieldPacket[] params;\n    private FieldPacket[] fields;\n    /**\n     * 存放COM_STMT_SEND_LONG_DATA命令发送过来的字节数据\n     * <pre>\n     * key : param_id\n     * value : byte data\n     * </pre>\n     */\n    private Map<Long, ByteArrayOutputStream> longDataMap;\n\n    public PreparedStatement(long id, String statement, int parametersNumber) {\n        this.id = id;\n        this.statement = statement;\n        // this.columnNames = getColumns(statement);\n        this.parametersNumber = parametersNumber;\n        this.parametersType = new int[parametersNumber];\n        this.longDataMap = new HashMap<Long, ByteArrayOutputStream>();\n\n        constructColumns();\n    }\n\n    // 获取预处理语句中column的个数\n    public void constructColumns() {\n        String[] columnNames;\n        try {\n            SQLStatementParser sqlStatementParser = SQLParserUtils.createSQLStatementParser(statement, JdbcUtils.MYSQL);\n            SQLStatement statement = sqlStatementParser.parseStatement();\n            if (statement instanceof SQLSelectStatement) {\n                SQLSelect select = ((SQLSelectStatement) statement).getSelect();\n                com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock query = (com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock) select.getQuery();\n                int size = query.getSelectList().size();\n                if (size == 1){\n                    if(\"*\".equalsIgnoreCase(   query.getSelectList().get(0).toString())){\n                        throw new Exception(\"unsupport * in select items:\" + statement);\n                    }\n                } {\n                    columnNames = new String[size];\n                    for (int i = 0; i < size; i++) {\n                        columnNames[i] = query.getSelectList().get(i).toString();\n                    }\n                    this.columnNames = columnNames;\n                }\n\n            }\n        }catch (Exception e){\n            LOGGER.error(\"can not get column count\",e);\n        }\n\n        this.columnNames = new String[] {};\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public String getStatement() {\n        return statement;\n    }\n\n    public int getColumnsNumber() {\n        if (this.fields != null) {\n            return this.fields.length;\n        } else {\n            return this.columnNames.length;\n        }\n    }\n\n    public int getParametersNumber() {\n        return parametersNumber;\n    }\n\n    public int[] getParametersType() {\n        return parametersType;\n    }\n    \n    public boolean hasLongData(long paramId) {\n    \treturn longDataMap.containsKey(paramId);\n    }\n\n    public ByteArrayOutputStream getLongData(long paramId) {\n    \treturn longDataMap.get(paramId);\n    }\n    \n    /**\n     * COM_STMT_RESET命令将调用该方法进行数据重置\n     */\n    public void resetLongData() {\n    \tfor(Long paramId : longDataMap.keySet()) {\n    \t\tlongDataMap.get(paramId).reset();\n    \t}\n    }\n    \n    /**\n     * 追加数据到指定的预处理参数\n     * @param paramId\n     * @param data\n     * @throws IOException\n     */\n    public void appendLongData(long paramId, byte[] data) throws IOException {\n    \tif(getLongData(paramId) == null) {\n    \t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n        \tout.write(data);\n    \t\tlongDataMap.put(paramId, out);\n    \t} else {\n    \t\tlongDataMap.get(paramId).write(data);\n    \t}\n    }\n\n    public String[] getColumnNames() {\n        return columnNames;\n    }\n\n    public FieldPacket[] getParams() {\n        return params;\n    }\n\n    public void setParams(FieldPacket[] params) {\n        this.params = params;\n        this.parametersNumber = params == null ? 0 : params.length;\n    }\n\n    public FieldPacket[] getFields() {\n        return fields;\n    }\n\n    public void setFields(FieldPacket[] fields) {\n        this.fields = fields;\n\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/SecurityUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql;\n\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\n\n/**\n * 加密解密工具类\n * \n * @author mycat\n */\npublic class SecurityUtil {\n\n    public static final byte[] scramble411(byte[] pass, byte[] seed) throws NoSuchAlgorithmException {\n        MessageDigest md = MessageDigest.getInstance(\"SHA-1\");\n        byte[] pass1 = md.digest(pass);\n        md.reset();\n        byte[] pass2 = md.digest(pass1);\n        md.reset();\n        md.update(seed);\n        byte[] pass3 = md.digest(pass2);\n        for (int i = 0; i < pass3.length; i++) {\n            pass3[i] = (byte) (pass3[i] ^ pass1[i]);\n        }\n        return pass3;\n    }\n\n    public static final String scramble323(String pass, String seed) {\n        if ((pass == null) || (pass.length() == 0)) {\n            return pass;\n        }\n        byte b;\n        double d;\n        long[] pw = hash(seed);\n        long[] msg = hash(pass);\n        long max = 0x3fffffffL;\n        long seed1 = (pw[0] ^ msg[0]) % max;\n        long seed2 = (pw[1] ^ msg[1]) % max;\n        char[] chars = new char[seed.length()];\n        for (int i = 0; i < seed.length(); i++) {\n            seed1 = ((seed1 * 3) + seed2) % max;\n            seed2 = (seed1 + seed2 + 33) % max;\n            d = (double) seed1 / (double) max;\n            b = (byte) java.lang.Math.floor((d * 31) + 64);\n            chars[i] = (char) b;\n        }\n        seed1 = ((seed1 * 3) + seed2) % max;\n//        seed2 = (seed1 + seed2 + 33) % max;\n        d = (double) seed1 / (double) max;\n        b = (byte) java.lang.Math.floor(d * 31);\n        for (int i = 0; i < seed.length(); i++) {\n            chars[i] ^= (char) b;\n        }\n        return new String(chars);\n    }\n\n    private static long[] hash(String src) {\n        long nr = 1345345333L;\n        long add = 7;\n        long nr2 = 0x12345671L;\n        long tmp;\n        for (int i = 0; i < src.length(); ++i) {\n            switch (src.charAt(i)) {\n            case ' ':\n            case '\\t':\n                continue;\n            default:\n                tmp = (0xff & src.charAt(i));\n                nr ^= ((((nr & 63) + add) * tmp) + (nr << 8));\n                nr2 += ((nr2 << 8) ^ nr);\n                add += tmp;\n            }\n        }\n        long[] result = new long[2];\n        result[0] = nr & 0x7fffffffL;\n        result[1] = nr2 & 0x7fffffffL;\n        return result;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/StreamUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql;\n\nimport java.io.EOFException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\n/**\n * @author mycat\n */\npublic class StreamUtil {\n    private static final long NULL_LENGTH = -1;\n    private static final byte[] EMPTY_BYTES = new byte[0];\n\n    public static final void read(InputStream in, byte[] b, int offset, int length) throws IOException {\n        for (int got = 0; length > 0;) {\n            got = in.read(b, offset, length);\n            if (got < 0) {\n                throw new EOFException();\n            }\n            offset += got;\n            length -= got;\n        }\n    }\n\n    public static final byte read(InputStream in) throws IOException {\n        int got = in.read();\n        if (got < 0) {\n            throw new EOFException();\n        }\n        return (byte) (got & 0xff);\n    }\n\n    public static final int readUB2(InputStream in) throws IOException {\n        byte[] b = new byte[2];\n        read(in, b, 0, b.length);\n        int i = b[0] & 0xff;\n        i |= (b[1] & 0xff) << 8;\n        return i;\n    }\n\n    public static final int readUB3(InputStream in) throws IOException {\n        byte[] b = new byte[3];\n        read(in, b, 0, b.length);\n        int i = b[0] & 0xff;\n        i |= (b[1] & 0xff) << 8;\n        i |= (b[2] & 0xff) << 16;\n        return i;\n    }\n\n    public static final int readInt(InputStream in) throws IOException {\n        byte[] b = new byte[4];\n        read(in, b, 0, b.length);\n        int i = b[0] & 0xff;\n        i |= (b[1] & 0xff) << 8;\n        i |= (b[2] & 0xff) << 16;\n        i |= (b[3] & 0xff) << 24;\n        return i;\n    }\n\n    public static final float readFloat(InputStream in) throws IOException {\n        return Float.intBitsToFloat(readInt(in));\n    }\n\n    public static final long readUB4(InputStream in) throws IOException {\n        byte[] b = new byte[4];\n        read(in, b, 0, b.length);\n        long l = (long) (b[0] & 0xff);\n        l |= (long) (b[1] & 0xff) << 8;\n        l |= (long) (b[2] & 0xff) << 16;\n        l |= (long) (b[3] & 0xff) << 24;\n        return l;\n    }\n\n    public static final long readLong(InputStream in) throws IOException {\n        byte[] b = new byte[8];\n        read(in, b, 0, b.length);\n        long l = (long) (b[0] & 0xff);\n        l |= (long) (b[1] & 0xff) << 8;\n        l |= (long) (b[2] & 0xff) << 16;\n        l |= (long) (b[3] & 0xff) << 24;\n        l |= (long) (b[4] & 0xff) << 32;\n        l |= (long) (b[5] & 0xff) << 40;\n        l |= (long) (b[6] & 0xff) << 48;\n        l |= (long) (b[7] & 0xff) << 56;\n        return l;\n    }\n\n    public static final double readDouble(InputStream in) throws IOException {\n        return Double.longBitsToDouble(readLong(in));\n    }\n\n    public static final byte[] readWithLength(InputStream in) throws IOException {\n        int length = (int) readLength(in);\n        if (length <= 0) {\n            return EMPTY_BYTES;\n        }\n        byte[] b = new byte[length];\n        read(in, b, 0, b.length);\n        return b;\n    }\n\n    public static final void write(OutputStream out, byte b) throws IOException {\n        out.write(b & 0xff);\n    }\n\n    public static final void writeUB2(OutputStream out, int i) throws IOException {\n        byte[] b = new byte[2];\n        b[0] = (byte) (i & 0xff);\n        b[1] = (byte) (i >>> 8);\n        out.write(b);\n    }\n\n    public static final void writeUB3(OutputStream out, int i) throws IOException {\n        byte[] b = new byte[3];\n        b[0] = (byte) (i & 0xff);\n        b[1] = (byte) (i >>> 8);\n        b[2] = (byte) (i >>> 16);\n        out.write(b);\n    }\n\n    public static final void writeInt(OutputStream out, int i) throws IOException {\n        byte[] b = new byte[4];\n        b[0] = (byte) (i & 0xff);\n        b[1] = (byte) (i >>> 8);\n        b[2] = (byte) (i >>> 16);\n        b[3] = (byte) (i >>> 24);\n        out.write(b);\n    }\n\n    public static final void writeFloat(OutputStream out, float f) throws IOException {\n        writeInt(out, Float.floatToIntBits(f));\n    }\n\n    public static final void writeUB4(OutputStream out, long l) throws IOException {\n        byte[] b = new byte[4];\n        b[0] = (byte) (l & 0xff);\n        b[1] = (byte) (l >>> 8);\n        b[2] = (byte) (l >>> 16);\n        b[3] = (byte) (l >>> 24);\n        out.write(b);\n    }\n\n    public static final void writeLong(OutputStream out, long l) throws IOException {\n        byte[] b = new byte[8];\n        b[0] = (byte) (l & 0xff);\n        b[1] = (byte) (l >>> 8);\n        b[2] = (byte) (l >>> 16);\n        b[3] = (byte) (l >>> 24);\n        b[4] = (byte) (l >>> 32);\n        b[5] = (byte) (l >>> 40);\n        b[6] = (byte) (l >>> 48);\n        b[7] = (byte) (l >>> 56);\n        out.write(b);\n    }\n\n    public static final void writeDouble(OutputStream out, double d) throws IOException {\n        writeLong(out, Double.doubleToLongBits(d));\n    }\n\n    public static final long readLength(InputStream in) throws IOException {\n        int length = in.read();\n        if (length < 0) {\n            throw new EOFException();\n        }\n        switch (length) {\n        case 251:\n            return NULL_LENGTH;\n        case 252:\n            return readUB2(in);\n        case 253:\n            return readUB3(in);\n        case 254:\n            return readLong(in);\n        default:\n            return length;\n        }\n    }\n\n    public static final void writeLength(OutputStream out, long length) throws IOException {\n        if (length < 251) {\n            out.write((byte) length);\n        } else if (length < 0x10000L) {\n            out.write((byte) 252);\n            writeUB2(out, (int) length);\n        } else if (length < 0x1000000L) {\n            out.write((byte) 253);\n            writeUB3(out, (int) length);\n        } else {\n            out.write((byte) 254);\n            writeLong(out, length);\n        }\n    }\n\n    public static final void writeWithNull(OutputStream out, byte[] src) throws IOException {\n        out.write(src);\n        out.write((byte) 0);\n    }\n\n    public static final void writeWithLength(OutputStream out, byte[] src) throws IOException {\n        int length = src.length;\n        if (length < 251) {\n            out.write((byte) length);\n        } else if (length < 0x10000L) {\n            out.write((byte) 252);\n            writeUB2(out, length);\n        } else if (length < 0x1000000L) {\n            out.write((byte) 253);\n            writeUB3(out, length);\n        } else {\n            out.write((byte) 254);\n            writeLong(out, length);\n        }\n        out.write(src);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/listener/DefaultSqlExecuteStageListener.java",
    "content": "package io.mycat.backend.mysql.listener;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport io.mycat.server.ServerConnection;\r\n\r\n/**\r\n * sql各自执行阶段（读取，解析，路由，执行，完成）\r\n * @author funny 2020年7月26日 下午9:36:59\r\n * @since 1.0.0\r\n */\r\npublic class DefaultSqlExecuteStageListener implements SqlExecuteStageListener {\r\n    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSqlExecuteStageListener.class);\r\n\r\n    private ServerConnection source;\r\n\r\n    public DefaultSqlExecuteStageListener(ServerConnection source) {\r\n        this.source = source;\r\n    }\r\n\r\n    @Override\r\n    public void fireEvent(SqlExecuteStage stage) {\r\n        switch (stage) {\r\n        case READ:\r\n            onReadCompleted();\r\n            break;\r\n        case PARSE:\r\n            onParseCompleted();\r\n            break;\r\n        case ROUTE:\r\n            onRouteCompleted();\r\n            break;\r\n        case EXECUTE:\r\n            onExecuteCompleted();\r\n            break;\r\n        case MERGE:\r\n            onMergeCompleted();\r\n            break;\r\n        case END:\r\n            onEndCompleted();\r\n            break;\r\n\r\n        }\r\n    }\r\n\r\n    private void onReadCompleted() {\r\n\r\n    }\r\n\r\n    private void onParseCompleted() {\r\n\r\n    }\r\n\r\n    private void onRouteCompleted() {\r\n\r\n    }\r\n\r\n    private void onExecuteCompleted() {\r\n\r\n    }\r\n\r\n    private void onMergeCompleted() {\r\n\r\n    }\r\n\r\n    private void onEndCompleted() {\r\n        LOGGER.debug(\"on event sql end\");\r\n        source.onEventSqlCompleted();\r\n    }\r\n\r\n}\r\n\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/listener/SqlExecuteStage.java",
    "content": "package io.mycat.backend.mysql.listener;\r\n\r\npublic enum SqlExecuteStage {\r\n    READ, PARSE, ROUTE, EXECUTE, MERGE, END\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/listener/SqlExecuteStageListener.java",
    "content": "package io.mycat.backend.mysql.listener;\r\n\r\n/**\r\n * 定义sql各个执行阶段（读取，解析，路由，执行，完成）监听事件，比如写日志，写执行统计\r\n * @author funnyAnt 2020年7月26日 下午9:54:51\r\n * @since 1.0.0\r\n */\r\npublic interface SqlExecuteStageListener {\r\n    void fireEvent(SqlExecuteStage stage);\r\n\r\n}\r\n\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/MySQLConnection.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio;\n\nimport java.io.UnsupportedEncodingException;\nimport java.nio.channels.NetworkChannel;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.CharsetUtil;\nimport io.mycat.backend.mysql.SecurityUtil;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.backend.mysql.xa.TxState;\nimport io.mycat.config.Capabilities;\nimport io.mycat.config.Isolations;\nimport io.mycat.net.BackendAIOConnection;\nimport io.mycat.net.mysql.AuthPacket;\nimport io.mycat.net.mysql.CommandPacket;\nimport io.mycat.net.mysql.HandshakePacket;\nimport io.mycat.net.mysql.MySQLPacket;\nimport io.mycat.net.mysql.QuitPacket;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.util.TimeUtil;\nimport io.mycat.util.exception.UnknownTxIsolationException;\n\n/**\n * @author mycat\n */\npublic class MySQLConnection extends BackendAIOConnection {\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(MySQLConnection.class);\n\tprivate static final long CLIENT_FLAGS = initClientFlags();\n\tprivate volatile long lastTime; \n\tprivate volatile String schema = null;\n\tprivate volatile String oldSchema;\n\tprivate volatile boolean borrowed = false;\n\tprivate volatile boolean modifiedSQLExecuted = false;\n\tprivate volatile int batchCmdCount = 0;\n\n\tprivate static long initClientFlags() {\n\t\tint flag = 0;\n\t\tflag |= Capabilities.CLIENT_LONG_PASSWORD;\n\t\tflag |= Capabilities.CLIENT_FOUND_ROWS;\n\t\tflag |= Capabilities.CLIENT_LONG_FLAG;\n\t\tflag |= Capabilities.CLIENT_CONNECT_WITH_DB;\n\t\t// flag |= Capabilities.CLIENT_NO_SCHEMA;\n\t\tboolean usingCompress=MycatServer.getInstance().getConfig().getSystem().getUseCompression()==1 ;\n\t\tif(usingCompress)\n\t\t{\n\t\t\t flag |= Capabilities.CLIENT_COMPRESS;\n\t\t}\n\t\tflag |= Capabilities.CLIENT_ODBC;\n\t\tflag |= Capabilities.CLIENT_LOCAL_FILES;\n\t\tflag |= Capabilities.CLIENT_IGNORE_SPACE;\n\t\tflag |= Capabilities.CLIENT_PROTOCOL_41;\n\t\tflag |= Capabilities.CLIENT_INTERACTIVE;\n\t\t// flag |= Capabilities.CLIENT_SSL;\n\t\tflag |= Capabilities.CLIENT_IGNORE_SIGPIPE;\n\t\tflag |= Capabilities.CLIENT_TRANSACTIONS;\n\t\t// flag |= Capabilities.CLIENT_RESERVED;\n\t\tflag |= Capabilities.CLIENT_SECURE_CONNECTION;\n\t\t// client extension\n\t\tflag |= Capabilities.CLIENT_MULTI_STATEMENTS;\n\t\tflag |= Capabilities.CLIENT_MULTI_RESULTS;\n\t\treturn flag;\n\t}\n\n\tprivate static final CommandPacket _READ_UNCOMMITTED = new CommandPacket();\n\tprivate static final CommandPacket _READ_COMMITTED = new CommandPacket();\n\tprivate static final CommandPacket _REPEATED_READ = new CommandPacket();\n\tprivate static final CommandPacket _SERIALIZABLE = new CommandPacket();\n\tprivate static final CommandPacket _AUTOCOMMIT_ON = new CommandPacket();\n\tprivate static final CommandPacket _AUTOCOMMIT_OFF = new CommandPacket();\n\tprivate static final CommandPacket _COMMIT = new CommandPacket();\n\tprivate static final CommandPacket _ROLLBACK = new CommandPacket();\n\tstatic {\n\t\t_READ_UNCOMMITTED.packetId = 0;\n\t\t_READ_UNCOMMITTED.command = MySQLPacket.COM_QUERY;\n\t\t_READ_UNCOMMITTED.arg = \"SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED\"\n\t\t\t\t.getBytes();\n\t\t_READ_COMMITTED.packetId = 0;\n\t\t_READ_COMMITTED.command = MySQLPacket.COM_QUERY;\n\t\t_READ_COMMITTED.arg = \"SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED\"\n\t\t\t\t.getBytes();\n\t\t_REPEATED_READ.packetId = 0;\n\t\t_REPEATED_READ.command = MySQLPacket.COM_QUERY;\n\t\t_REPEATED_READ.arg = \"SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ\"\n\t\t\t\t.getBytes();\n\t\t_SERIALIZABLE.packetId = 0;\n\t\t_SERIALIZABLE.command = MySQLPacket.COM_QUERY;\n\t\t_SERIALIZABLE.arg = \"SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE\"\n\t\t\t\t.getBytes();\n\t\t_AUTOCOMMIT_ON.packetId = 0;\n\t\t_AUTOCOMMIT_ON.command = MySQLPacket.COM_QUERY;\n\t\t_AUTOCOMMIT_ON.arg = \"SET autocommit=1\".getBytes();\n\t\t_AUTOCOMMIT_OFF.packetId = 0;\n\t\t_AUTOCOMMIT_OFF.command = MySQLPacket.COM_QUERY;\n\t\t_AUTOCOMMIT_OFF.arg = \"SET autocommit=0\".getBytes();\n\t\t_COMMIT.packetId = 0;\n\t\t_COMMIT.command = MySQLPacket.COM_QUERY;\n\t\t_COMMIT.arg = \"commit\".getBytes();\n\t\t_ROLLBACK.packetId = 0;\n\t\t_ROLLBACK.command = MySQLPacket.COM_QUERY;\n\t\t_ROLLBACK.arg = \"rollback\".getBytes();\n\t}\n\n\tprivate MySQLDataSource pool;\n\tprivate boolean fromSlaveDB;\n\tprivate long threadId;\n\tprivate HandshakePacket handshake;\n\tprivate volatile int txIsolation;\n\tprivate volatile boolean autocommit;\n\tprivate volatile boolean txReadonly;\n\t/** 保存SET SQL_SELECT_LIMIT的值, default 解析为-1. */\n\tprivate volatile int sqlSelectLimit = -1;\n\tprivate long clientFlags;\n\tprivate boolean isAuthenticated;\n\tprivate String user;\n\tprivate String password;\n\tprivate Object attachment;\n\tprivate volatile ResponseHandler respHandler;\n\n\tprivate final AtomicBoolean isQuit;\n\tprivate volatile StatusSync statusSync;\n\tprivate volatile boolean metaDataSyned = true;\n\tprivate volatile int xaStatus = 0;\n\n\tpublic MySQLConnection(NetworkChannel channel, boolean fromSlaveDB) {\n\t\tsuper(channel);\n\t\tthis.clientFlags = CLIENT_FLAGS;\n\t\tthis.lastTime = TimeUtil.currentTimeMillis();\n\t\tthis.isQuit = new AtomicBoolean(false);\n\t\tthis.autocommit = true;\n\t\tthis.fromSlaveDB = fromSlaveDB;\n\t\t// 设为默认值，免得每个初始化好的连接都要去同步一下\n\t\tthis.txIsolation = MycatServer.getInstance().getConfig().getSystem().getTxIsolation();\n\t\tthis.txReadonly = false;\n\t}\n\n\tpublic int getXaStatus() {\n\t\treturn xaStatus;\n\t}\n\n\tpublic void setXaStatus(int xaStatus) {\n\t\tthis.xaStatus = xaStatus;\n\t}\n\n\tpublic void onConnectFailed(Throwable t) {\n\t\tif (handler instanceof MySQLConnectionHandler) {\n\t\t\tMySQLConnectionHandler theHandler = (MySQLConnectionHandler) handler;\n\t\t\ttheHandler.connectionError(t);\n\t\t} else {\n\t\t\t((MySQLConnectionAuthenticator) handler).connectionError(this, t);\n\t\t}\n\t}\n\n\tpublic String getSchema() {\n\t\treturn this.schema;\n\t}\n\n\tpublic void setSchema(String newSchema) {\n\t\tString curSchema = schema;\n\t\tif (curSchema == null) {\n\t\t\tthis.schema = newSchema;\n\t\t\tthis.oldSchema = newSchema;\n\t\t} else {\n\t\t\tthis.oldSchema = curSchema;\n\t\t\tthis.schema = newSchema;\n\t\t}\n\t}\n\n\tpublic MySQLDataSource getPool() {\n\t\treturn pool;\n\t}\n\n\tpublic void setPool(MySQLDataSource pool) {\n\t\tthis.pool = pool;\n\t}\n\n\tpublic String getUser() {\n\t\treturn user;\n\t}\n\n\tpublic void setUser(String user) {\n\t\tthis.user = user;\n\t}\n\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\n\tpublic HandshakePacket getHandshake() {\n\t\treturn handshake;\n\t}\n\n\tpublic void setHandshake(HandshakePacket handshake) {\n\t\tthis.handshake = handshake;\n\t}\n\n\tpublic long getThreadId() {\n\t\treturn threadId;\n\t}\n\n\tpublic void setThreadId(long threadId) {\n\t\tthis.threadId = threadId;\n\t}\n\n\tpublic boolean isAuthenticated() {\n\t\treturn isAuthenticated;\n\t}\n\n\tpublic void setAuthenticated(boolean isAuthenticated) {\n\t\tthis.isAuthenticated = isAuthenticated;\n\t}\n\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\n\tpublic void authenticate() {\n\t\tAuthPacket packet = new AuthPacket();\n\t\tpacket.packetId = 1;\n\t\tpacket.clientFlags = clientFlags;\n\t\tpacket.maxPacketSize = maxPacketSize;\n\t\tpacket.charsetIndex = this.charsetIndex;\n\t\tpacket.user = user;\n\t\ttry {\n\t\t\tpacket.password = passwd(password, handshake);\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tthrow new RuntimeException(e.getMessage());\n\t\t}\n\t\tpacket.database = schema;\n\t\tpacket.write(this);\n\t}\n\n\tpublic boolean isAutocommit() {\n\t\treturn autocommit;\n\t}\n\n\tpublic boolean isTxReadonly() {\n\t\treturn txReadonly;\n\t}\n\n\tpublic int getSqlSelectLimit() {\n\t\treturn sqlSelectLimit;\n\t}\n\n\tpublic Object getAttachment() {\n\t\treturn attachment;\n\t}\n\n\tpublic void setAttachment(Object attachment) {\n\t\tthis.attachment = attachment;\n\t}\n\n\tpublic boolean isClosedOrQuit() {\n\t\treturn isClosed() || isQuit.get();\n\t}\n\n\t/**\n\t * <pre>\n\t * 用于解决mysql协议中com_field_list类似的命令的支持 \n\t * （https://dev.mysql.com/doc/internals/en/com-field-list.html）\n\t * 如ogg工具中使用到此命令。\n\t * </pre>\n\t */\n\tprivate void sendComFieldListCmd(String query) {\n\t\tCommandPacket packet = new CommandPacket();\n\t\tpacket.packetId = 0;\n\t\tpacket.command = MySQLPacket.COM_FIELD_LIST;\n\t\ttry {\n\t\t\t//只需要命令中最后的具体信息\n\t\t\tint index = query.indexOf(ServerParse.COM_FIELD_LIST_FLAG);\n\t\t\tquery = query.substring(index + 17);\n\t\t\tpacket.arg = query.getBytes(charset);\n\t\t\t//把query中最后一个改为协议中 0x00\n\t\t\tpacket.arg[query.length() - 1] = (byte) 0x00;\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t\tlastTime = TimeUtil.currentTimeMillis();\n\t\tpacket.write(this);\n\t}\n\tprotected void sendQueryCmd(String query) {\n\t\tCommandPacket packet = new CommandPacket();\n\t\tpacket.packetId = 0;\n\t\tpacket.command = MySQLPacket.COM_QUERY;\n\t\ttry {\n\t\t\tpacket.arg = query.getBytes(charset);\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t\tlastTime = TimeUtil.currentTimeMillis();\n\t\tpacket.write(this);\n\t}\n\n\tprivate static void getCharsetCommand(StringBuilder sb, int clientCharIndex) {\n\t\tsb.append(\"SET names \").append(CharsetUtil.getCharset(clientCharIndex))\n\t\t\t\t.append(\";\");\n\t}\n\n\tprivate static void getTxIsolationCommand(StringBuilder sb, int txIsolation) {\n\t\tswitch (txIsolation) {\n\t\tcase Isolations.READ_UNCOMMITTED:\n\t\t\tsb.append(\"SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;\");\n\t\t\treturn;\n\t\tcase Isolations.READ_COMMITTED:\n\t\t\tsb.append(\"SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;\");\n\t\t\treturn;\n\t\tcase Isolations.REPEATED_READ:\n\t\t\tsb.append(\"SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;\");\n\t\t\treturn;\n\t\tcase Isolations.SERIALIZABLE:\n\t\t\tsb.append(\"SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;\");\n\t\t\treturn;\n\t\tdefault:\n\t\t\tthrow new UnknownTxIsolationException(\"txIsolation:\" + txIsolation);\n\t\t}\n\t}\n\n\tprivate void getAutocommitCommand(StringBuilder sb, boolean autoCommit) {\n\t\tif (autoCommit) {\n\t\t\tsb.append(\"SET autocommit=1;\");\n\t\t} else {\n\t\t\tsb.append(\"SET autocommit=0;\");\n\t\t}\n\t}\n\tprivate void getTxReadonly(StringBuilder sb, boolean txReadonly) {\n\t\tif (txReadonly) {\n\t\t\tsb.append(\"SET SESSION TRANSACTION READ ONLY;\");\n\t\t} else {\n\t\t\tsb.append(\"SET SESSION TRANSACTION READ WRITE;\");\n\t\t}\n\t}\n\tprivate void getSqlSelectLimit(StringBuilder sb, int sqlSelectLimit) {\n\t\tif (sqlSelectLimit == -1) {\n\t\t\tsb.append(\"SET SQL_SELECT_LIMIT=DEFAULT;\");\n\t\t} else {\n\t\t\tsb.append(\"SET SQL_SELECT_LIMIT=\").append(sqlSelectLimit).append(\";\");\n\t\t}\n\t}\n\n\tprivate static class StatusSync {\n\t\tprivate final String schema;\n\t\tprivate final Integer charsetIndex;\n\t\tprivate final Integer txtIsolation;\n\t\tprivate final Boolean autocommit;\n\t\tprivate final AtomicInteger synCmdCount;\n\t\tprivate final boolean xaStarted;\n\t\tprivate final Boolean txReadonly;\n\t\tprivate final Integer sqlSelectLimit;\n\n\t\tpublic StatusSync(boolean xaStarted, String schema,\n\t\t\t\tInteger charsetIndex, Integer txtIsolation, Boolean autocommit,\n\t\t\t\tint synCount, boolean txReadonly, Integer sqlSelectLimit) {\n\t\t\tsuper();\n\t\t\tthis.xaStarted = xaStarted;\n\t\t\tthis.schema = schema;\n\t\t\tthis.charsetIndex = charsetIndex;\n\t\t\tthis.txtIsolation = txtIsolation;\n\t\t\tthis.autocommit = autocommit;\n\t\t\tthis.synCmdCount = new AtomicInteger(synCount);\n\t\t\tthis.txReadonly = txReadonly;\n\t\t\tthis.sqlSelectLimit = sqlSelectLimit;\n\t\t}\n\n\t\tpublic boolean synAndExecuted(MySQLConnection conn) {\n\t\t\tint remains = synCmdCount.decrementAndGet();\n\t\t\tif (remains == 0) {// syn command finished\n\t\t\t\tthis.updateConnectionInfo(conn);\n\t\t\t\tconn.metaDataSyned = true;\n\t\t\t\treturn false;\n\t\t\t} else if (remains < 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate void updateConnectionInfo(MySQLConnection conn)\n\n\t\t{\n\t\t\tif (schema != null) {\n\t\t\t\tconn.schema = schema;\n\t\t\t\tconn.oldSchema = conn.schema;\n\t\t\t}\n\t\t\tif (charsetIndex != null) {\n\t\t\t\tconn.setCharset(CharsetUtil.getCharset(charsetIndex));\n\t\t\t}\n\t\t\tif (txtIsolation != null) {\n\t\t\t\tconn.txIsolation = txtIsolation;\n\t\t\t}\n\t\t\tif (autocommit != null) {\n\t\t\t\tconn.autocommit = autocommit;\n\t\t\t}\n\t\t\tif (txReadonly != null) {\n\t\t\t\tconn.txReadonly = txReadonly;\n\t\t\t}\n\t\t\tif (sqlSelectLimit != null) {\n\t\t\t\tconn.sqlSelectLimit = sqlSelectLimit;\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * @return if synchronization finished and execute-sql has already been sent\n\t *         before\n\t */\n\tpublic boolean syncAndExcute() {\n\t\tStatusSync sync = this.statusSync;\n\t\tif (sync == null) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\tboolean executed = sync.synAndExecuted(this);\n\t\t\tif (executed) {\n\t\t\t\tstatusSync = null;\n\t\t\t}\n\t\t\treturn executed;\n\t\t}\n\n\t}\n\n\tpublic void execute(RouteResultsetNode rrn, ServerConnection sc,\n\t\t\tboolean autocommit) throws UnsupportedEncodingException {\n\t\tif (!modifiedSQLExecuted && rrn.isModifySQL()) {\n\t\t\tmodifiedSQLExecuted = true;\n\t\t}\n\t\tString xaTXID = null;\n\t\tif(sc.getSession2().getXaTXID()!=null){\n\t\t\txaTXID = sc.getSession2().getXaTXID()+\",'\"+getSchema()+\"'\";\n\t\t}\n\t\tsynAndDoExecute(xaTXID, rrn, sc.getCharsetIndex(), sc.getTxIsolation(),\n\t\t\t\tautocommit, sc.isTxReadonly(), sc.getSqlSelectLimit());\n\t}\n\n\tprivate void synAndDoExecute(String xaTxID, RouteResultsetNode rrn,\n\t\t\tint clientCharSetIndex, int clientTxIsoLation,\n\t\t\tboolean clientAutoCommit, boolean clientTxReadonly, int clientSqlSelectLimit) {\n\t\tString xaCmd = null;\n\n\t\tboolean conAutoComit = this.autocommit;\n\t\tboolean conTxReadonly = this.txReadonly;\n\t\tint conSqlSelectLimit = this.sqlSelectLimit;\n\t\tString conSchema = this.schema;\n\t\tboolean strictTxIsolation = MycatServer.getInstance().getConfig().getSystem().isStrictTxIsolation();\n\t\tboolean expectAutocommit = false;\n\t\t// 如果在非自动提交情况下,如果需要严格保证事务级别,则需做下列判断\n\t\tif (strictTxIsolation) {\n\t\t\texpectAutocommit = isFromSlaveDB() || clientAutoCommit;\n\t\t} else {\n\t\t\t// never executed modify sql,so auto commit\n\t\t\texpectAutocommit = (!modifiedSQLExecuted || isFromSlaveDB() || clientAutoCommit);\n\t\t}\n\t\tif (expectAutocommit == false && xaTxID != null && xaStatus == TxState.TX_INITIALIZE_STATE) {\n\t\t\t//clientTxIsoLation = Isolations.SERIALIZABLE;\n\t\t\txaCmd = \"XA START \" + xaTxID + ';';\n\t\t\tthis.xaStatus = TxState.TX_STARTED_STATE;\n\t\t}\n\t\tint schemaSyn = conSchema.equals(oldSchema) ? 0 : 1;\n\t\tint charsetSyn = 0;\n\t\tif (this.charsetIndex != clientCharSetIndex) {\n\t\t\t//need to syn the charset of connection.\n\t\t\t//set current connection charset to client charset.\n\t\t\t//otherwise while sending commend to server the charset will not coincidence.\n\t\t\tsetCharset(CharsetUtil.getCharset(clientCharSetIndex));\n\t\t\tcharsetSyn = 1;\n\t\t}\n\t\tint txIsoLationSyn = (txIsolation == clientTxIsoLation) ? 0 : 1;\n\t\tint autoCommitSyn = (conAutoComit == expectAutocommit) ? 0 : 1;\n\t\tint txReadonlySyn = (conTxReadonly == clientTxReadonly) ? 0 : 1;\n\t\tint sqlSelectLimitSyn = (conSqlSelectLimit == clientSqlSelectLimit) ? 0 : 1;\n\t\tint synCount = schemaSyn + charsetSyn + txIsoLationSyn + autoCommitSyn + (xaCmd!=null?1:0) + txReadonlySyn\n\t\t\t+ sqlSelectLimitSyn;\n//\t\tif (synCount == 0 && this.xaStatus != TxState.TX_STARTED_STATE) {\n\t\tif (synCount == 0 ) {\n\t\t\t// not need syn connection\n//\t\t\tif (LOGGER.isDebugEnabled()) {\n//\t\t\t\tLOGGER.debug(\"not need syn connection :\\n\" + this+\"\\n to send query cmd:\\n\"+rrn.getStatement()\n//\t\t\t\t\t\t+\"\\n in pool\\n\"\n//\t\t\t\t+this.getPool().getConfig());\n//\t\t\t}\n\t\t\tif (rrn.getSqlType() == ServerParse.COMMAND) {\n\t\t\t\tthis.sendComFieldListCmd(rrn.getStatement() + \";\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsendQueryCmd(rrn.getStatement());\n\t\t\treturn;\n\t\t}\n\t\tCommandPacket schemaCmd = null;\n\t\tStringBuilder sb = new StringBuilder();\n\t\tif (schemaSyn == 1) {\n\t\t\tschemaCmd = getChangeSchemaCommand(conSchema);\n\t\t\t// getChangeSchemaCommand(sb, conSchema);\n\t\t}\n\n\t\tif (charsetSyn == 1) {\n\t\t\tgetCharsetCommand(sb, clientCharSetIndex);\n\t\t}\n\t\tif (txIsoLationSyn == 1) {\n\t\t\tgetTxIsolationCommand(sb, clientTxIsoLation);\n\t\t}\n\t\tif (autoCommitSyn == 1) {\n\t\t\tgetAutocommitCommand(sb, expectAutocommit);\n\t\t}\n\t\tif (txReadonlySyn == 1) {\n\t\t\tgetTxReadonly(sb, false);\n\t\t}\n\t\tif (sqlSelectLimitSyn == 1) {\n\t\t\tgetSqlSelectLimit(sb, clientSqlSelectLimit);\n\t\t}\n\t\tif (xaCmd != null) {\n\t\t\tsb.append(xaCmd);\n\t\t}\n\t\tmetaDataSyned = false;\n\t\tstatusSync = new StatusSync(xaCmd != null,\n\t\t\t\t\t\t\t\t\tconSchema,\n\t\t\t\t\t\t\t\t\tclientCharSetIndex,\n\t\t\t\t\t\t\t\t\tclientTxIsoLation,\n\t\t\t\t\t\t\t\t\texpectAutocommit,\n\t\t\t\t\t\t\t\t\tsynCount,\n\t\t\t\t\t\t\t\t\tclientTxReadonly, clientSqlSelectLimit);\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"con need syn ,total syn cmd \" + synCount\n\t\t\t\t\t+ \" commands \" + sb.toString() + \"schema change:\"\n\t\t\t\t\t+ (schemaCmd != null) + \" con:\" + this);\n\t\t}\n\t\t// syn schema\n\t\tif (schemaCmd != null) {\n\t\t\tschemaCmd.write(this);\n\t\t}\n\n\t\tif(rrn.getSqlType() == ServerParse.COMMAND ) {\n\t\t\tif(sb.length() > 0 ) {\n\t\t\t\tstatusSync.synCmdCount.incrementAndGet();\n\t\t\t\tthis.sendQueryCmd(sb.toString());\n\t\t\t}\n\t\t\tthis.sendComFieldListCmd(rrn.getStatement()+\";\");\n\t\t\treturn ;\n\t\t}\n\t\t// and our query sql to multi command at last\n\t\tsb.append(rrn.getStatement()+\";\");\n\t\t// syn and execute others\n\t\tthis.sendQueryCmd(sb.toString());\n\t\t// waiting syn result...\n\n\t}\n\n\tprivate static CommandPacket getChangeSchemaCommand(String schema) {\n\t\tCommandPacket cmd = new CommandPacket();\n\t\tcmd.packetId = 0;\n\t\tcmd.command = MySQLPacket.COM_INIT_DB;\n\t\tcmd.arg = schema.getBytes();\n\t\treturn cmd;\n\t}\n\n\t/**\n\t * by wuzh ,execute a query and ignore transaction settings for performance\n\t * \n\t * @param query\n\t * @throws UnsupportedEncodingException\n\t */\n\tpublic void query(String query) throws UnsupportedEncodingException {\n\t\tRouteResultsetNode rrn = new RouteResultsetNode(\"default\",\n\t\t\t\tServerParse.SELECT, query);\n\n\t\tsynAndDoExecute(null, rrn, this.charsetIndex, this.txIsolation, true, this.txReadonly, this.sqlSelectLimit);\n\n\t}\n\t/**\n\t * by zwy ,execute a query with charsetIndex\n\t * \n\t * @param query\n\t * @throws UnsupportedEncodingException\n\t */\n\t@Override\n\tpublic void query(String query, int charsetIndex) {\n\t\tRouteResultsetNode rrn = new RouteResultsetNode(\"default\",\n\t\t\t\tServerParse.SELECT, query);\n\n\t\tsynAndDoExecute(null, rrn, charsetIndex, this.txIsolation, true, this.txReadonly, this.sqlSelectLimit);\n\t\t\n\t}\n\tpublic long getLastTime() {\n\t\treturn lastTime;\n\t}\n\n\tpublic void setLastTime(long lastTime) {\n\t\tthis.lastTime = lastTime;\n\t}\n\n\tpublic void quit() {\n\t\tif (isQuit.compareAndSet(false, true) && !isClosed()) {\n\t\t\tif (isAuthenticated) {\n\t\t\t\twrite(writeToBuffer(QuitPacket.QUIT, allocate()));\n\t\t\t\twrite(allocate());\n\t\t\t} else {\n\t\t\t\tclose(\"normal\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close(String reason) {\n\t\tif (!isClosed.get()) {\n\t\t\tisQuit.set(true);\n\t\t\tResponseHandler tmpRespHandlers= respHandler;\n\t\t\tsetResponseHandler(null);\n\t\t\tsuper.close(reason);\n\t\t\tpool.connectionClosed(this);\n\t\t\tif (tmpRespHandlers != null) {\n\t\t\t\ttmpRespHandlers.connectionClose(this, reason);\n\t\t\t}\n\t\t\tif( this.handler instanceof MySQLConnectionAuthenticator) {\n\t\t\t\t((MySQLConnectionAuthenticator) this.handler).connectionError(this, new Throwable(reason));\n\t\t\t\t\n\t\t\t}\n\t\t} else {\n\t\t\t//主要起一个清理资源的作用\n\t\t\tsuper.close(reason);\n\t\t}\n\t}\n\n    @Override\n    public void closeWithoutRsp(String reason) {\n        // TODO Auto-generated method stub\n        this.respHandler = null;\n        this.close(reason);\n    }\n\n\tpublic void commit() {\n\n\t\t_COMMIT.write(this);\n\n\t}\n\n\tpublic boolean batchCmdFinished() {\n\t\tbatchCmdCount--;\n\t\treturn (batchCmdCount == 0);\n\t}\n\n\tpublic void execCmd(String cmd) {\n\t\tthis.sendQueryCmd(cmd);\n\t}\n\n\tpublic void execBatchCmd(String[] batchCmds) {\n\t\t// \"XA END \"+xaID+\";\"+\"XA PREPARE \"+xaID\n\t\tthis.batchCmdCount = batchCmds.length;\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (String sql : batchCmds) {\n\t\t\tsb.append(sql).append(';');\n\t\t}\n\t\tthis.sendQueryCmd(sb.toString());\n\t}\n\n\tpublic void rollback() {\n\t\t_ROLLBACK.write(this);\n\t}\n\n\tpublic void release() {\n\t\tif (metaDataSyned == false) {// indicate connection not normalfinished\n\t\t\t\t\t\t\t\t\t\t// ,and\n\t\t\t\t\t\t\t\t\t\t// we can't know it's syn status ,so\n\t\t\t\t\t\t\t\t\t\t// close\n\t\t\t\t\t\t\t\t\t\t// it\n\t\t\tLOGGER.warn(\"can't sure connection syn result,so close it \" + this);\n\t\t\tthis.respHandler = null;\n\t\t\tthis.close(\"syn status unkown \");\n\t\t\treturn;\n\t\t}\n\t\tmetaDataSyned = true;\n\t\tattachment = null;\n\t\tstatusSync = null;\n\t\tmodifiedSQLExecuted = false;\t\t\n\t\txaStatus = TxState.TX_INITIALIZE_STATE;\n\t\tsetResponseHandler(null);\n\t\tpool.releaseChannel(this);\n\t}\n\n\tpublic boolean setResponseHandler(ResponseHandler queryHandler) {\n\t\tif (handler instanceof MySQLConnectionHandler) {\n\t\t\t((MySQLConnectionHandler) handler).setResponseHandler(queryHandler);\n\t\t\trespHandler = queryHandler;\n\t\t\treturn true;\n\t\t} else if (queryHandler != null) {\n\t\t\tLOGGER.warn(\"set not MySQLConnectionHandler \"\n\t\t\t\t\t+ queryHandler.getClass().getCanonicalName());\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 写队列为空，可以继续写数据\n\t */\n\tpublic void writeQueueAvailable() {\n\t\tif (respHandler != null) {\n\t\t\trespHandler.writeQueueAvailable();\n\t\t}\n\t}\n\n\t/**\n\t * 记录sql执行信息\n\t */\n\tpublic void recordSql(String host, String schema, String stmt) {\n\t\t// final long now = TimeUtil.currentTimeMillis();\n\t\t// if (now > this.lastTime) {\n\t\t// // long time = now - this.lastTime;\n\t\t// // SQLRecorder sqlRecorder = this.pool.getSqlRecorder();\n\t\t// // if (sqlRecorder.check(time)) {\n\t\t// // SQLRecord recorder = new SQLRecord();\n\t\t// // recorder.host = host;\n\t\t// // recorder.schema = schema;\n\t\t// // recorder.statement = stmt;\n\t\t// // recorder.startTime = lastTime;\n\t\t// // recorder.executeTime = time;\n\t\t// // recorder.dataNode = pool.getName();\n\t\t// // recorder.dataNodeIndex = pool.getIndex();\n\t\t// // sqlRecorder.add(recorder);\n\t\t// // }\n\t\t// }\n\t\t// this.lastTime = now;\n\t}\n\n\tprivate static byte[] passwd(String pass, HandshakePacket hs)\n\t\t\tthrows NoSuchAlgorithmException {\n\t\tif (pass == null || pass.length() == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tbyte[] passwd = pass.getBytes();\n\t\tint sl1 = hs.seed.length;\n\t\tint sl2 = hs.restOfScrambleBuff.length;\n\t\tbyte[] seed = new byte[sl1 + sl2];\n\t\tSystem.arraycopy(hs.seed, 0, seed, 0, sl1);\n\t\tSystem.arraycopy(hs.restOfScrambleBuff, 0, seed, sl1, sl2);\n\t\treturn SecurityUtil.scramble411(passwd, seed);\n\t}\n\n\t@Override\n\tpublic boolean isFromSlaveDB() {\n\t\treturn fromSlaveDB;\n\t}\n\n\t@Override\n\tpublic boolean isBorrowed() {\n\t\treturn borrowed;\n\t}\n\n\t@Override\n\tpublic void setBorrowed(boolean borrowed) {\n\t\tthis.lastTime = TimeUtil.currentTimeMillis();\n\t\tthis.borrowed = borrowed;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"MySQLConnection@\"+ hashCode() +\" [id=\" + id + \", lastTime=\" + lastTime\n\t\t\t\t+ \", user=\" + user\n\t\t\t\t+ \", schema=\" + schema + \", old shema=\" + oldSchema\n\t\t\t\t+ \", borrowed=\" + borrowed + \", fromSlaveDB=\" + fromSlaveDB\n\t\t\t\t+ \", threadId=\" + threadId + \", charset=\" + charset\n\t\t\t\t+ \", txIsolation=\" + txIsolation + \", autocommit=\" + autocommit + \", txReadonly=\" + txReadonly\n\t\t\t\t+ \", attachment=\" + attachment + \", respHandler=\" + respHandler\n\t\t\t\t+ \", host=\" + host + \", port=\" + port + \", statusSync=\"\n\t\t\t\t+ statusSync + \", writeQueue=\" + this.getWriteQueue().size()\n\t\t\t\t+ \", modifiedSQLExecuted=\" + modifiedSQLExecuted + \"]\";\n\t}\n\n\t@Override\n\tpublic boolean isModifiedSQLExecuted() {\n\t\treturn modifiedSQLExecuted;\n\t}\n\n\t@Override\n\tpublic int getTxIsolation() {\n\t\treturn txIsolation;\n\t}\n\n    @Override\n    public void disableRead() {\n        this.getSocketWR().disableRead();\n    }\n\n    @Override\n    public void enableRead() {\n        this.getSocketWR().enableRead();\n    }\n\t\n\t// 是否需要同步schmea\n    public boolean isNeedSyncSchema() {\n        return schema.equals(oldSchema) ? false : true;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/MySQLConnectionAuthenticator.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.CharsetUtil;\nimport io.mycat.backend.mysql.SecurityUtil;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.config.Capabilities;\nimport io.mycat.net.ConnectionException;\nimport io.mycat.net.NIOHandler;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.HandshakePacket;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.net.mysql.Reply323Packet;\n\n/**\n * MySQL 验证处理器\n * \n * @author mycat\n */\npublic class MySQLConnectionAuthenticator implements NIOHandler {\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(MySQLConnectionAuthenticator.class);\n\tprivate final MySQLConnection source;\n\tprivate final ResponseHandler listener;\n\n\tpublic MySQLConnectionAuthenticator(MySQLConnection source,\n\t\t\tResponseHandler listener) {\n\t\tthis.source = source;\n\t\tthis.listener = listener;\n\t}\n\n\tpublic void connectionError(MySQLConnection source, Throwable e) {\n\t\tlistener.connectionError(e, source);\n\t}\n\n\t@Override\n\tpublic void handle(byte[] data) {\n\t\ttry {\n\t\t\tswitch (data[4]) {\n\t\t\tcase OkPacket.FIELD_COUNT:\n\t\t\t\tHandshakePacket packet = source.getHandshake();\n\t\t\t\tif (packet == null) {\n\t\t\t\t\tprocessHandShakePacket(data);\n\t\t\t\t\t// 发送认证数据包\n\t\t\t\t\tsource.authenticate();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t// 处理认证结果\n\t\t\t\tsource.setHandler(new MySQLConnectionHandler(source));\n\t\t\t\tsource.setAuthenticated(true);\n\t\t\t\tboolean clientCompress = Capabilities.CLIENT_COMPRESS==(Capabilities.CLIENT_COMPRESS & packet.serverCapabilities);\n\t\t\t\tboolean usingCompress= MycatServer.getInstance().getConfig().getSystem().getUseCompression()==1 ;\n\t\t\t\tif(clientCompress&&usingCompress)\n\t\t\t\t{\n\t\t\t\t\tsource.setSupportCompress(true);\n\t\t\t\t}\n\t\t\t\tif (listener != null) {\n\t\t\t\t\tlistener.connectionAcquired(source);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ErrorPacket.FIELD_COUNT:\n\t\t\t\tErrorPacket err = new ErrorPacket();\n\t\t\t\terr.read(data);\n\t\t\t\tString errMsg = new String(err.message);\n\t\t\t\tLOGGER.warn(\"can't connect to mysql server ,errmsg:\"+errMsg+\" \"+source);\n\t\t\t\t//source.close(errMsg);\n\t\t\t\tthrow new ConnectionException(err.errno, errMsg);\n\n\t\t\tcase EOFPacket.FIELD_COUNT:\n\t\t\t\tauth323(data[3]);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tpacket = source.getHandshake();\n\t\t\t\tif (packet == null) {\n\t\t\t\t\tprocessHandShakePacket(data);\n\t\t\t\t\t// 发送认证数据包\n\t\t\t\t\tsource.authenticate();\n\t\t\t\t\tbreak;\n\t\t\t\t} else {\n\t\t\t\t\tthrow new RuntimeException(\"Unknown Packet!\");\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} catch (RuntimeException e) {\n\t\t\tif (listener != null) {\n\t\t\t\tlistener.connectionError(e, source);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tprivate void processHandShakePacket(byte[] data) {\n\t\t// 设置握手数据包\n\t\tHandshakePacket packet= new HandshakePacket();\n\t\tpacket.read(data);\n\t\tsource.setHandshake(packet);\n\t\tsource.setThreadId(packet.threadId);\n\n\t\t// 设置字符集编码\n\t\tint charsetIndex = (packet.serverCharsetIndex & 0xff);\n\t\tString charset = CharsetUtil.getCharset(charsetIndex);\n\t\tif (charset != null) {\n\t\t\tsource.setCharset(charset);\n\t\t} else {\n\t\t\tthrow new RuntimeException(\"Unknown charsetIndex:\" + charsetIndex);\n\t\t}\n\t}\n\n\tprivate void auth323(byte packetId) {\n\t\t// 发送323响应认证数据包\n\t\tReply323Packet r323 = new Reply323Packet();\n\t\tr323.packetId = ++packetId;\n\t\tString pass = source.getPassword();\n\t\tif (pass != null && pass.length() > 0) {\n\t\t\tbyte[] seed = source.getHandshake().seed;\n\t\t\tr323.seed = SecurityUtil.scramble323(pass, new String(seed))\n\t\t\t\t\t.getBytes();\n\t\t}\n\t\tr323.write(source);\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/MySQLConnectionFactory.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.nio.channels.AsynchronousSocketChannel;\nimport java.nio.channels.CompletionHandler;\nimport java.nio.channels.NetworkChannel;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.config.model.DBHostConfig;\nimport io.mycat.net.NIOConnector;\nimport io.mycat.net.factory.BackendConnectionFactory;\n\n/**\n * @author mycat\n */\npublic class MySQLConnectionFactory extends BackendConnectionFactory {\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tpublic MySQLConnection make(MySQLDataSource pool, ResponseHandler handler,\n\t\t\tString schema) throws IOException {\n\n\t\tDBHostConfig dsc = pool.getConfig();\n\t\tNetworkChannel channel = openSocketChannel(MycatServer.getInstance()\n\t\t\t\t.isAIO());\n\n\t\tMySQLConnection c = new MySQLConnection(channel, pool.isReadNode());\n\t\tMycatServer.getInstance().getConfig().setSocketParams(c, false);\n\t\tc.setHost(dsc.getIp());\n\t\tc.setPort(dsc.getPort());\n\t\tc.setUser(dsc.getUser());\n\t\tc.setPassword(dsc.getPassword());\n\t\tc.setSchema(schema);\n\t\tc.setHandler(new MySQLConnectionAuthenticator(c, handler));\n\t\tc.setPool(pool);\n\t\tc.setIdleTimeout(pool.getConfig().getIdleTimeout());\n\t\tif (channel instanceof AsynchronousSocketChannel) {\n\t\t\t((AsynchronousSocketChannel) channel).connect(\n\t\t\t\t\tnew InetSocketAddress(dsc.getIp(), dsc.getPort()), c,\n\t\t\t\t\t(CompletionHandler) MycatServer.getInstance()\n\t\t\t\t\t\t\t.getConnector());\n\t\t} else {\n\t\t\t((NIOConnector) MycatServer.getInstance().getConnector())\n\t\t\t\t\t.postConnect(c);\n\n\t\t}\n\t\treturn c;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/MySQLConnectionHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.mysql.ByteUtil;\nimport io.mycat.backend.mysql.nio.handler.LoadDataResponseHandler;\nimport io.mycat.backend.mysql.nio.handler.PrepareRequestHandler;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.net.handler.BackendAsyncHandler;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.net.mysql.PreparedOkPacket;\nimport io.mycat.net.mysql.RequestFilePacket;\nimport io.mycat.route.RouteResultsetNode;\n\n/**\n * life cycle: from connection establish to close <br/>\n *\n * @author mycat\n */\npublic class MySQLConnectionHandler extends BackendAsyncHandler {\n\tprivate static final Logger logger = LoggerFactory\n\t\t\t.getLogger(MySQLConnectionHandler.class);\n\tprivate static final int RESULT_STATUS_INIT = 0;\n\tprivate static final int RESULT_STATUS_HEADER = 1;\n\tprivate static final int RESULT_STATUS_FIELD_EOF = 2;\n    private static final int RESULT_STATUS_PREPARE_RESPONSE_OK = 3;\n\n\tprivate final MySQLConnection source;\n\tprivate volatile int resultStatus;\n\tprivate volatile byte[] header;\n\tprivate volatile List<byte[]> fields;\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(MySQLConnectionHandler.class);\n\t/**\n\t * life cycle: one SQL execution\n\t */\n\tprivate volatile ResponseHandler responseHandler;\n\n\tpublic MySQLConnectionHandler(MySQLConnection source) {\n\t\tthis.source = source;\n\t\tthis.resultStatus = RESULT_STATUS_INIT;\n\t}\n\n\tpublic void connectionError(Throwable e) {\n\t\tif (responseHandler != null) {\n\t\t\tresponseHandler.connectionError(e, source);\n\t\t}\n\t\tLOGGER.error(\"connectionError but not handle\");\n\t}\n\n\tpublic MySQLConnection getSource() {\n\t\treturn source;\n\t}\n\n\t@Override\n\tpublic void handle(byte[] data) {\n\t\tofferData(data, source.getProcessor().getExecutor());\n\t}\n\n\t@Override\n\tprotected void offerDataError() {\n\t\tresultStatus = RESULT_STATUS_INIT;\n\t\tthrow new RuntimeException(\"offer data error!\");\n\t}\n\n\t@Override\n\tprotected void handleData(byte[] data) {\n\t\tswitch (resultStatus) {\n\t\t\tcase RESULT_STATUS_INIT:\n\t\t\t\tswitch (data[4]) {\n\t\t\t\t\tcase OkPacket.FIELD_COUNT:\n                        int packetSize = (int) ByteUtil.readLength(data, 0);\n                        // COM_STMT_PREPARE_OK状态标识 [00]，和OK包一致，区别是COM_STMT_PREPARE_OK固定长度为12\n                        if (packetSize == PreparedOkPacket.PACKET_SIZE\n                                && (responseHandler != null && responseHandler instanceof PrepareRequestHandler)) {\n                            handlePrepareOkPacket(data);\n                            resultStatus = RESULT_STATUS_PREPARE_RESPONSE_OK;\n                        } else {\n                            handleOkPacket(data);\n                        }\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ErrorPacket.FIELD_COUNT:\n\t\t\t\t\t\thandleErrorPacket(data);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RequestFilePacket.FIELD_COUNT:\n\t\t\t\t\t\thandleRequestPacket(data);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresultStatus = RESULT_STATUS_HEADER;\n\t\t\t\t\t\theader = data;\n\t\t\t\t\t\tfields = new ArrayList<byte[]>((int) ByteUtil.readLength(data,\n\t\t\t\t\t\t\t\t4));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase RESULT_STATUS_HEADER:\n\t\t\t\tswitch (data[4]) {\n\t\t\t\t\tcase ErrorPacket.FIELD_COUNT:\n\t\t\t\t\t\tresultStatus = RESULT_STATUS_INIT;\n\t\t\t\t\t\thandleErrorPacket(data);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase EOFPacket.FIELD_COUNT:\n\t\t\t\t\t\tresultStatus = RESULT_STATUS_FIELD_EOF;\n\t\t\t\t\t\thandleFieldEofPacket(data);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tfields.add(data);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase RESULT_STATUS_FIELD_EOF:\n\t\t\t\tswitch (data[4]) {\n\t\t\t\t\tcase ErrorPacket.FIELD_COUNT:\n\t\t\t\t\t\tresultStatus = RESULT_STATUS_INIT;\n\t\t\t\t\t\thandleErrorPacket(data);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase EOFPacket.FIELD_COUNT:\n\t\t\t\t\t\tresultStatus = RESULT_STATUS_INIT;\n\t\t\t\t\t\thandleRowEofPacket(data);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\thandleRowPacket(data);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase RESULT_STATUS_PREPARE_RESPONSE_OK:\n                handlePrepareOkPacket(data);\n\t\t\t    break;\n\t\t\tdefault:\n\t\t\t\tthrow new RuntimeException(\"unknown status!\");\n\t\t}\n\t}\n\n\tpublic void setResponseHandler(ResponseHandler responseHandler) {\n\t\t// logger.info(\"set response handler \"+responseHandler);\n\t\t// if (this.responseHandler != null && responseHandler != null) {\n\t\t// throw new RuntimeException(\"reset agani!\");\n\t\t// }\n\t\t// 连接释放后如果结果状态不正确则尝试设置一下\n\t\tif( responseHandler==null && resultStatus!=RESULT_STATUS_INIT ) {\n\t\t\tlogger.warn(\"try to reset resultStatus. last responseHandler:{}, resultStatus = {}\", this.responseHandler, resultStatus);\n\t\t\tresultStatus = RESULT_STATUS_INIT;\n\t\t}\n\t\tthis.responseHandler = responseHandler;\n\t}\n\n\tprivate void handleLogNodeInfo(String pkgName) {\n\t\tObject att = source.getAttachment();\n\t\tif(LOGGER.isDebugEnabled() && att!=null && att instanceof RouteResultsetNode){\n\t\t\tRouteResultsetNode rrn = (RouteResultsetNode) att;\n\t\t\tString sql =  rrn.getStatement();\n\t\t\tif(sql!=null){\n\t\t\t\tsql = sql.replaceAll(\"[\\r\\n]+\", \"\");\n\t\t\t}\n\t\t\tLOGGER.debug(\"{} MySQLConnection@{} [id={}] for node={}, sql={}\",\n\t\t\t\tnew Object[]{pkgName, source.hashCode(), source.getId(), rrn.getName(), sql});\n\t\t}\n\t}\n    /**\n     * prepare response OK数据包处理\n     */\n    private void handlePrepareOkPacket(byte[] data) {\n        ResponseHandler respHand = responseHandler;\n        if (respHand != null) {\n            respHand.okResponse(data, source);\n            if (((PrepareRequestHandler) respHand).isLastPacket()) {\n                this.resultStatus = RESULT_STATUS_INIT;\n            }\n        } else {\n            LOGGER.error(\"receive OkPacket but not handle\");\n            source.close(\"receive OkPacket but not handle\");\n        }\n    }\n\n\t/**\n\t * OK数据包处理\n\t */\n\tprivate void handleOkPacket(byte[] data) {\n\t\tResponseHandler respHand = responseHandler;\n\t\tif (respHand != null) {\n\t\t\thandleLogNodeInfo(\"handleOkPacket\");\n\t\t\trespHand.okResponse(data, source);\n\t\t}else {\n\t\t\tLOGGER.error(\"receive OkPacket but not handle\");\n\t\t}\n\t}\n\n\t/**\n\t * ERROR数据包处理\n\t */\n\tprivate void handleErrorPacket(byte[] data) {\n\t\tResponseHandler respHand = responseHandler;\n\t\tif (respHand != null) {\n\t\t\thandleLogNodeInfo(\"handleErrorPacket\");\n\t\t\trespHand.errorResponse(data, source);\n\t\t} else {\n\t\t\tLOGGER.error(\"receive ErrorPacket but no handler\");\n\t\t\tcloseNoHandler();\n\t\t}\n\t}\n\n\t/**\n\t * load data file 请求文件数据包处理\n\t */\n\tprivate void handleRequestPacket(byte[] data) {\n\t\tResponseHandler respHand = responseHandler;\n\t\tif (respHand != null && respHand instanceof LoadDataResponseHandler) {\n\t\t\t((LoadDataResponseHandler) respHand).requestDataResponse(data,\n\t\t\t\t\tsource);\n\t\t} else {\n\t\t\tLOGGER.error(\"receive RequestPacket but no handler\");\n\t\t\tcloseNoHandler();\n\t\t}\n\t}\n\n\t/**\n\t * 字段数据包结束处理\n\t */\n\tprivate void handleFieldEofPacket(byte[] data) {\n\t\tResponseHandler respHand = responseHandler;\n\t\tif (respHand != null) {\n\t\t\thandleLogNodeInfo(\"handleFieldEofPacket\");\n\t\t\trespHand.fieldEofResponse(header, fields, data, source);\n\t\t} else {\n\t\t\tLOGGER.error(\"receive FieldEofPacket but no handler\");\n\t\t\tcloseNoHandler();\n\t\t}\n\t}\n\n\t/**\n\t * 行数据包处理\n\t */\n\tprivate void handleRowPacket(byte[] data) {\n\t\tResponseHandler respHand = responseHandler;\n\t\tif (respHand != null) {\n\t\t\trespHand.rowResponse(data, source);\n\t\t} else {\n\t\t\tLOGGER.error(\"receive RowPacket but no handler\");\n\t\t\tcloseNoHandler();\n\n\t\t}\n\t}\n\n\tprivate void closeNoHandler() {\n\t\tif (!source.isClosedOrQuit()) {\n\t\t\tsource.close(\"no handler\");\n\t\t\tlogger.warn(\"no handler bind in this con \" + this + \" client:\"\n\t\t\t\t\t+ source);\n\t\t}\n\t}\n\n\t/**\n\t * 行数据包结束处理\n\t */\n\tprivate void handleRowEofPacket(byte[] data) {\n\t\tif (responseHandler != null) {\n\t\t\thandleLogNodeInfo(\"handleRowEofPacket\");\n\t\t\tresponseHandler.rowEofResponse(data, source);\n\t\t} else {\n\t\t\tLOGGER.error(\"receive RowEofPacket but no handler\");\n\t\t\tcloseNoHandler();\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/MySQLDataSource.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.Socket;\nimport java.security.NoSuchAlgorithmException;\n\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.heartbeat.DBHeartbeat;\nimport io.mycat.backend.heartbeat.MySQLHeartbeat;\nimport io.mycat.backend.mysql.SecurityUtil;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.config.Capabilities;\nimport io.mycat.config.model.DBHostConfig;\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.net.mysql.AuthPacket;\nimport io.mycat.net.mysql.BinaryPacket;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.HandshakePacket;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.net.mysql.QuitPacket;\nimport io.mycat.net.mysql.Reply323Packet;\n\n/**\n * @author mycat\n */\npublic class MySQLDataSource extends PhysicalDatasource {\n\n\tprivate final MySQLConnectionFactory factory;\n\n\tpublic MySQLDataSource(DBHostConfig config, DataHostConfig hostConfig,\n\t\t\tboolean isReadNode) {\n\t\tsuper(config, hostConfig, isReadNode);\n\t\tthis.factory = new MySQLConnectionFactory();\n\n\t}\n\n\t@Override\n\tpublic void createNewConnection(ResponseHandler handler,String schema) throws IOException {\n\t\tfactory.make(this, handler,schema);\n\t}\n\t\n\tprivate long getClientFlags() {\t\t\n\t\tint flag = 0;\n        flag |= Capabilities.CLIENT_LONG_PASSWORD;\n        flag |= Capabilities.CLIENT_FOUND_ROWS;\n        flag |= Capabilities.CLIENT_LONG_FLAG;\n        flag |= Capabilities.CLIENT_CONNECT_WITH_DB;\n        // flag |= Capabilities.CLIENT_NO_SCHEMA;\n        // flag |= Capabilities.CLIENT_COMPRESS;\n        flag |= Capabilities.CLIENT_ODBC;\n        // flag |= Capabilities.CLIENT_LOCAL_FILES;\n        flag |= Capabilities.CLIENT_IGNORE_SPACE;\n        flag |= Capabilities.CLIENT_PROTOCOL_41;\n        flag |= Capabilities.CLIENT_INTERACTIVE;\n        // flag |= Capabilities.CLIENT_SSL;\n        flag |= Capabilities.CLIENT_IGNORE_SIGPIPE;\n        flag |= Capabilities.CLIENT_TRANSACTIONS;\n        // flag |= Capabilities.CLIENT_RESERVED;\n        flag |= Capabilities.CLIENT_SECURE_CONNECTION;\n        // client extension\n        // flag |= Capabilities.CLIENT_MULTI_STATEMENTS;\n        // flag |= Capabilities.CLIENT_MULTI_RESULTS;        \n        return flag;\n\t}\n\t\n\t\n\tprivate byte[] passwd(String pass, HandshakePacket hs) throws NoSuchAlgorithmException {\n\t\tif (pass == null || pass.length() == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tbyte[] passwd = pass.getBytes();\n\t\tint sl1 = hs.seed.length;\n\t\tint sl2 = hs.restOfScrambleBuff.length;\n\t\tbyte[] seed = new byte[sl1 + sl2];\n\t\tSystem.arraycopy(hs.seed, 0, seed, 0, sl1);\n\t\tSystem.arraycopy(hs.restOfScrambleBuff, 0, seed, sl1, sl2);\n\t\treturn SecurityUtil.scramble411(passwd, seed);\n\t}\n\t\n\t@Override\n\tpublic boolean testConnection(String schema) throws IOException {\n\t\t\n\t\tboolean isConnected = true;\n\t\t\n\t\tSocket socket = null;\n\t\tInputStream in = null;\n\t\tOutputStream out = null;\t\t\n\t\ttry {\t\t\t\n\t\t\tsocket = new Socket(this.getConfig().getIp(), this.getConfig().getPort());\n\t\t\tsocket.setSoTimeout(1000 * 20);\n\t\t\tsocket.setReceiveBufferSize( 32768 );\n\t\t    socket.setSendBufferSize( 32768 );\n\t\t\tsocket.setTcpNoDelay(true);\n\t        socket.setKeepAlive(true);\n\t        \n\t        in = new BufferedInputStream(socket.getInputStream(), 32768);\n\t\t\tout = new BufferedOutputStream( socket.getOutputStream(), 32768 );\n\t\t\t\n\t\t\t/**\n\t         * Phase 1: MySQL to client. Send handshake packet.\n\t        */\n\t\t\tBinaryPacket bin1 = new BinaryPacket();\n\t\t\tbin1.read(in);\n\t\t\t\n\t\t\tHandshakePacket handshake = new HandshakePacket();\n\t\t\thandshake.read( bin1 );\n\t\t\t\n\t\t\t/**\n\t         * Phase 2: client to MySQL. Send auth packet.\n\t         */\n\t\t\tAuthPacket authPacket = new AuthPacket();\n\t\t\tauthPacket.packetId = 1;\n\t\t\tauthPacket.clientFlags = getClientFlags();\n\t\t\tauthPacket.maxPacketSize = 1024 * 1024 * 16;\n\t\t\tauthPacket.charsetIndex = handshake.serverCharsetIndex & 0xff;\n\t\t\tauthPacket.user = this.getConfig().getUser();;\n\t\t\ttry {\n\t\t\t\tauthPacket.password = passwd(this.getConfig().getPassword(), handshake);\n\t\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\t\tthrow new RuntimeException(e.getMessage());\n\t\t\t}\n\t\t\tauthPacket.database = schema;\n\t\t\tauthPacket.write(out);\n\t\t    out.flush();\n\t\t\t  \n\t\t\t/**\n\t         * Phase 3: MySQL to client. send OK/ERROR packet.\n\t         */\n\t        BinaryPacket bin2 = new BinaryPacket();\n\t        bin2.read(in);\n\t        switch (bin2.data[0]) {\n\t        case OkPacket.FIELD_COUNT:\n\t            break;\n\t        case ErrorPacket.FIELD_COUNT:\n\t            ErrorPacket err = new ErrorPacket();\n\t            err.read(bin2);\n\t            isConnected = false;\n\t        case EOFPacket.FIELD_COUNT:\t\t        \t\n\t        \t// 发送323响应认证数据包\n\t    \t\tReply323Packet r323 = new Reply323Packet();\n\t    \t\tr323.packetId = ++bin2.packetId;\n\t    \t\tString passwd = this.getConfig().getPassword();\n\t    \t\tif (passwd != null && passwd.length() > 0) {\n\t    \t\t\tr323.seed = SecurityUtil.scramble323(passwd, new String(handshake.seed)).getBytes();\n\t    \t\t}\n\t    \t\tr323.write(out);\n\t    \t\tout.flush();\n\t            break;\n\t        }\t\t\t\n\t\t\t\n\t\t} catch (IOException e) {\n\t\t\tisConnected = false;\n\t\t} finally {\t\t\t\n\t\t\ttry {\n\t\t\t\tif (in != null) {\n\t\t\t\t\tin.close();\n\t\t\t\t}\n\t\t\t} catch (IOException e) {}\n\n\t\t\ttry {\n\t\t\t\tif (out != null) {\n\t\t\t\t\tout.write(QuitPacket.QUIT);\n\t\t\t\t\tout.flush();\n\t\t\t\t\tout.close();\n\t\t\t\t}\n\t\t\t} catch (IOException e) {}\n\n\t\t\ttry {\n\t\t\t\tif (socket != null)\n\t\t\t\t\tsocket.close();\n\t\t\t} catch (IOException e) {}\n\t\t}\n\t\t\n\t\treturn isConnected;\n\t}\n\n\t@Override\n\tpublic DBHeartbeat createHeartBeat() {\n\t\treturn new MySQLHeartbeat(this);\n\t}\t\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/CommitNodeHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.mysql.nio.MySQLConnection;\nimport io.mycat.backend.mysql.xa.TxState;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.NonBlockingSession;\n\n/**\n * @author mycat\n */\npublic class CommitNodeHandler extends MultiNodeHandler implements ResponseHandler {\n    private static final Logger LOGGER = LoggerFactory.getLogger(CommitNodeHandler.class);\n\tprivate final NonBlockingSession session;\n    protected byte[] responseData;\n\n\tpublic CommitNodeHandler(NonBlockingSession session) {\n        super(session);\n\t\tthis.session = session;\n    }\n\n    public CommitNodeHandler(NonBlockingSession session, byte[] responseData) {\n        super(session);\n        this.session = session;\n        this.responseData = responseData;\n\n    }\n\n    public void commit() {\n        final int initCount = session.getTargetCount();\n        lock.lock();\n        try {\n            reset(initCount);\n        } finally {\n            lock.unlock();\n        }\n        for (RouteResultsetNode rrn : session.getTargetKeys()) {\n            final BackendConnection conn = session.getTarget(rrn);\n            commit(conn);\n        }\n    }\n\n\tpublic void commit(BackendConnection conn) {\n\t\tconn.setResponseHandler(CommitNodeHandler.this);\n\t\tboolean isClosed=conn.isClosedOrQuit();\n\t\tif(isClosed)\n\t\t{\n\t\t\tsession.getSource().writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR,\n\t\t\t\t\t\"receive commit,but find backend con is closed or quit\");\n\t\t\tLOGGER.error( conn+\"receive commit,but fond backend con is closed or quit\");\n\t\t}\n\t   if(conn instanceof MySQLConnection)\n\t   {\n\t\t   MySQLConnection mysqlCon = (MySQLConnection) conn;\n\t\t   if (mysqlCon.getXaStatus() == 1)\n\t\t   {\n\t\t\t   String xaTxId = session.getXaTXID()+\",'\"+mysqlCon.getSchema()+\"'\";\n\t\t\t   String[] cmds = new String[]{\"XA END \" + xaTxId,\n\t\t\t\t\t   \"XA PREPARE \" + xaTxId};\n\t\t\t   mysqlCon.execBatchCmd(cmds);\n\t\t   } else\n\t\t   {\n\t\t\t   conn.commit();\n\t\t   }\n\t   }else\n\t   {\n\t\t   conn.commit();\n\t   }\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\t\tLOGGER.error(\"unexpected invocation: connectionAcquired from commit\");\n\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] ok, BackendConnection conn) {\n\t\tif(conn instanceof MySQLConnection)\n\t\t{\n\t\t\tMySQLConnection mysqlCon = (MySQLConnection) conn;\n\t\t\tswitch (mysqlCon.getXaStatus())\n\t\t\t{\n\t\t\t\tcase TxState.TX_STARTED_STATE:\n\t\t\t\t\tif (mysqlCon.batchCmdFinished())\n\t\t\t\t\t{\n\t\t\t\t\t\tString xaTxId = session.getXaTXID()+\",'\"+mysqlCon.getSchema()+\"'\";\n\t\t\t\t\t\tmysqlCon.execCmd(\"XA COMMIT \" + xaTxId);\n\t\t\t\t\t\tmysqlCon.setXaStatus(TxState.TX_PREPARED_STATE);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\tcase TxState.TX_PREPARED_STATE:\n\t\t\t\t{\n\t\t\t\t\tmysqlCon.setXaStatus(TxState.TX_INITIALIZE_STATE);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t//\tLOGGER.error(\"Wrong XA status flag!\");\n\t\t\t}\n\t\t\t\n\t\t\t/* 1.  事务提交后,xa 事务结束     */\n\t\t\tif(TxState.TX_INITIALIZE_STATE==mysqlCon.getXaStatus()){\n\t\t\t\tif(session.getXaTXID()!=null){\n\t\t\t\t\tsession.setXATXEnabled(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t/* 2. preAcStates 为true,事务结束后,需要设置为true。preAcStates 为ac上一个状态    */\n        if(session.getSource().isPreAcStates()&&!session.getSource().isAutocommit()){\n        \tsession.getSource().setAutocommit(true);\n        }\n        // session.clearResources(false);\n        // ServerConnection source = session.getSource();\n        // source.write(ok);\n        if (decrementCountBy(1)) {\n            if (responseData != null) {\n                cleanAndFeedback(responseData);\n            } else {\n                cleanAndFeedback(ok);\n            }\n\n        }\n\t}\n\n\t@Override\n\tpublic void errorResponse(byte[] err, BackendConnection conn) {\n\t\tErrorPacket errPkg = new ErrorPacket();\n\t\terrPkg.read(err);\n\t\tString errInfo = new String(errPkg.message);\n\t\tsession.getSource().setTxInterrupt(errInfo);\n        // errPkg.write(session.getSource());\n        if (decrementCountBy(1)) {\n            cleanAndFeedback(errPkg.writeToBytes());\n        }\n\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t\tLOGGER.error(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": field's eof\").toString());\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\t\tLOGGER.error(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": field's eof\").toString());\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t\tLOGGER.warn(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": row data packet\").toString());\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\n\t}\n\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n        if (decrementCountBy(1)) {\n            cleanAndFeedback(createErrPkg(e.getMessage()).writeToBytes());\n        }\n\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n        if (decrementCountBy(1)) {\n            cleanAndFeedback(createErrPkg(reason).writeToBytes());\n        }\n\n\t}\n\n    private void cleanAndFeedback(byte[] responseData) {\n        // clear all resources\n        session.clearResources(false);\n        if (session.closed()) {\n            return;\n        }\n        if (this.isFail()) {\n            createErrPkg(error).write(session.getSource());\n        } else {\n            session.getSource().write(responseData);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/ConnectionHeartBeatHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.net.mysql.ErrorPacket;\n\n/**\n * heartbeat check for mysql connections\n * \n * @author wuzhih\n * \n */\npublic class ConnectionHeartBeatHandler implements ResponseHandler {\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(ConnectionHeartBeatHandler.class);\n\tprotected final ReentrantLock lock = new ReentrantLock();\n\tprivate final ConcurrentHashMap<Long, HeartBeatCon> allCons = new ConcurrentHashMap<Long, HeartBeatCon>();\n\n\tpublic void doHeartBeat(BackendConnection conn, String sql) {\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"do heartbeat for con \" + conn);\n\t\t}\n\n\t\ttry {\n\n\t\t\tHeartBeatCon hbCon = new HeartBeatCon(conn);\n\t\t\tboolean notExist = (allCons.putIfAbsent(hbCon.conn.getId(), hbCon) == null);\n\t\t\tif (notExist) {\n\t\t\t\tconn.setResponseHandler(this);\n\t\t\t\tconn.query(sql);\n\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\texecuteException(conn, e);\n\t\t}\n\t}\n\n\t/**\n\t * remove timeout connections\n\t */\n\tpublic void abandTimeOuttedConns() {\n\t\tif (allCons.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tCollection<BackendConnection> abandCons = new LinkedList<BackendConnection>();\n\t\tlong curTime = System.currentTimeMillis();\n\t\tIterator<Entry<Long, HeartBeatCon>> itors = allCons.entrySet()\n\t\t\t\t.iterator();\n\t\twhile (itors.hasNext()) {\n\t\t\tHeartBeatCon hbCon = itors.next().getValue();\n\t\t\tif (hbCon.timeOutTimestamp < curTime) {\n\t\t\t\tabandCons.add(hbCon.conn);\n\t\t\t\titors.remove();\n\t\t\t}\n\t\t}\n\n\t\tif (!abandCons.isEmpty()) {\n\t\t\tfor (BackendConnection con : abandCons) {\n\t\t\t\ttry {\n\t\t\t\t\t// if(con.isBorrowed())\n\t\t\t\t\tcon.close(\"heartbeat timeout \");\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOGGER.warn(\"close err:\" + e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\t\t// not called\n\t}\n\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\t\t// not called\n\n\t}\n\n\t@Override\n\tpublic void errorResponse(byte[] data, BackendConnection conn) {\n\t\tremoveFinished(conn);\n\t\tErrorPacket err = new ErrorPacket();\n\t\terr.read(data);\n\t\tLOGGER.warn(\"errorResponse \" + err.errno + \" \"\n\t\t\t\t+ new String(err.message));\n\t\tconn.release();\n\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] ok, BackendConnection conn) {\n\t\tboolean executeResponse = conn.syncAndExcute();\n\t\tif (executeResponse) {\n\t\t\tremoveFinished(conn);\n\t\t\tconn.release();\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t\tremoveFinished(conn);\n\t\tconn.release();\n\t}\n\n\tprivate void executeException(BackendConnection c, Throwable e) {\n\t\tremoveFinished(c);\n\t\tLOGGER.warn(\"executeException   \", e);\n\t\tc.close(\"heatbeat exception:\" + e);\n\n\t}\n\n\tprivate void removeFinished(BackendConnection con) {\n\t\tLong id = ((BackendConnection) con).getId();\n\t\tthis.allCons.remove(id);\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\t\tremoveFinished(conn);\n\t\tLOGGER.warn(\"connection closed \" + conn + \" reason:\" + reason);\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"received field eof  from \" + conn);\n\t\t}\n\t}\n\n}\n\nclass HeartBeatCon {\n\tpublic final long timeOutTimestamp;\n\tpublic final BackendConnection conn;\n\n\tpublic HeartBeatCon(BackendConnection conn) {\n\t\tsuper();\n\t\tthis.timeOutTimestamp = System.currentTimeMillis() + 20 * 1000L;\n\t\tthis.conn = conn;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/DelegateResponseHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\n\nimport io.mycat.backend.BackendConnection;\n\n/**\n * @author mycat\n */\npublic class DelegateResponseHandler implements ResponseHandler {\n    private final ResponseHandler target;\n\n    public DelegateResponseHandler(ResponseHandler target) {\n        if (target == null) {\n            throw new IllegalArgumentException(\"delegate is null!\");\n        }\n        this.target = target;\n    }\n\n    @Override\n    public void connectionAcquired(BackendConnection conn) {\n        target.connectionAcquired(conn);\n    }\n\n    @Override\n    public void connectionError(Throwable e, BackendConnection conn) {\n        target.connectionError(e, conn);\n    }\n\n    @Override\n    public void okResponse(byte[] ok, BackendConnection conn) {\n        target.okResponse(ok, conn);\n    }\n\n    @Override\n    public void errorResponse(byte[] err, BackendConnection conn) {\n        target.errorResponse(err, conn);\n    }\n\n    @Override\n    public void fieldEofResponse(byte[] header, List<byte[]> fields, byte[] eof, BackendConnection conn) {\n        target.fieldEofResponse(header, fields, eof, conn);\n    }\n\n    @Override\n    public void rowResponse(byte[] row, BackendConnection conn) {\n        target.rowResponse(row, conn);\n    }\n\n    @Override\n    public void rowEofResponse(byte[] eof, BackendConnection conn) {\n        target.rowEofResponse(eof, conn);\n    }\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\t\ttarget.writeQueueAvailable();\n\t\t\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\t\ttarget.connectionClose(conn, reason);\n\t}\n\n\t\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/FetchStoreNodeOfChildTableHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.cache.CachePool;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\n\n/**\n * company where id=(select company_id from customer where id=3); the one which\n * return data (id) is the datanode to store child table's records\n * \n * @author wuzhih\n * \n */\npublic class FetchStoreNodeOfChildTableHandler implements ResponseHandler {\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(FetchStoreNodeOfChildTableHandler.class);\n\tprivate String sql;\n\tprivate volatile String result;\n\tprivate volatile String dataNode;\n\tprivate AtomicInteger finished = new AtomicInteger(0);\n\tprotected final ReentrantLock lock = new ReentrantLock();\n\tprivate volatile ServerConnection sc; \n\tpublic String execute(String schema, String sql, List<String> dataNodes, ServerConnection sc) {\n\t\t\n\t\tString key = schema + \":\" + sql;\n\t\tCachePool cache = MycatServer.getInstance().getCacheService()\n\t\t\t\t.getCachePool(\"ER_SQL2PARENTID\");\n\t\tString result = (String) cache.get(key);\n\t\tif (result != null) {\n\t\t\treturn result;\n\t\t}\n\t\tthis.sql = sql;\n\t\tint totalCount = dataNodes.size();\n\t\tlong startTime = System.currentTimeMillis();\n\t\tlong endTime = startTime + 5 * 60 * 1000L;\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\tthis.sc = sc;\n\t\tLOGGER.debug(\"find child node with sql:\" + sql);\n\t\tfor (String dn : dataNodes) {\n\t\t\tif (dataNode != null) {\n\t\t\t\treturn dataNode;\n\t\t\t}\n\t\t\tPhysicalDBNode mysqlDN = conf.getDataNodes().get(dn);\n\t\t\ttry {\n\t\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\t\tLOGGER.debug(sc + \"execute in datanode \" + dn);\n\t\t\t\t}\n\t\t\t\tRouteResultsetNode node = new RouteResultsetNode(dn, ServerParse.SELECT, sql);\n\t\t\t\tnode.setRunOnSlave(false);\t// 获取 子表节点，最好走master为好\n\n\t\t\t\t/*\n\t\t\t\t * fix #1370 默认应该先从已经持有的连接中取连接, 否则可能因为事务隔离性看不到当前事务内更新的数据\n\t\t\t\t * Tips: 通过mysqlDN.getConnection获取到的连接不是当前连接\n\t\t\t\t *\n\t\t\t\t */\n\t\t\t\tBackendConnection conn = sc.getSession2().getTarget(node);\n\t\t\t\tif(sc.getSession2().tryExistsCon(conn, node)) {\n\t\t\t\t\t_execute(conn, node, sc);\n\t\t\t\t} else {\n\t\t\t\t\tmysqlDN.getConnection(mysqlDN.getDatabase(), sc.isAutocommit(), node, this, node);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.warn(\"get connection err \" + e);\n\t\t\t}\n\t\t}\n\n\t\twhile (dataNode == null && System.currentTimeMillis() < endTime) {\n\t\t\ttry {\n\t\t\t\tThread.sleep(50);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (dataNode != null || finished.get() >= totalCount) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (dataNode != null) {\n\t\t\tcache.putIfAbsent(key, dataNode);\n\t\t}\n\t\tif(System.currentTimeMillis() > endTime) {\n\t\t\tLOGGER.error(\"timeout when executing fetch sql  \" + sql);\n\n\t\t}\n\t\treturn dataNode;\n\t\t\n\t}\n\n\tpublic String execute(String schema, String sql, ArrayList<String> dataNodes) {\n\t\tString key = schema + \":\" + sql;\n\t\tCachePool cache = MycatServer.getInstance().getCacheService()\n\t\t\t\t.getCachePool(\"ER_SQL2PARENTID\");\n\t\tString result = (String) cache.get(key);\n\t\tif (result != null) {\n\t\t\treturn result;\n\t\t}\n\t\tthis.sql = sql;\n\t\tint totalCount = dataNodes.size();\n\t\tlong startTime = System.currentTimeMillis();\n\t\tlong endTime = startTime + 5 * 60 * 1000L;\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\n\t\tLOGGER.debug(\"find child node with sql:\" + sql);\n\t\tfor (String dn : dataNodes) {\n\t\t\tif (dataNode != null) {\n\t\t\t\treturn dataNode;\n\t\t\t}\n\t\t\tPhysicalDBNode mysqlDN = conf.getDataNodes().get(dn);\n\t\t\ttry {\n\t\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\t\tLOGGER.debug(\"execute in datanode \" + dn);\n\t\t\t\t}\n\t\t\t\tRouteResultsetNode node = new RouteResultsetNode(dn, ServerParse.SELECT, sql);\n\t\t\t\tnode.setRunOnSlave(false);\t// 获取 子表节点，最好走master为好\n\n\t\t\t\tmysqlDN.getConnection(mysqlDN.getDatabase(), true, node, this, node);\n\t\t\t\t \n//\t\t\t\tmysqlDN.getConnection(mysqlDN.getDatabase(), true,\n//\t\t\t\t\t\tnew RouteResultsetNode(dn, ServerParse.SELECT, sql),\n//\t\t\t\t\t\tthis, dn);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.warn(\"get connection err \" + e);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tThread.sleep(200);\n\t\t\t} catch (InterruptedException e) {\n\n\t\t\t}\n\t\t}\n\n\t\twhile (dataNode == null && System.currentTimeMillis() < endTime) {\n\t\t\ttry {\n\t\t\t\tThread.sleep(50);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (dataNode != null || finished.get() >= totalCount) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (dataNode != null) {\n\t\t\tcache.putIfAbsent(key, dataNode);\n\t\t}\n\t\treturn dataNode;\n\n\t}\n\t\n\tprivate void _execute(BackendConnection conn, RouteResultsetNode node, ServerConnection sc) {\n\t\tconn.setResponseHandler(this);\n\t\ttry {\n\t\t\tconn.execute(node, sc, sc.isAutocommit());\n\t\t} catch (IOException e) {\n\t\t\tconnectionError(e, conn);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\t\tconn.setResponseHandler(this);\n\t\ttry {\n\t\t\tconn.query(sql);\n\t\t} catch (Exception e) {\n\t\t\texecuteException(conn, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\t\tfinished.incrementAndGet();\n\t\tLOGGER.warn(\"connectionError \" + e);\n\n\t}\n\n\t@Override\n\tpublic void errorResponse(byte[] data, BackendConnection conn) {\n\t\tfinished.incrementAndGet();\n\t\tErrorPacket err = new ErrorPacket();\n\t\terr.read(data);\n\t\tLOGGER.warn(\"errorResponse \" + err.errno + \" \"\n\t\t\t\t+ new String(err.message));\n\t\treleaseConnection(conn);\n\t\tLOGGER.warn(this.sc + \" connection release \" + conn + \" errorResponse\" );\n\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] ok, BackendConnection conn) {\n\t\tboolean executeResponse = conn.syncAndExcute();\n\t\tif (executeResponse) {\n\t\t\tfinished.incrementAndGet();\n\t\t\t//conn.release();\n\t\t\treleaseConnection(conn);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(this.sc + \"received rowResponse response,\" + getColumn(row)\n\t\t\t+ \" from  \" + conn);\n\t\t}\n\t\tif (result == null) {\n\t\t\tresult = getColumn(row);\n\t\t\tdataNode = ((RouteResultsetNode) conn.getAttachment()).getName();\n\t\t} else {\n\t\t\tLOGGER.warn(\"find multi data nodes for child table store, sql is:  \"\n\t\t\t\t\t+ sql);\n\t\t}\n\n\t}\n\n\tprivate String getColumn(byte[] row) {\n\t\tRowDataPacket rowDataPkg = new RowDataPacket(1);\n\t\trowDataPkg.read(row);\n\t\tbyte[] columnData = rowDataPkg.fieldValues.get(0);\n\t\treturn new String(columnData);\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t\tfinished.incrementAndGet();\n\t\t//conn.release();\n\t\treleaseConnection(conn);\n\t}\n\n\tprivate void executeException(BackendConnection c, Throwable e) {\n\t\tfinished.incrementAndGet();\n\t\tLOGGER.warn(\"executeException   \" + e);\n\t\tc.close(\"exception:\" + e);\n\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\t\tfinished.incrementAndGet();\n\t\tLOGGER.warn(\"connection closed \" + conn + \" reason:\" + reason);\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\n\t}\n\tprivate void releaseConnection(BackendConnection conn) {\n\t\tif(this.sc  != null ) {\n\t\t\tMap<RouteResultsetNode, BackendConnection> target = sc.getSession2().getTargetMap();\n\t\t\tfor(BackendConnection backConn :target.values()) {\n\t\t\t\tif(backConn != null && backConn.equals(conn)) {\n\t\t\t\t\treturn ;\n\t\t\t\t}\n\t\t\t}\n//\t\t\tif(sc.getSession2().tryExistsCon(conn, node)) {\n//\t\t\t\t_execute(conn, node, sc);\n//\t\t\t} else {\n//\t\t\t\tmysqlDN.getConnection(mysqlDN.getDatabase(), sc.isAutocommit(), node, this, node);\n//\t\t\t}\n\t\t}\n\t\tconn.release();\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/GetConnectionHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.BackendConnection;\n\n/**\n * wuzh\n * \n * @author mycat\n * \n */\npublic class GetConnectionHandler implements ResponseHandler {\n\tprivate final CopyOnWriteArrayList<BackendConnection> successCons;\n\tprivate static final Logger logger = LoggerFactory\n\t\t\t.getLogger(GetConnectionHandler.class);\n\tprivate final AtomicInteger finishedCount = new AtomicInteger(0);\n\tprivate final int total;\n\n\tpublic GetConnectionHandler(\n\t\t\tCopyOnWriteArrayList<BackendConnection> connsToStore,\n\t\t\tint totalNumber) {\n\t\tsuper();\n\t\tthis.successCons = connsToStore;\n\t\tthis.total = totalNumber;\n\t}\n\n\tpublic String getStatusInfo()\n\t{\n\t\treturn \"finished \"+ finishedCount.get()+\" success \"+successCons.size()+\" target count:\"+this.total;\n\t}\n\tpublic boolean finished() {\n\t\treturn finishedCount.get() >= total;\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\t\tsuccessCons.add(conn);\n\t\tfinishedCount.addAndGet(1);\n\t\tlogger.info(\"connected successfuly \" + conn);\n        conn.release();\n\t}\n\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\t\tfinishedCount.addAndGet(1);\n\t\tlogger.warn(\"connect error \" + conn+ e);\n        conn.release();\n\t}\n\n\t@Override\n\tpublic void errorResponse(byte[] err, BackendConnection conn) {\n\t\tlogger.warn(\"caught error resp: \" + conn + \" \" + new String(err));\n        conn.release();\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] ok, BackendConnection conn) {\n\t\tlogger.info(\"received ok resp: \" + conn + \" \" + new String(ok));\n\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/JDBCFetchStoreNodeOfChildTableHandler.java",
    "content": "package io.mycat.backend.mysql.nio.handler;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.jdbc.JDBCDatasource;\nimport io.mycat.cache.CachePool;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Map;\n\npublic class JDBCFetchStoreNodeOfChildTableHandler {\n\n    private static final Logger LOGGER = LoggerFactory\n            .getLogger(JDBCFetchStoreNodeOfChildTableHandler.class);\n\n    public String execute(String schema, String sql, ArrayList<String> dataNodes) {\n\n        String key = schema + \":\" + sql;\n        CachePool cache = MycatServer.getInstance().getCacheService()\n                .getCachePool(\"ER_SQL2PARENTID\");\n        String result = (String) cache.get(key);\n        if (result != null) {\n            return result;\n        }\n        Map<String, PhysicalDBNode> dbNodeMap = MycatServer.getInstance().getConfig().getDataNodes();\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"find child node with sql:\" + sql);\n        }\n        for (String dn : dataNodes) {\n\n            PhysicalDBNode physicalDBNode = dbNodeMap.get(dn);\n            PhysicalDatasource physicalDatasource = physicalDBNode.getDbPool().getSource();\n            if(physicalDatasource instanceof JDBCDatasource) {\n                JDBCDatasource jdbcDatasource = (JDBCDatasource) physicalDatasource;\n                Connection con = null;\n                PreparedStatement pstmt = null;\n                ResultSet rs = null;\n                try {\n                    con = jdbcDatasource.getDruidConnection();\n                    String useDB = \"use `\" + physicalDBNode.getDatabase() + \"`;\";\n                    pstmt = con.prepareStatement(useDB);\n                    pstmt.execute();\n                    pstmt = con.prepareStatement(sql);\n                    rs = pstmt.executeQuery();\n                    if (rs.next()) {\n                        return dn;\n                    }\n                } catch (SQLException e) {\n                    e.printStackTrace();\n                } finally {\n                    try {\n                        if (con != null) {\n                            con.close();\n                        }\n                        if (pstmt != null) {\n                            pstmt.close();\n                        }\n                        if (rs != null) {\n                            rs.close();\n                        }\n                    } catch (SQLException e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        }\n        LOGGER.error(\"can't find (root) parent sharding node for sql:\"+ sql);\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/KillConnectionHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.List;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.mysql.nio.MySQLConnection;\nimport io.mycat.net.mysql.CommandPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.MySQLPacket;\nimport io.mycat.server.NonBlockingSession;\n\n/**\n * @author mycat\n */\npublic class KillConnectionHandler implements ResponseHandler {\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(KillConnectionHandler.class);\n\n\tprivate final MySQLConnection killee;\n\tprivate final NonBlockingSession session;\n\n\tpublic KillConnectionHandler(BackendConnection killee,\n\t\t\tNonBlockingSession session) {\n\t\tthis.killee = (MySQLConnection) killee;\n\t\tthis.session = session;\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\t\tMySQLConnection mysqlCon = (MySQLConnection) conn;\n\t\tconn.setResponseHandler(this);\n\t\tCommandPacket packet = new CommandPacket();\n\t\tpacket.packetId = 0;\n\t\tpacket.command = MySQLPacket.COM_QUERY;\n\t\tpacket.arg = new StringBuilder(\"KILL \").append(killee.getThreadId())\n\t\t\t\t.toString().getBytes();\n\t\tpacket.write(mysqlCon);\n\t}\n\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\t\tkillee.close(\"exception:\" + e.toString());\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] ok, BackendConnection conn) {\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"kill connection success connection id:\"\n\t\t\t\t\t+ killee.getThreadId());\n\t\t}\n\t\tconn.release();\n\t\tkillee.close(\"killed\");\n\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t\tLOGGER.warn(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": field's eof\").toString());\n\t\tconn.quit();\n\t\tkillee.close(\"killed\");\n\t}\n\n\t@Override\n\tpublic void errorResponse(byte[] data, BackendConnection conn) {\n\t\tErrorPacket err = new ErrorPacket();\n\t\terr.read(data);\n\t\tString msg = null;\n\t\ttry {\n\t\t\tmsg = new String(err.message, conn.getCharset());\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\tmsg = new String(err.message);\n\t\t}\n\t\tLOGGER.warn(\"kill backend connection \" + killee + \" failed: \" + msg\n\t\t\t\t+ \" con:\" + conn);\n\t\tconn.release();\n\t\tkillee.close(\"exception:\" + msg);\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/LoadDataResponseHandler.java",
    "content": "package io.mycat.backend.mysql.nio.handler;\n\nimport io.mycat.backend.BackendConnection;\n\n/**\n * Created by nange on 2015/3/31.\n */\npublic interface LoadDataResponseHandler\n{\n    /**\n     * 收到请求发送文件数据包的响应处理\n     */\n    void requestDataResponse(byte[] row, BackendConnection conn);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/LockTablesHandler.java",
    "content": "package io.mycat.backend.mysql.nio.handler;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.NonBlockingSession;\n\n/**\n * lock tables 语句处理器\n * @author songdabin\n * \n */\npublic class LockTablesHandler extends MultiNodeHandler {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(LockTablesHandler.class);\n\n\tprivate final RouteResultset rrs;\n\tprivate final ReentrantLock lock;\n\tprivate final boolean autocommit;\n\t\n\tpublic LockTablesHandler(NonBlockingSession session, RouteResultset rrs) {\n\t\tsuper(session);\n\t\tthis.rrs = rrs;\n\t\tthis.autocommit = session.getSource().isAutocommit();\n\t\tthis.lock = new ReentrantLock();\n\t}\n\t\n\tpublic void execute() throws Exception {\n\t\tsuper.reset(this.rrs.getNodes().length);\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\tfor (final RouteResultsetNode node : rrs.getNodes()) {\n\t\t\tBackendConnection conn = session.getTarget(node);\n\t\t\tif (session.tryExistsCon(conn, node)) {\n\t\t\t\t_execute(conn, node);\n\t\t\t} else {\n\t\t\t\t// create new connection\n\t\t\t\tPhysicalDBNode dn = conf.getDataNodes().get(node.getName());\n\t\t\t\tdn.getConnection(dn.getDatabase(), autocommit, node, this, node);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate void _execute(BackendConnection conn, RouteResultsetNode node) {\n\t\tif (clearIfSessionClosed(session)) {\n\t\t\treturn;\n\t\t}\n\t\tconn.setResponseHandler(this);\n\t\ttry {\n\t\t\tconn.execute(node, session.getSource(), autocommit);\n\t\t} catch (IOException e) {\n\t\t\tconnectionError(e, conn);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\t\tfinal RouteResultsetNode node = (RouteResultsetNode) conn.getAttachment();\n\t\tsession.bindConnection(node, conn);\n\t\t_execute(conn, node);\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] data, BackendConnection conn) {\n\t\tboolean executeResponse = conn.syncAndExcute();\n\t\tif (executeResponse) {\n\t\t\tif (clearIfSessionClosed(session)) {\n                return;\n            }\n\t\t\tboolean isEndPack = decrementCountBy(1);\n\t\t\tif (isEndPack) {\n\t\t\t\tif (this.isFail() || session.closed()) {\n\t\t\t\t\ttryErrorFinished(true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tOkPacket ok = new OkPacket();\n\t\t\t\tok.read(data);\n\t\t\t\tlock.lock();\n\t\t\t\ttry {\n\t\t\t\t\tok.packetId = ++ packetId;\n\t\t\t\t\tok.serverStatus = session.getSource().isAutocommit() ? 2:1;\n\t\t\t\t} finally {\n\t\t\t\t\tlock.unlock();\n\t\t\t\t}\n\t\t\t\tok.write(session.getSource());\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprotected String byte2Str(byte[] data) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (byte b : data) {\n\t\t\tsb.append(Byte.toString(b));\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields, byte[] eof, BackendConnection conn) {\n\t\tLOGGER.error(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": field's eof\").toString());\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t\tLOGGER.warn(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": row data packet\").toString());\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t\tLOGGER.error(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": row's eof\").toString());\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/MiddlerQueryResultHandler.java",
    "content": "package io.mycat.backend.mysql.nio.handler;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.druid.sql.ast.expr.SQLCharExpr;\n\nimport io.mycat.backend.mysql.DataType;\n\n/**\n * 查询中间结果处理器\n * @author huangyiming\n *\n * @param <T>\n */\npublic class MiddlerQueryResultHandler<T> implements MiddlerResultHandler<T> {\n\n\tList<SQLCharExpr> reusult = new ArrayList<>();\n\tDataType dataType;\n\tClass<T> clazz;\n\tprivate SecondHandler secondHandler;\n\t\n\tpublic MiddlerQueryResultHandler(SecondHandler secondHandler) {\n \t\tthis.secondHandler = secondHandler;\n \t\t\n \t  \n \t}\n\t//确保只有一个构造函数入口\n\tprivate MiddlerQueryResultHandler(){\n\t\t\n\t}\n\t\n\t@Override\n\tpublic List<SQLCharExpr> getResult() {\n \t\treturn reusult;\n\t}\n\t@Override\n\tpublic void add(T t ) {\n \t\treusult.add(new SQLCharExpr(t==null?null:t.toString()));\n \t}\t \n\t \n\t@Override\n\tpublic String getDataType() {\n \t\treturn dataType.name();\n\t}\n\t\n\t@Override\n\tpublic void secondEexcute() {\n\t\tsecondHandler.doExecute(getResult());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/MiddlerResultHandler.java",
    "content": "package io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\n\nimport com.alibaba.druid.sql.ast.expr.SQLCharExpr;\n\n/**\n * 中间结果处理器\n * @author huangyiming\n *\n * @param <T>\n */\npublic interface MiddlerResultHandler<T> {\n\n \t\n\tpublic List<SQLCharExpr> getResult();\n\t\n\tpublic void  add(T t );\n\t\n\tpublic String getDataType();\n\t\n\tpublic void secondEexcute();\n\t\n\t\n\t  \n\t\n\t\n\t\n }\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/MultiNodeCoordinator.java",
    "content": "package io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.mysql.nio.MySQLConnection;\nimport io.mycat.backend.mysql.xa.CoordinatorLogEntry;\nimport io.mycat.backend.mysql.xa.ParticipantLogEntry;\nimport io.mycat.backend.mysql.xa.TxState;\nimport io.mycat.backend.mysql.xa.recovery.Repository;\nimport io.mycat.backend.mysql.xa.recovery.impl.FileSystemRepository;\nimport io.mycat.backend.mysql.xa.recovery.impl.InMemoryRepository;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.NonBlockingSession;\nimport io.mycat.server.sqlcmd.SQLCtrlCommand;\n\npublic class MultiNodeCoordinator implements ResponseHandler {\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(MultiNodeCoordinator.class);\n\tpublic static final Repository fileRepository = new FileSystemRepository();\n\tpublic static final Repository inMemoryRepository = new InMemoryRepository();\n\tprivate final AtomicInteger runningCount = new AtomicInteger(0);\n\tprivate final AtomicInteger prepareCount = new AtomicInteger(0);\n\tprotected volatile int coorXaStatus;\n\n\tprivate final AtomicInteger faileCount = new AtomicInteger(0);\n\tprivate volatile int nodeCount;\n\tprivate final NonBlockingSession session;\n\tprivate SQLCtrlCommand cmdHandler;\n\tprivate final AtomicBoolean failed = new AtomicBoolean(false);\n\tprotected volatile String error;\n    \n\tpublic MultiNodeCoordinator(NonBlockingSession session) {\n\t\tthis.session = session;\n\t}\n\n\t/** Multi-nodes 1pc Commit Handle **/\n\tpublic void executeBatchNodeCmd(SQLCtrlCommand cmdHandler) {\n\t\tthis.cmdHandler = cmdHandler;\n\t\tfinal int initCount = session.getTargetCount();\n\t\trunningCount.set(initCount);\n\t\tnodeCount = initCount;\n\t\tfailed.set(false);\n\t\tfaileCount.set(0);\n\t\t//XA statue init\n\t\tif(prepareCount.get() != 0){\n\t\t\t//System.out.println(\"prepareCount :\" + prepareCount.get());\n\t\t}\n\t\tprepareCount.set(0);\n\t\tif(session.getXaTXID()!=null){\n\t\t\tcoorXaStatus = TxState.TX_STARTED_STATE;\n\t\t\twriteRecoverLog(initCount); \n\t\t} else {\n\t\t\tcoorXaStatus = -1;\n\t\t}\n\t\t\n\t\t// 执行\n\t\tint started = 0;\n\t\tfor (RouteResultsetNode rrn : session.getTargetKeys()) {\n\t\t\tif (rrn == null) {\n\t\t\t\tLOGGER.error(\"null is contained in RoutResultsetNodes, source = \"\n\t\t\t\t\t\t+ session.getSource());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfinal BackendConnection conn = session.getTarget(rrn);\n\t\t\tif (conn != null) {\n\t\t\t\tconn.setResponseHandler(this);\n\t\t\t\t//process the XA_END XA_PREPARE Command\n\t\t\t\tif(conn instanceof MySQLConnection){\n\t\t\t\t\tMySQLConnection mysqlCon = (MySQLConnection) conn;\n\t\t\t\t\tString xaTxId = null;\n\t\t\t\t\tif(session.getXaTXID()!=null){\n\t\t\t\t\t\txaTxId = session.getXaTXID() +\",'\"+ mysqlCon.getSchema()+\"'\";\n\t\t\t\t\t}\n\t\t\t\t\tif (mysqlCon.getXaStatus() == TxState.TX_STARTED_STATE)\n\t\t\t\t\t{\n\t\t\t\t\t\t//recovery Log\n//\t\t\t\t\t\tparticipantLogEntry[started] = new ParticipantLogEntry(xaTxId,conn.getHost(),0,conn.getSchema(),((MySQLConnection) conn).getXaStatus());\n\t\t\t\t\t\tString[] cmds = new String[]{\"XA END \" + xaTxId,\n\t\t\t\t\t\t\t\t\"XA PREPARE \" + xaTxId};\n\t\t\t\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\t\t\t\tLOGGER.debug(\"Start execute the batch cmd : \"+ cmds[0] + \";\" + cmds[1]+\",\"+\n\t\t\t\t\t\t\t\t\t\"current connection:\"+conn.getHost()+\":\"+conn.getPort());\n\t\t\t\t\t\t}\n//\t\t\t\t\t\tprepareCount.incrementAndGet();\n\t\t\t\t\t\tmysqlCon.execBatchCmd(cmds);\n\t\t\t\t\t} else\n\t\t\t\t\t{\n\t\t\t\t\t\t//recovery Log\n//\t\t\t\t\t\tparticipantLogEntry[started] = new ParticipantLogEntry(xaTxId,conn.getHost(),0,conn.getSchema(),((MySQLConnection) conn).getXaStatus());\n\t\t\t\t\t\tcmdHandler.sendCommand(session, conn);\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\tcmdHandler.sendCommand(session, conn);\n\t\t\t\t}\n\t\t\t\t++started;\n\t\t\t}\n\t\t}\n\n\t\t\n\t\tif (started < nodeCount) {\n\t\t\trunningCount.set(started);\n\t\t\tLOGGER.warn(\"some connection failed to execute \"\n\t\t\t\t\t+ (nodeCount - started));\n\t\t\t/**\n\t\t\t * assumption: only caused by front-end connection close. <br/>\n\t\t\t * Otherwise, packet must be returned to front-end\n\t\t\t */\n\t\t\tfailed.set(true);\n\t\t}\n\t}\n\n\tprivate void writeRecoverLog(int initCount) {\n\t\t// 执行\n\t\tint started = 0;\n\t\t//recovery nodes log\n\t\tParticipantLogEntry[] participantLogEntry = new ParticipantLogEntry[initCount];\n\t\tfor (RouteResultsetNode rrn : session.getTargetKeys()) {\n\t\t\tif (rrn == null) {\n\t\t\t\tLOGGER.error(\"null is contained in RoutResultsetNodes, source = \"\n\t\t\t\t\t\t+ session.getSource());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfinal BackendConnection conn = session.getTarget(rrn);\n\t\t\tif (conn != null) {\n\t\t\t\t//process the XA_END XA_PREPARE Command\n\t\t\t\tif(conn instanceof MySQLConnection){\n\t\t\t\t\tMySQLConnection mysqlCon = (MySQLConnection) conn;\n\t\t\t\t\tString xaTxId = null;\n\t\t\t\t\tif(session.getXaTXID()!=null){\n\t\t\t\t\t\txaTxId = session.getXaTXID() +\",'\"+ mysqlCon.getSchema()+\"'\";\n\t\t\t\t\t}\n\t\t\t\t\tif (mysqlCon.getXaStatus() == TxState.TX_STARTED_STATE)\n\t\t\t\t\t{\n\t\t\t\t\t\t//recovery Log\n\t\t\t\t\t\tparticipantLogEntry[started] = new ParticipantLogEntry(xaTxId,conn.getHost(),0,conn.getSchema(),((MySQLConnection) conn).getXaStatus());\n\t\t\t\t\t\tprepareCount.incrementAndGet();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tstarted++;\n\t\t}\n\t\t//xa recovery log\n//\t\tif(session.getXaTXID()!=null) {\n\t\t\tCoordinatorLogEntry coordinatorLogEntry = new CoordinatorLogEntry(session.getXaTXID(), false, participantLogEntry);\n\t\t\tinMemoryRepository.put(session.getXaTXID(), coordinatorLogEntry);\n\t\t\tfileRepository.writeCheckpoint(session.getXaTXID(), inMemoryRepository.getAllCoordinatorLogEntries());\n//\t\t}\t\t\n\t}\n\n\tprivate boolean finished() {\n\t\tint val = runningCount.decrementAndGet();\n\t\treturn (val == 0);\n\t}\n\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\n\t}\n\n\t@Override\n\tpublic void errorResponse(byte[] err, BackendConnection conn) {\n//\t\tfaileCount.incrementAndGet();\n\t\tErrorPacket errorPacket = new ErrorPacket();\n\t\terrorPacket.read(err);\n\t\tString msg = new String(errorPacket.message);\n\t\tif (LOGGER.isInfoEnabled()){\n\n\t\t\tLOGGER.info(\"======================\" + \"errorResponse from {} msg: {}\", conn, new String(errorPacket.message));\n\t\t}\n\t\t// to do xa prepare failure rollback \n\t\t\n\t\tif(conn instanceof MySQLConnection) {\n\t\t\tMySQLConnection mysqlCon = (MySQLConnection) conn;\n\t\t\tif(coorXaStatus == TxState.TX_STARTED_STATE) {\n\t\t\t\tthis.setFail(msg);\n\t\t\t\t// 1 pc prepare failure to rollback \n\t\t\t\tLOGGER.error(\"XA prepare failure in :xa end;xa prepare; XA id is:\" +session.getXaTXID() + \" errmsg {},current conn:{}\", msg ,conn);\n\t\t\t\tif(mysqlCon.batchCmdFinished()) {\n\t\t\t\t\tif(prepareCount.decrementAndGet() == 0) {\n\t\t\t\t\t\tthis.tryErrorFinished(true);\n\t\t\t\t\t}\n\t\t\t\t\treturn ;\n\t\t\t\t}\n\t\t\t}\t\n\t\t\t//replayCommit prepare statue replay commit\n\t\t\tif(coorXaStatus == TxState.TX_PREPARED_STATE) {\n\t\t\t//\tif(mysqlCon.getXaStatus() == TxState.TX_PREPARED_STATE) {\n\t\t\t\tString xaTxId = session.getXaTXID();\n\t\t\t\tif (xaTxId != null) {\n\t\t\t\t\txaTxId += \",'\" + mysqlCon.getSchema() + \"'\";\n\t\t\t\t\tString cmd = \"XA COMMIT \" + xaTxId;\n\t\t\t\t\tif (LOGGER.isInfoEnabled()) {\n\t\t\t\t\t\tLOGGER.info(\"Replay Commit execute the cmd :\" + cmd + \",current host:\" + mysqlCon.getHost()\n\t\t\t\t\t\t\t\t+ \":\" + mysqlCon.getPort());\n\t\t\t\t\t}\n\t\t\t\t\tmysqlCon.execCmd(cmd);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\t\t\t\n\t\t}\t\t\n\t\t\n\t\t//if some commit and some failure , print error info and return failure info  and then release back connection \n\t\t\n\t\t//release connection\n\t\tif (this.cmdHandler.releaseConOnErr()) {\n\t\t\tsession.releaseConnection(conn);\n\t\t} else {\n\t\t\tsession.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(),false);\n\t\t}\n\t\t\n\t\t//\n\t\tthis.setFail(msg);\n\t\tif (this.finished()) {\n\t\t\tthis.tryErrorFinished(true);\n\t\t}\n\n\t}\n\n\t\n\t@Override\n\tpublic void okResponse(byte[] ok, BackendConnection conn) {\n\t\t//LOGGER.info(\"======================\" + \"okResponse from {} \", conn);\n\n\t\t//process the XA Transatcion 2pc commit\n\t\tif(conn instanceof MySQLConnection)\n\t\t{   \n\t\t\tMySQLConnection mysqlCon = (MySQLConnection) conn;\n\t\t\tswitch (mysqlCon.getXaStatus())\n\t\t\t{\n\t\t\t\tcase TxState.TX_STARTED_STATE:\n\t\t\t\t\t//if there have many SQL execute wait the okResponse,will come to here one by one\n\t\t\t\t\t//should be wait all nodes ready ,then send xa commit to all nodes.\n\t\t\t\t\tif (mysqlCon.batchCmdFinished())\n\t\t\t\t\t{\n\t\t\t\t\t\tString xaTxId = session.getXaTXID();\n\n\t\t\t\t\t\t//recovery log\n\t\t\t\t\t\tCoordinatorLogEntry coordinatorLogEntry = inMemoryRepository.get(xaTxId);\n\t\t\t\t\t\tfor(int i=0; i<coordinatorLogEntry.participants.length;i++){\n\t\t\t\t\t\t\t//LOGGER.debug(\"[In Memory CoordinatorLogEntry]\"+coordinatorLogEntry.participants[i]);\n\t\t\t\t\t\t\tif(coordinatorLogEntry.participants[i].resourceName.equals(conn.getSchema())){\n\t\t\t\t\t\t\t\tcoordinatorLogEntry.participants[i].txState = TxState.TX_PREPARED_STATE;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tinMemoryRepository.put(xaTxId,coordinatorLogEntry);\n//\t\t\t\t\t\tfileRepository.writeCheckpoint(xaTxId, inMemoryRepository.getAllCoordinatorLogEntries());\n\t\t\t\t\t\tmysqlCon.setXaStatus(TxState.TX_PREPARED_STATE);\n\n\t\t\t\t\t\t\n\t\t\t\t\t\t//wait all nodes prepare and send all nodes prepare  \n\t\t\t\t\t\tif(prepareCount.decrementAndGet() == 0) {\n\t\t\t\t\t\t\tfileRepository.writeCheckpoint(xaTxId, inMemoryRepository.getAllCoordinatorLogEntries());\n\t\t\t\t\t\t\t//判断是否有错误\n\t\t\t\t\t\t\tif(faileCount.get() > 0) {\n\t\t\t\t\t\t\t\tthis.tryErrorFinished(true);\n\t\t\t\t\t\t\t\treturn ;\n\t\t\t\t\t\t\t}\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tcoorXaStatus = TxState.TX_PREPARED_STATE; //进入prepare状态 \n\t\t\t\t\t\t\tfor (RouteResultsetNode rrn : session.getTargetKeys()) {\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif (rrn == null) {\n\t\t\t\t\t\t\t\t\tLOGGER.error(\"null is contained in RoutResultsetNodes, source = \"\n\t\t\t\t\t\t\t\t\t\t\t+ session.getSource());\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfinal BackendConnection backConn = session.getTarget(rrn);\n\t\t\t\t\t\t\t\tif (conn != null) {\n//\t\t\t\t\t\t\t\t\tconn.setResponseHandler(this);\n\t\t\t\t\t\t\t\t\t//process the XA_END XA_PREPARE Command\n\t\t\t\t\t\t\t\t\tif(conn instanceof MySQLConnection){\n\t\t\t\t\t\t\t\t\t\tMySQLConnection backMysqlCon = (MySQLConnection) backConn;\n\t\t\t\t\t\t\t\t\t\tif(session.getXaTXID()!=null){\n\t\t\t\t\t\t\t\t\t\t\txaTxId = session.getXaTXID() +\",'\"+ backMysqlCon.getSchema()+\"'\";\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tif (backMysqlCon.getXaStatus() == TxState.TX_PREPARED_STATE)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tString cmd = \"XA COMMIT \" + xaTxId ;\n\t\t\t\t\t\t\t\t\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\t\t\t\t\t\t\t\t\tLOGGER.debug(\"Start execute the  cmd : \"+ cmd+\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\" current connection:\"+conn.getHost()+\":\"+conn.getPort());\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tbackMysqlCon.execCmd(cmd);\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tLOGGER.info(\"{} not in PREPARED_STATE\", backMysqlCon);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\tcase TxState.TX_PREPARED_STATE:\n\t\t\t\t{\n\t\t\t\t\t//recovery log\n\t\t\t\t\tString xaTxId = session.getXaTXID();\n\t\t\t\t\tCoordinatorLogEntry coordinatorLogEntry = inMemoryRepository.get(xaTxId);\n\t\t\t\t\tfor(int i=0; i<coordinatorLogEntry.participants.length;i++){\n\t\t\t\t\t\tif(coordinatorLogEntry.participants[i].resourceName.equals(conn.getSchema())){\n\t\t\t\t\t\t\tcoordinatorLogEntry.participants[i].txState = TxState.TX_COMMITED_STATE;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tinMemoryRepository.put(xaTxId,coordinatorLogEntry);\n\t\t\t\t\tfileRepository.writeCheckpoint(xaTxId, inMemoryRepository.getAllCoordinatorLogEntries());\n\n\t\t\t\t\t//XA reset status now\n\t\t\t\t\tmysqlCon.setXaStatus(TxState.TX_INITIALIZE_STATE);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\t//\tLOGGER.error(\"Wrong XA status flag!\");\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (this.cmdHandler.relaseConOnOK()) {\n\t\t\tif(prepareCount.get() == 0) {\n\t\t\t\tsession.releaseConnection(conn);\n\t\t\t}\n\t\t} else {\n\t\t\tsession.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(),\n\t\t\t\t\tfalse);\n\t\t}\n\t\tif (this.finished()) {\n\t\t\tif (cmdHandler.isAutoClearSessionCons()) {\n\t\t\t\tsession.clearResources(false);\n\t\t\t}\n\t\t\t/* 1.  事务提交后,xa 事务结束   */\n\t\t\tif(session.getXaTXID()!=null){\n\t\t\t\tsession.setXATXEnabled(false);\n\t\t\t}\n\t\t\t\n\t\t\t/* 2. preAcStates 为true,事务结束后,需要设置为true。preAcStates 为ac上一个状态    */\n\t\t\tif(session.getSource().isPreAcStates()){\n\t\t\t\tsession.getSource().setAutocommit(true);\n\t\t\t}\n\t\t\tcmdHandler.okResponse(session, ok);\n\n\t\t}\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n//\t\tfaileCount.incrementAndGet();\n\t\tif (LOGGER.isInfoEnabled()){\n\t\t\tLOGGER.info(\"======================\" + \"connectionClose from {} msg: {}\", conn, reason);\n\t\t}\n\t\t\n\t\tif(session.getXaTXID() != null) {\n\t\t\tif(conn instanceof MySQLConnection) {\n\t\t\t\tMySQLConnection mysqlCon = (MySQLConnection) conn;\n\t\t\t\tif(coorXaStatus == TxState.TX_STARTED_STATE) {\n\t\t\t\t\tthis.setFail(reason);\n\t\t\t\t\t// 1 pc prepare failure to rollback \n\t\t\t\t\tLOGGER.error(\"XA prepare failure in :xa end;xa prepare; XA id is:\" +session.getXaTXID() + \" errmsg {},current conn:{}\", reason ,conn);\n\t\t\t\t\tif(prepareCount.decrementAndGet() == 0) {\n\t\t\t\t\t\tthis.tryErrorFinished(true);\n\t\t\t\t\t}\n\t\t\t\t\treturn ;\n\t\t\t\t\t\n\t\t\t\t}\t\n\t\t\t\t//replayCommit prepare statue replay commit\n\t\t\t\t//todo 重新创建 连接 重新进行提交 conn 是prepare阶段的时候\n\t\t\t\tif(coorXaStatus == TxState.TX_PREPARED_STATE) {\n\t\t\t\t\tString xaTxId = session.getXaTXID();\n\t\t\t\t\tif (xaTxId != null) {\n\t\t\t\t\t\txaTxId += \",'\" + mysqlCon.getSchema() + \"'\";\n\t\t\t\t\t\tString cmd = \"XA COMMIT \" + xaTxId;\n\t\t\t\t\t\tLOGGER.error(\"XA Comit failure in :\"+cmd+\" errmsg {},current conn:{}\", reason ,conn);\n//\t\t\t\t\t\tmysqlCon.execCmd(cmd);\n\t\t\t\t\t\t//return;\n\t\t\t\t\t}\n\n\t\t\t\t}\t\t\t\n\t\t\t}\n\t\t}\n\t\t//release connection\n\t\tif (this.cmdHandler.releaseConOnErr()) {\n\t\t\tsession.releaseConnection(conn);\n\t\t} else {\n\t\t\tsession.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(),false);\n\t\t}\n\t\t\n\t\t//设置错误信息\n\t\tthis.setFail(\"close back conn reason:\"  + reason);\n\t\tif (this.finished()) {\n\t\t\tthis.tryErrorFinished(true);\n//\t\t\tErrorPacket errPkg = new ErrorPacket();\n//\t\t\terrPkg.errno = 1;\n//\t\t\terrPkg.message = (\"close back conn reason:\"  + reason).getBytes();\n//\t\t\tcmdHandler.errorResponse(session, errPkg.writeToBytes() , this.nodeCount,\n//\t\t\t\t\tthis.faileCount.get());\n//\t\t\tif (cmdHandler.isAutoClearSessionCons()) {\n//\t\t\t\tsession.clearResources(session.getSource().isTxInterrupted());\n//\t\t\t}\n\t\t}\n\t}\n\t//设置失败\n\tprivate void setFail(String err){\n\t\tthis.error = err;\n\t\tfaileCount.incrementAndGet();\n\t}\n\tprotected void tryErrorFinished(boolean allEnd) {\n\t\tif (allEnd && !session.closed()) {\t\t\n\t\t\t// xa error auto rollback \n\t\t\tif(coorXaStatus == TxState.TX_STARTED_STATE ) {\n\t\t\t\tLOGGER.info(\"{} found failure in xa ,so to rollback ,reason :{}\",this, this.error);\n\t\t\t\tsession.rollback();\n\t\t\t\treturn ;\n\t\t\t} \n\t\t\t\n\t\t\t// clear session resources,release all\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(this.toString()+\"error all end ,clear session resource \");\n\t\t\t}\n\t\t\t//释放连接\n\t\t\tsession.clearResources(session.getSource().isTxInterrupted());\n\n\t\t\t//关闭所有的错误后端连接 清理资源\n//\t\t\tsession.closeAndClearResources(error);\n\t\t\t\n\t\t\tErrorPacket errPkg = new ErrorPacket();\n\t\t\terrPkg.packetId= 1;\n\t\t\terrPkg.errno =  ErrorCode.ER_UNKNOWN_ERROR;\n\t\t\terrPkg.message = (this.error).getBytes();\n\t\t\tsession.setAutoCommitStatus();\n\t\t\tcmdHandler.errorResponse(session, errPkg.writeToBytes(), this.nodeCount,\n\t\t\t\t\tthis.faileCount.get());\n\t\t\tsession.getSource().clearTxInterrupt();\n\t\t\t//if (cmdHandler.isAutoClearSessionCons()) {\n\t\t\t//}\n\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/MultiNodeHandler.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.backend.mysql.nio.handler;\r\n\r\nimport java.util.concurrent.atomic.AtomicBoolean;\r\nimport java.util.concurrent.locks.ReentrantLock;\r\n\r\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\r\n\r\nimport io.mycat.backend.BackendConnection;\r\nimport io.mycat.config.ErrorCode;\r\nimport io.mycat.net.mysql.ErrorPacket;\r\nimport io.mycat.server.NonBlockingSession;\r\nimport io.mycat.util.StringUtil;\r\n\r\n/**\r\n * @author mycat\r\n */\r\nabstract class MultiNodeHandler implements ResponseHandler, Terminatable {\r\n\tprivate static final Logger LOGGER = LoggerFactory\r\n\t\t\t.getLogger(MultiNodeHandler.class);\r\n\tprotected final ReentrantLock lock = new ReentrantLock();\r\n\tprotected final NonBlockingSession session;\r\n\tprivate AtomicBoolean isFailed = new AtomicBoolean(false);\r\n\tprotected volatile String error;\r\n\tprotected byte packetId;\r\n\tpublic final AtomicBoolean errorRepsponsed = new AtomicBoolean(false);\r\n\t\r\n\tpublic MultiNodeHandler(NonBlockingSession session) {\r\n\t\tif (session == null) {\r\n\t\t\tthrow new IllegalArgumentException(\"session is null!\");\r\n\t\t}\r\n\t\tthis.session = session;\r\n\t}\r\n\r\n\tpublic void setFail(String errMsg) {\r\n\t\tisFailed.set(true);\r\n\t\terror = errMsg;\r\n\t}\r\n\r\n\tpublic boolean isFail() {\r\n\t\treturn isFailed.get();\r\n\t}\r\n\r\n\tprotected int nodeCount;\r\n\r\n\tprivate Runnable terminateCallBack;\r\n\r\n\t@Override\r\n\tpublic void terminate(Runnable terminateCallBack) {\r\n\t\tboolean zeroReached = false;\r\n\t\tlock.lock();\r\n\t\ttry {\r\n\t\t\tif (nodeCount > 0) {\r\n\t\t\t\tthis.terminateCallBack = terminateCallBack;\r\n\t\t\t} else {\r\n\t\t\t\tzeroReached = true;\r\n\t\t\t}\r\n\t\t} finally {\r\n\t\t\tlock.unlock();\r\n\t\t}\r\n\t\tif (zeroReached) {\r\n\t\t\tterminateCallBack.run();\r\n\t\t}\r\n\t}\r\n\r\n\tprotected boolean canClose(BackendConnection conn, boolean tryErrorFinish) {\r\n\r\n\t\t// realse this connection if safe\r\n\t\tsession.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(), false);\r\n\t\tboolean allFinished = false;\r\n\t\tif (tryErrorFinish) {\r\n\t\t\tallFinished = this.decrementCountBy(1);\r\n\t\t\tthis.tryErrorFinished(allFinished);\r\n\t\t}\r\n\r\n\t\treturn allFinished;\r\n\t}\r\n\r\n\tprotected void decrementCountToZero() {\r\n\t\tRunnable callback;\r\n\t\tlock.lock();\r\n\t\ttry {\r\n\t\t\tnodeCount = 0;\r\n\t\t\tcallback = this.terminateCallBack;\r\n\t\t\tthis.terminateCallBack = null;\r\n\t\t} finally {\r\n\t\t\tlock.unlock();\r\n\t\t}\r\n\t\tif (callback != null) {\r\n\t\t\tcallback.run();\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\r\n\t\tsetFail(\"backend connect: \"+e);\r\n\t\tif (LOGGER.isDebugEnabled()) {\r\n\t\t\tLOGGER.debug(this.toString() +\"on connectionError \" + conn + \"  is has Error\"+ errorRepsponsed.get() +\"  not receive num\"+nodeCount);\r\n\t\t}\r\n\t\tfinal boolean canClose = decrementCountBy(1);\r\n\t\t// 需要把Throwable e的错误信息保存下来（setFail()）， 否则会导致响应 \r\n\t\t//null信息，结果mysql命令行等客户端查询结果是\"Query OK\"！！\r\n\t\t// @author Uncle-pan\r\n\t\t// @since 2016-03-26\r\n\t\t//if(canClose){\r\n\t\t//}\r\n\t\tLOGGER.warn(this + \"backend connect\", e);\r\n//\t\tif(canClose) {\r\n//\t\t}\r\n\t\tthis.tryErrorFinished(canClose);\r\n\t}\r\n\r\n\tpublic void errorResponse(byte[] data, BackendConnection conn) {\r\n\t\tsession.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(), false);\r\n\t\tErrorPacket err = new ErrorPacket();\r\n\t\terr.read(data);\r\n\t\t\r\n\t\tString errmsg = new String(err.message);\r\n\t\tthis.setFail(errmsg);\r\n\t\t\r\n\t\tLOGGER.warn(this.toString() +\"error response from \" + conn + \" err \" + errmsg + \" code:\" + err.errno);\r\n\t\t\r\n\t\tthis.tryErrorFinished(this.decrementCountBy(1));\r\n\t}\r\n\r\n\tpublic boolean clearIfSessionClosed(NonBlockingSession session) {\r\n\t\tif (session.closed()) {\r\n\t\t\tif (LOGGER.isDebugEnabled()) {\r\n\t\t\t\tLOGGER.debug(\"session closed ,clear resources \" + session);\r\n\t\t\t}\r\n\r\n\t\t\tsession.clearResources(true);\r\n\t\t\tthis.clearResources();\r\n\t\t\treturn true;\r\n\t\t} else {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tprotected boolean decrementCountBy(int finished) {\r\n\t\tboolean zeroReached = false;\r\n\t\tRunnable callback = null;\r\n\t\tlock.lock();\r\n\t\ttry {\r\n\t\t\tnodeCount -= finished;\r\n\t\t\tzeroReached = nodeCount == 0;\r\n\t\t\tif (zeroReached) {\r\n\t\t\t\tcallback = this.terminateCallBack;\r\n\t\t\t\tthis.terminateCallBack = null;\r\n\t\t\t}\r\n\t\t} finally {\r\n\t\t\tlock.unlock();\r\n\t\t}\r\n\t\tif (zeroReached && callback != null) {\r\n\t\t\tcallback.run();\r\n\t\t}\r\n\t\treturn zeroReached;\r\n\t}\r\n\r\n\tprotected void reset(int initCount) {\r\n\t\tnodeCount = initCount;\r\n\t\tisFailed.set(false);\r\n\t\terror = null;\r\n\t\tpacketId = 0;\r\n\t}\r\n\r\n\tprotected ErrorPacket createErrPkg(String errmgs) {\r\n\t\tErrorPacket err = new ErrorPacket();\r\n\t\tlock.lock();\r\n\t\ttry {\r\n\t\t\terr.packetId = ++packetId;\r\n\t\t} finally {\r\n\t\t\tlock.unlock();\r\n\t\t}\r\n\t\terr.errno = ErrorCode.ER_UNKNOWN_ERROR;\r\n\t\terr.message = StringUtil.encode(errmgs, session.getSource().getCharset());\r\n\t\treturn err;\r\n\t}\r\n\r\n\tprotected void tryErrorFinished(boolean allEnd) {\r\n\t\tif (allEnd && !session.closed()) {\r\n\t\t\t\r\n\r\n\t\t\t// clear session resources,release all\r\n\t\t\tif (LOGGER.isDebugEnabled()) {\r\n\t\t\t\tLOGGER.debug(this.toString()+\"error all end ,clear session resource \");\r\n\t\t\t\tLOGGER.debug(\"error all end ,clear session resource \");\r\n\t\t\t}\r\n\t\t\tif (session.getSource().isAutocommit()) {\r\n\t\t\t\tsession.closeAndClearResources(error);\r\n\t\t\t} else {\r\n\t\t\t\tsession.getSource().setTxInterrupt(this.error);\r\n\t\t\t\t// clear resouces\r\n\t\t\t\tclearResources();\r\n\t\t\t}\r\n\t\t\tif (errorRepsponsed.compareAndSet(false, true)) {\r\n\t\t\t\t//createErrPkg(this.error).write(session.getSource());\r\n\t\t\t\tsession.getSource().writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR, this.error);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tpublic void connectionClose(BackendConnection conn, String reason) {\r\n\t\tthis.setFail(\"closed connection:\" + reason + \" con:\" + conn);\r\n\t\tif (LOGGER.isDebugEnabled()) {\r\n\t\t\tLOGGER.debug(this.toString()+ \"closed connection:\" + reason + \" con:\" + conn);\r\n\t\t}\r\n\t\tboolean finished = false;\r\n\t\tlock.lock();\r\n\t\ttry {\r\n\t\t\tfinished = (this.nodeCount == 0);\r\n\r\n\t\t} finally {\r\n\t\t\tlock.unlock();\r\n\t\t}\r\n\t\tif (finished == false) {\r\n\t\t\tfinished = this.decrementCountBy(1);\r\n\t\t}\r\n\t\tif (error == null) {\r\n\t\t\terror = \"back connection closed \";\r\n\t\t}\r\n\t\ttryErrorFinished(finished);\r\n\t}\r\n\r\n\tpublic void clearResources() {\r\n\t}\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/MultiNodeQueryHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.mysql.CharsetUtil;\nimport io.mycat.backend.mysql.LoadDataUtil;\nimport io.mycat.backend.mysql.listener.SqlExecuteStage;\nimport io.mycat.backend.mysql.nio.MySQLConnection;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.memory.unsafe.row.UnsafeRow;\nimport io.mycat.net.mysql.BinaryRowDataPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.NonBlockingSession;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.sqlengine.mpp.AbstractDataNodeMerge;\nimport io.mycat.sqlengine.mpp.ColMeta;\nimport io.mycat.sqlengine.mpp.DataMergeService;\nimport io.mycat.sqlengine.mpp.DataNodeMergeManager;\nimport io.mycat.sqlengine.mpp.MergeCol;\nimport io.mycat.statistic.stat.QueryResult;\nimport io.mycat.statistic.stat.QueryResultDispatcher;\nimport io.mycat.util.ResultSetUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class MultiNodeQueryHandler extends MultiNodeHandler implements LoadDataResponseHandler {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(MultiNodeQueryHandler.class);\n\n\tprivate final RouteResultset rrs;\n\tprivate final NonBlockingSession session;\n\t// private final CommitNodeHandler icHandler;\n\tprivate final AbstractDataNodeMerge dataMergeSvr;\n\tprivate final boolean autocommit;\n\tprivate String priamaryKeyTable = null;\n\tprivate int primaryKeyIndex = -1;\n\tprivate int fieldCount = 0;\n\t// private final ReentrantLock lock;\n\tprivate long affectedRows;\n\tprivate long selectRows;\n\tprivate long insertId;\n\tprivate volatile boolean fieldsReturned;\n\tprivate int okCount;\n\tprivate final boolean isCallProcedure;\n\tprivate long startTime;\n\tprivate long netInBytes;\n\tprivate long netOutBytes;\n\tprivate int execCount = 0;\n\n\tprivate boolean prepared;\n\tprivate List<FieldPacket> fieldPackets = new ArrayList<FieldPacket>();\n\tprivate int isOffHeapuseOffHeapForMerge = 1;\n\t//huangyiming add  中间处理结果是否处理完毕\n\tprivate final AtomicBoolean isMiddleResultDone;\n\t/**\n\t * Limit N，M\n\t */\n\tprivate   int limitStart;\n\tprivate   int limitSize;\n\n\tprivate int index = 0;\n\n\tprivate int end = 0;\n\n\t//huangyiming\n\tprivate byte[] header = null;\n\tprivate List<byte[]> fields = null;\n    protected List<BackendConnection> errConnections;\n\n\t// by kaiz : 为了解决Mybatis获取由Mycat生成自增主键时，MySQL返回的Last_insert_id为最大值的问题；\n\t//\t\t当逻辑表设置了autoIncrement='false'时，MyCAT会将ok packet当中的最小last insert id记录下来，返回给应用\n\t// \t\t当逻辑表设置了autoIncrement='true'时，MyCAT会将ok packet当中的最大的last insert id记录下来，然后减掉affected rows的数量后，返回给应用\n\n\tpublic MultiNodeQueryHandler(int sqlType, RouteResultset rrs,\n\t\t\tboolean autocommit, NonBlockingSession session) {\n\n\t\tsuper(session);\n \t\tthis.isMiddleResultDone = new AtomicBoolean(false);\n\n\t\tif (rrs.getNodes() == null) {\n\t\t\tthrow new IllegalArgumentException(\"routeNode is null!\");\n\t\t}\n\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"execute mutinode query \" + rrs.getStatement());\n\t\t}\n\n\t\tthis.rrs = rrs;\n\t\tisOffHeapuseOffHeapForMerge = MycatServer.getInstance().\n\t\t\t\tgetConfig().getSystem().getUseOffHeapForMerge();\n\t\tif (ServerParse.SELECT == sqlType && rrs.needMerge()) {\n\t\t\t/**\n\t\t\t * 使用Off Heap\n\t\t\t */\n\t\t\tif(isOffHeapuseOffHeapForMerge == 1){\n\t\t\t\tdataMergeSvr = new DataNodeMergeManager(this,rrs,isMiddleResultDone);\n\t\t\t}else {\n\t\t\t\tdataMergeSvr = new DataMergeService(this,rrs);\n\t\t\t}\n\t\t} else {\n\t\t\tdataMergeSvr = null;\n\t\t}\n\n\t\tisCallProcedure = rrs.isCallStatement();\n\t\tthis.autocommit = session.getSource().isAutocommit();\n\t\tthis.session = session;\n\t\t// this.lock = new ReentrantLock();\n\t\t// this.icHandler = new CommitNodeHandler(session);\n\n\t\tthis.limitStart = rrs.getLimitStart();\n\t\tthis.limitSize = rrs.getLimitSize();\n\t\tthis.end = limitStart + rrs.getLimitSize();\n\n\t\tif (this.limitStart < 0)\n\t\t\tthis.limitStart = 0;\n\n\t\tif (rrs.getLimitSize() < 0)\n\t\t\tend = Integer.MAX_VALUE;\n\t\tif ((dataMergeSvr != null)\n\t\t\t\t&& LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(\"has data merge logic \");\n\t\t}\n\n\t\tif ( rrs != null && rrs.getStatement() != null) {\n\t\t\tnetInBytes += rrs.getStatement().getBytes().length;\n\t\t}\n        this.errConnections = new ArrayList<>();\n    }\n\n\tprotected void reset(int initCount) {\n\t\tsuper.reset(initCount);\n\t\tthis.okCount = initCount;\n\t\tthis.execCount = 0;\n\t\tthis.netInBytes = 0;\n\t\tthis.netOutBytes = 0;\n\n\t\tif (rrs.isLoadData()) {\n\t\t\tpacketId = session.getSource().getLoadDataInfileHandler().getLastPackId();\n\t\t}\n\t}\n\n\tprivate synchronized int incExecCount() {\n\t\treturn ++this.execCount;\n\t}\n\n\tpublic NonBlockingSession getSession() {\n\t\treturn session;\n\t}\n\n\tpublic void execute() throws Exception {\n\t\tfinal ReentrantLock lock = this.lock;\n\t\tlock.lock();\n\t\ttry {\n\t\t\tthis.reset(rrs.getNodes().length);\n\t\t\tthis.fieldsReturned = false;\n\t\t\tthis.affectedRows = 0L;\n\t\t\tthis.insertId = 0L;\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\tstartTime = System.currentTimeMillis();\n\t\tLOGGER.debug(\"rrs.getRunOnSlave()-\" + rrs.getRunOnSlaveDebugInfo());\n\t\t//todo 增加处理如果超过最大链接的处理。是zwy 2018.07\n\t\tint start = 0;\n\t\ttry {\n\t\t\tfor (final RouteResultsetNode node : rrs.getNodes()) {\n\t\t\t\tBackendConnection conn = session.getTarget(node);\n\t\t\t\tif (session.tryExistsCon(conn, node)) {\n\t\t\t\t\tif(LOGGER.isDebugEnabled()) {\n\t\t\t\t\t\tLOGGER.debug(\"node.getRunOnSlave()-\" + node.getRunOnSlave());\n\t\t\t            LOGGER.debug(new StringBuilder(this.toString()).append(session.getSource()).append(rrs).toString());\n\t\t\t\t\t}\n\t\t\t\t\tnode.setRunOnSlave(rrs.getRunOnSlave());\t// 实现 master/slave注解\t\n\t\t\t\t\tif(LOGGER.isDebugEnabled()) {\n\t\t\t\t\t\tLOGGER.debug(\"node.getRunOnSlave()-\" + node.getRunOnSlave());\n\t\t\t\t\t}\n\t\t\t\t\t_execute(conn, node);\n\t\t\t\t} else {\n\t\t\t\t\t// create new connection\n\t\t\t\t\t//LOGGER.debug(\"node.getRunOnSlave()1-\" + node.getRunOnSlave());\n\t\t\t\t\tnode.setRunOnSlave(rrs.getRunOnSlave());\t// 实现 master/slave注解\n\t\t\t\t\t//LOGGER.debug(\"node.getRunOnSlave()2-\" + node.getRunOnSlave());\n\t\t\t\t\tPhysicalDBNode dn = conf.getDataNodes().get(node.getName());\n\t\t\t\t\tdn.getConnection(dn.getDatabase(), autocommit, node, this, node);\n\t\t\t\t\t// 注意该方法不仅仅是获取连接，获取新连接成功之后，会通过层层回调，最后回调到本类 的connectionAcquired\n\t\t\t\t\t// 这是通过 上面方法的 this 参数的层层传递完成的。\n\t\t\t\t\t// connectionAcquired 进行执行操作:\n\t\t\t\t\t// session.bindConnection(node, conn);\n\t\t\t\t\t// _execute(conn, node); \n\t\t\t\t}\n\t\t\t\tstart++;\n\t\t\t}\n\t\t}catch (Exception e) {\n\t\t\tServerConnection source = session.getSource();\n            int len = rrs.getNodes().length - start;\n            for(int i = 0 ; i < len ; i++) {\n            \t//flag = this.decrementCountBy(1);\n            \t this.connectionError(e, null);\n            }\n            LOGGER.error(new StringBuilder(this.toString()).append(source).append(rrs).toString(), e);\n           // this.connectionError(e, null);\n           // if(flag) {\n           //     LOGGER.error(new StringBuilder(this.toString()).append(source).append(rrs).toString(), e);\n            //}\n\t\t}\n\t}\n\n\tprivate void _execute(BackendConnection conn, RouteResultsetNode node) {\n\t\tif (clearIfSessionClosed(session)) {\n\t\t\treturn;\n\t\t}\n\t\tconn.setResponseHandler(this);\n\t\ttry {\n            // conn.execute(node, session.getSource(), autocommit);\n            /**\n             *  多节点下，非DDL的修改语句，自动开启事务；DDL语句不开启事务，原因是ddl语句会引发自动提交，如果在事务中会引起下面的问题。\n             *  1）如果是普通事务,直接引发自动提交；\n             *  2）XA事务，自动提交导致ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the  ACTIVE state\n             * \n             */\n            conn.execute(node, session.getSource(), autocommit && !node.isModifySQLExceptDDL());\n\t\t} catch (IOException e) {\n\t\t\tconnectionError(e, conn);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(final BackendConnection conn) {\n\t\tfinal RouteResultsetNode node = (RouteResultsetNode) conn\n\t\t\t\t.getAttachment();\n\t\tsession.bindConnection(node, conn);\n\t\tif(errorRepsponsed.get()) {\n\t\t\tServerConnection source = session.getSource();\t\t\t\n\t\t\tLOGGER.warn(new StringBuilder(this.toString()).append(source).append(rrs).toString(), \"connectionAcquired\",conn);\n\t\t\tthis.connectionClose(conn, \"find error, so close this connection\");\n\t\t\treturn ;\n\t\t}\n\t\t_execute(conn, node);\n\t}\n\n\tprivate boolean decrementOkCountBy(int finished) {\n\t\tlock.lock();\n\t\ttry {\n\t\t\treturn --okCount == 0;\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\n    @Override\n    public void errorResponse(byte[] data, BackendConnection conn) {\n        ErrorPacket errPacket = new ErrorPacket();\n        errPacket.read(data);\n\n        lock.lock();\n        try {\n            if (!isFail()) {\n                setFail(new String(errPacket.message));\n            }\n            errConnections.add(conn);\n            conn.syncAndExcute();\n\n            if (--nodeCount == 0) {\n                errPacket.packetId = ++packetId;\n                processFinishWork(errPacket.writeToBytes(), false, conn);\n            }\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    /**\n     * wangkai 后端的mysql连接被关闭调用到这里；\n     * 2019年3月13日,异常走executeError，保证最后走handleEndPacket，保证XA顺利释放资源\n     */\n    @Override\n    public void connectionClose(BackendConnection conn, String reason) {\n        LOGGER.warn(\"backend connect\" + reason + \", conn info:\" + conn);\n        ErrorPacket errPacket = new ErrorPacket();\n        errPacket.errno = ErrorCode.ER_ABORTING_CONNECTION;\n        reason = \"Connection {DataHost[\" + conn.getHost() + \":\" + conn.getPort() + \"],Schema[\" + conn.getSchema()\n                + \"],threadID[\" + ((MySQLConnection) conn).getThreadId() + \"]} was closed ,reason is [\" + reason + \"]\";\n        errPacket.message = StringUtil.encode(reason, session.getSource().getCharset());\n        executeError(conn, errPacket);\n    }\n\n    @Override\n    public void connectionError(Throwable e, BackendConnection conn) {\n        LOGGER.info(\"backend connect\", e);\n        ErrorPacket errPacket = new ErrorPacket();\n        errPacket.errno = ErrorCode.ER_ABORTING_CONNECTION;\n        \n        String errMsg = null;\n        if(conn == null) {\n\t\t\terrMsg = e.toString();\n        }else {\n            errMsg = \"Backend connect Error, Connection{DataHost[\" + conn.getHost() + \":\" + conn.getPort()\n                + \"],Schema[\" + conn.getSchema() + \"]} refused\";\n        }\n        \n        errPacket.message = StringUtil.encode(errMsg, session.getSource().getCharset());\n        executeError(conn, errPacket);\n    }\n\n    private void executeError(BackendConnection conn, ErrorPacket errPacket) {\n        lock.lock();\n        try {\n            if (!isFail()) {\n                setFail(new String(errPacket.message));\n            }\n            \n            if(conn != null) {\n              errConnections.add(conn);\n            }\n            \n            if (--nodeCount == 0) {\n                errPacket.packetId = ++packetId;\n                processFinishWork(errPacket.writeToBytes(), false, conn);\n            }\n        } finally {\n            lock.unlock();\n        }\n    }\n\t@Override\n\tpublic void okResponse(byte[] data, BackendConnection conn) {\n\n\t\tthis.netOutBytes += data.length;\n\n\t\tboolean executeResponse = conn.syncAndExcute();\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"received ok response ,executeResponse:\"\n\t\t\t\t\t+ executeResponse + \" from \" + conn);\n\t\t}\n\t\tif (executeResponse) {\n\n\t\t\tServerConnection source = session.getSource();\n\t\t\tOkPacket ok = new OkPacket();\n\t\t\tok.read(data);\n\n\t\t\tlock.lock();\n\t\t\ttry {\n\t\t\t\taffectedRows += ok.affectedRows;\n\t\t\t} finally {\n\t\t\t\tlock.unlock();\n\t\t\t}\n\n\t\t\tif (ok.hasMoreResultsExists()) {\n\t\t\t\t// funnyAnt:当是批量update/delete语句，提示后面还有ok包\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// 存储过程\n\t\t\tboolean isCanClose2Client = (!rrs.isCallStatement())\n\t\t\t\t\t|| (rrs.isCallStatement() && !rrs.getProcedure().isResultSimpleValue());\n\n\t\t\t // if(!isCallProcedure)\n            // {\n            // if (clearIfSessionClosed(session))\n            // {\n            // return;\n            // } else if (canClose(conn, false))\n            // {\n            // return;\n            // }\n            // }\n\n\t\t\tlock.lock();\n\t\t\ttry {\n\t\t\t\tif (ok.insertId > 0) {\n\t\t\t\t\tif (rrs.getAutoIncrement()) {\n\t\t\t\t\t\tinsertId = (insertId == 0) ? ok.insertId : Math.max(insertId, ok.insertId);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tinsertId = (insertId == 0) ? ok.insertId : Math.min(insertId, ok.insertId);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\t} finally {\n\t\t\t\tlock.unlock();\n\t\t\t}\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(this.toString() +\"on row okResponse \" + conn + \"  \"+ errorRepsponsed.get() +\"  \"+nodeCount);\n\t\t\t}\n\t\t\t// 对于存储过程，其比较特殊，查询结果返回EndRow报文以后，还会再返回一个OK报文，才算结束\n\t\t\tboolean isEndPacket = isCallProcedure ? decrementOkCountBy(1): decrementCountBy(1);\n\t\t\tif (isEndPacket && isCanClose2Client) {\n\n                // if (this.autocommit && !session.getSource().isLocked()) {// clear all\n                // connections\n                // session.releaseConnections(false);\n                // }\n\n\t\t\t\tif (this.isFail() || session.closed()) {\n                    // tryErrorFinished(true);\n                    processFinishWork(createErrPkg(this.error).writeToBytes(), false, conn);\n                    return;\n\t\t\t\t}\n\n\t\t\t\tlock.lock();\n\t\t\t\ttry {\n\t\t\t\t\tok.packetId = ++packetId;// OK_PACKET\n\t\t\t\t\tif (rrs.isLoadData()) {\n\t\t\t\t\t\tok.message = (\"Records: \" + affectedRows + \"  Deleted: 0  Skipped: 0  Warnings: 0\")\n\t\t\t\t\t\t\t\t.getBytes();// 此处信息只是为了控制台给人看的\n\t\t\t\t\t\tsource.getLoadDataInfileHandler().clear();\n\t\t\t\t\t}\n\n\t\t\t\t\t// 如果是全局表，去除掉重复计算的部分\n\t\t\t\t\tif (rrs.isGlobalTable()) {\n\t\t\t\t\t\taffectedRows = affectedRows / rrs.getNodes().length;\n\t\t\t\t\t}\n\t\t\t\t\tok.affectedRows = affectedRows;\n\t\t\t\t\tok.serverStatus = source.isAutocommit() ? 2 : 1;\n\t\t\t\t\tif (insertId > 0) {\n\t\t\t\t\t\tok.insertId = rrs.getAutoIncrement() ? (insertId - affectedRows + 1) : insertId;\n\t\t\t\t\t\tsource.setLastInsertId(insertId);\n\t\t\t\t\t}\n\t\t\t\t\t//  判断是否已经报错返回给前台了 2018.07 \n\t\t\t\t\tif(source.canResponse()) {\n                        // ok.write(source);\n                        processFinishWork(ok.writeToBytes(), true, conn);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\thandleDataProcessException(e);\n\t\t\t\t} finally {\n\t\t\t\t\tlock.unlock();\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\t// add by lian\n\t\t\t// 解决sql统计中写操作永远为0\n\t\t\tint currentExecCount = this.incExecCount();\n\t\t\tif (currentExecCount == rrs.getNodes().length) {\n\t\t\t\tsource.setExecuteSql(null);  //完善show @@connection.sql 监控命令.已经执行完的sql 不再显示\n                QueryResult queryResult = new QueryResult(session.getSource().getSchema(),\n                        session.getSource().getUser(),\n\t\t\t\t\t\trrs.getSqlType(), rrs.getStatement(), selectRows, netInBytes, netOutBytes, startTime, System.currentTimeMillis(),0,source.getHost());\n\t\t\t\tQueryResultDispatcher.dispatchQuery( queryResult );\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(final byte[] eof, BackendConnection conn) {\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(this.toString() +\"on row end reseponse \" + conn + \"  \"+ errorRepsponsed.get() +\"  \"+nodeCount);\n\t\t}\n\n\t\tthis.netOutBytes += eof.length;\n\t\tMiddlerResultHandler middlerResultHandler = session.getMiddlerResultHandler();\n\n\t\tif (errorRepsponsed.get()) {\n\t\t\t// the connection has been closed or set to \"txInterrupt\" properly\n\t\t\t//in tryErrorFinished() method! If we close it here, it can\n\t\t\t// lead to tx error such as blocking rollback tx for ever.\n\t\t\t// @author Uncle-pan\n\t\t\t// @since 2016-03-25\n\t\t\t// conn.close(this.error);\n\t\t\treturn;\n\t\t}\n\n\t\tfinal ServerConnection source = session.getSource();\n\t\tif (!isCallProcedure) {\n\t\t\tif (clearIfSessionClosed(session)) {\n\t\t\t\treturn;\n\t\t\t} else if (canClose(conn, false)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (decrementCountBy(1)) {\n            if (!rrs.isCallStatement()||(rrs.isCallStatement()&&rrs.getProcedure().isResultSimpleValue())) {\n\t\t\t\tif (this.autocommit && !session.getSource().isLocked()) {// clear all connections\n\t\t\t\t\tsession.releaseConnections(false);\n\t\t\t\t}\n\n\t\t\t\tif (this.isFail() || session.closed()) {\n                    // tryErrorFinished(true);\n                    processFinishWork(createErrPkg(this.error).writeToBytes(), false, conn);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (dataMergeSvr != null) {\n\t\t\t\t//huangyiming add 数据合并前如果有中间过程则先执行数据合并再执行下一步\n\t\t\t\tif(session.getMiddlerResultHandler() !=null  ){\n\t\t\t\t\tisMiddleResultDone.set(true);\n            \t}\n\n\t\t\t\ttry {\n\t\t\t\t\tdataMergeSvr.outputMergeResult(session, eof);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\thandleDataProcessException(e);\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tlock.lock();\n\t\t\t\t\teof[3] = ++packetId;\n\t\t\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\t\t\tLOGGER.debug(\"last packet id:\" + packetId);\n\t\t\t\t\t}\n\t\t\t\t\tif(  middlerResultHandler ==null ){\n\t\t\t\t\t\t//middlerResultHandler.secondEexcute();\n\t\t\t\t\t\tif(source.canResponse()) {\n\t\t\t\t\t\t\tsource.write(eof);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n \t\t\t\t} finally {\n\t\t\t\t\tlock.unlock();\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tint currentExecCount = this.incExecCount();\n\t\tif(middlerResultHandler !=null){\n\t\t\tif (currentExecCount != rrs.getNodes().length) {\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t/*else{\n\t\t\t\tmiddlerResultHandler.secondEexcute(); \n\t\t\t}*/\n\t\t}\n\t\tif (currentExecCount == rrs.getNodes().length) {\n\t\t\tint resultSize = source.getWriteQueue().size()*MycatServer.getInstance().getConfig().getSystem().getBufferPoolPageSize();\n\t\t\tsource.setExecuteSql(null);  //完善show @@connection.sql 监控命令.已经执行完的sql 不再显示\n            session.getSource().getListener().fireEvent(SqlExecuteStage.END);\n\n\t\t\t//TODO: add by zhuam\n\t\t\t//查询结果派发\n            QueryResult queryResult = new QueryResult(session.getSource().getSchema(), session.getSource().getUser(),\n\t\t\t\t\trrs.getSqlType(), rrs.getStatement(), selectRows, netInBytes, netOutBytes, startTime, System.currentTimeMillis(),resultSize, source.getHost());\n\t\t\tQueryResultDispatcher.dispatchQuery( queryResult );\n\n\n\t\t\t//\tadd huangyiming  如果是中间过程,必须等数据合并好了再进行下一步语句的拼装\n \t\t\tif(middlerResultHandler !=null ){\n \t\t\t\twhile (!this.isMiddleResultDone.compareAndSet(false, true)) {\n \t                Thread.yield();\n \t             }\n \t\t\t\tmiddlerResultHandler.secondEexcute();\n\t\t\t\tisMiddleResultDone.set(false);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * 将汇聚结果集数据真正的发送给Mycat客户端\n\t * @param source\n\t * @param eof\n\t * @param\n\t */\n\tpublic void outputMergeResult(final ServerConnection source, final byte[] eof, Iterator<UnsafeRow> iter,AtomicBoolean isMiddleResultDone) {\n\n\t\ttry {\n\t\t\tlock.lock();\n\t\t\tByteBuffer buffer = session.getSource().allocate();\n\t\t\tfinal RouteResultset rrs = this.dataMergeSvr.getRrs();\n\n\t\t\t/**\n\t\t\t * 处理limit语句的start 和 end位置，将正确的结果发送给\n\t\t\t * Mycat 客户端\n\t\t\t */\n\t\t\tint start = rrs.getLimitStart();\n\t\t\tint end = start + rrs.getLimitSize();\n\t\t\tint index = 0;\n\n\t\t\tif (start < 0)\n\t\t\t\tstart = 0;\n\n\t\t\tif (rrs.getLimitSize() < 0)\n\t\t\t\tend = Integer.MAX_VALUE;\n\n\t\t\tif(prepared) {\n \t\t\t\twhile (iter.hasNext()){\n\t\t\t\t\tUnsafeRow row = iter.next();\n\t\t\t\t\tif(index >= start){\n\t\t\t\t\t\trow.packetId = ++packetId;\n\t\t\t\t\t\tBinaryRowDataPacket binRowPacket = new BinaryRowDataPacket();\n\t\t\t\t\t\tbinRowPacket.read(fieldPackets, row);\n\t\t\t\t\t\tbuffer = binRowPacket.write(buffer, source, true);\n\t\t\t\t\t}\n\t\t\t\t\tindex++;\n\t\t\t\t\tif(index == end){\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twhile (iter.hasNext()){\n\t\t\t\t\tUnsafeRow row = iter.next();\n\t\t\t\t\tif(index >= start){\n\t\t\t\t\t\trow.packetId = ++packetId;\n\t\t\t\t\t\tbuffer = row.write(buffer,source,true);\n\t\t\t\t\t}\n\t\t\t\t\tindex++;\n\t\t\t\t\tif(index == end){\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\teof[3] = ++packetId;\n\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(\"last packet id:\" + packetId);\n\t\t\t}\n\t\t\t//huangyiming add  中间过程缓存起来,isMiddleResultDone是确保合并部分执行完成后才会执行secondExecute\n\t\t\tMiddlerResultHandler middlerResultHandler = source.getSession2().getMiddlerResultHandler();\n \t\t\tif(null != middlerResultHandler){\n \t\t\t\tif(buffer.position() > 0){\n \t\t\t\t\tbuffer.flip();\n \t                byte[] data = new byte[buffer.limit()];\n \t                buffer.get(data);\n \t                buffer.clear();\n \t                //如果该操作只是一个中间过程则把结果存储起来\n \t\t\t\t\t String str =  ResultSetUtil.getColumnValAsString(data, fields, 0);\n \t\t\t\t\t //真的需要数据合并的时候才合并\n \t\t\t\t\t if(rrs.isHasAggrColumn()){\n \t\t\t\t\t\t middlerResultHandler.getResult().clear();\n \t\t\t\t\t\t if(str !=null){\n  \t\t\t\t\t\t\t middlerResultHandler.add(str);\n \t\t\t\t\t\t }\n \t\t\t\t\t }\n \t\t\t\t}\n\t\t\t\tisMiddleResultDone.set(false);\n\t\t}else{\n\t\t\tByteBuffer byteBuffer = source.writeToBuffer(eof, buffer);\n\n\t\t\t/**\n\t\t\t * 真正的开始把Writer Buffer的数据写入到channel 中\n\t\t\t */\n\t\t\tif(source.canResponse()) {\n\t\t\t\tsource.write(byteBuffer);\n\t\t\t}\n\t\t\t\n\t\t}\n\n            session.getSource().getListener().fireEvent(SqlExecuteStage.END);\n\n \t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\thandleDataProcessException(e);\n\t\t} finally {\n            dataMergeSvr.clear();\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\tpublic void outputMergeResult(final ServerConnection source,\n\t\t\tfinal byte[] eof, List<RowDataPacket> results) {\n\t\ttry {\n\t\t\tlock.lock();\n\t\t\tByteBuffer buffer = session.getSource().allocate();\n\t\t\tfinal RouteResultset rrs = this.dataMergeSvr.getRrs();\n\n\t\t\t// 处理limit语句\n\t\t\tint start = rrs.getLimitStart();\n\t\t\tint end = start + rrs.getLimitSize();\n\n\t\t\tif (start < 0) {\n\t\t\t\tstart = 0;\n\t\t\t}\n\n\t\t\tif (rrs.getLimitSize() < 0) {\n\t\t\t\tend = results.size();\n\t\t\t}\n\n//\t\t\t// 对于不需要排序的语句,返回的数据只有rrs.getLimitSize()\n//\t\t\tif (rrs.getOrderByCols() == null) {\n//\t\t\t\tend = results.size();\n//\t\t\t\tstart = 0;\n//\t\t\t}\n\t\t\tif (end > results.size()) {\n\t\t\t\tend = results.size();\n\t\t\t}\n\n//\t\t\tfor (int i = start; i < end; i++) {\n//\t\t\t\tRowDataPacket row = results.get(i);\n//\t\t\t\tif( prepared ) {\n//\t\t\t\t\tBinaryRowDataPacket binRowDataPk = new BinaryRowDataPacket();\n//\t\t\t\t\tbinRowDataPk.read(fieldPackets, row);\n//\t\t\t\t\tbinRowDataPk.packetId = ++packetId;\n//\t\t\t\t\t//binRowDataPk.write(source);\n//\t\t\t\t\tbuffer = binRowDataPk.write(buffer, session.getSource(), true);\n//\t\t\t\t} else {\n//\t\t\t\t\trow.packetId = ++packetId;\n//\t\t\t\t\tbuffer = row.write(buffer, source, true);\n//\t\t\t\t}\n//\t\t\t}\n\n\t\t\tif(prepared) {\n\t\t\t\tfor (int i = start; i < end; i++) {\n\t\t\t\t\tRowDataPacket row = results.get(i);\n\t\t\t\t\tBinaryRowDataPacket binRowDataPk = new BinaryRowDataPacket();\n\t\t\t\t\tbinRowDataPk.read(fieldPackets, row);\n\t\t\t\t\tbinRowDataPk.packetId = ++packetId;\n\t\t\t\t\t//binRowDataPk.write(source);\n\t\t\t\t\tbuffer = binRowDataPk.write(buffer, session.getSource(), true);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (int i = start; i < end; i++) {\n\t\t\t\t\tRowDataPacket row = results.get(i);\n\t\t\t\t\trow.packetId = ++packetId;\n\t\t\t\t\tbuffer = row.write(buffer, source, true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\teof[3] = ++packetId;\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(\"last packet id:\" + packetId);\n\t\t\t}\n\t\t\tif(source.canResponse()) {\n\t\t\t\tsource.write(source.writeToBuffer(eof, buffer));\n\t\t\t}\n\n            session.getSource().getListener().fireEvent(SqlExecuteStage.END);\n\n\t\t} catch (Exception e) {\n\t\t\thandleDataProcessException(e);\n\t\t} finally {\n            dataMergeSvr.clear();\n            lock.unlock();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\t\t\n\t\t//10个连接有一个连接错误怎么办哦。\n\t\tif (errorRepsponsed.get()|| this.isFail()) {\n\t\t\t// the connection has been closed or set to \"txInterrupt\" properly\n\t\t\t//in tryErrorFinished() method! If we close it here, it can\n\t\t\t// lead to tx error such as blocking rollback tx for ever.\n\t\t\t// @author Uncle-pan\n\t\t\t// @since 2016-03-25\n\t\t\t// conn.close(this.error);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t//huangyiming add\n\t\tthis.header = header;\n\t\tthis.fields = fields;\n\t\tMiddlerResultHandler middlerResultHandler = session.getMiddlerResultHandler();\n        /*if(null !=middlerResultHandler ){\n\t\t\treturn;\n\t\t}*/\n\t\tthis.netOutBytes += header.length;\n\t\tthis.netOutBytes += eof.length;\n\t\tfor (int i = 0, len = fields.size(); i < len; ++i) {\n\t\t\tbyte[] field = fields.get(i);\n\t\t\tthis.netOutBytes += field.length;\n\t\t}\n\n\t\tServerConnection source = null;\n\n\t\tif (fieldsReturned) {\n\t\t\treturn;\n\t\t}\n\t\tlock.lock();\n\t\ttry {\n\t\t\tif (fieldsReturned) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfieldsReturned = true;\n\n\t\t\tboolean needMerg = (dataMergeSvr != null)\n\t\t\t\t\t&& dataMergeSvr.getRrs().needMerge();\n\t\t\tSet<String> shouldRemoveAvgField = new HashSet<>();\n\t\t\tSet<String> shouldRenameAvgField = new HashSet<>();\n\t\t\tif (needMerg) {\n\t\t\t\tMap<String, Integer> mergeColsMap = dataMergeSvr.getRrs()\n\t\t\t\t\t\t.getMergeCols();\n\t\t\t\tif (mergeColsMap != null) {\n\t\t\t\t\tfor (Map.Entry<String, Integer> entry : mergeColsMap\n\t\t\t\t\t\t\t.entrySet()) {\n\t\t\t\t\t\tString key = entry.getKey();\n\t\t\t\t\t\tint mergeType = entry.getValue();\n\t\t\t\t\t\tif (MergeCol.MERGE_AVG == mergeType\n\t\t\t\t\t\t\t\t&& mergeColsMap.containsKey(key + \"SUM\")) {\n\t\t\t\t\t\t\tshouldRemoveAvgField.add((key + \"COUNT\")\n\t\t\t\t\t\t\t\t\t.toUpperCase());\n\t\t\t\t\t\t\tshouldRenameAvgField.add((key + \"SUM\")\n\t\t\t\t\t\t\t\t\t.toUpperCase());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tsource = session.getSource();\n\t\t\tByteBuffer buffer = source.allocate();\n\t\t\tfieldCount = fields.size();\n\t\t\tif (shouldRemoveAvgField.size() > 0) {\n\t\t\t\tResultSetHeaderPacket packet = new ResultSetHeaderPacket();\n\t\t\t\tpacket.packetId = ++packetId;\n\t\t\t\tpacket.fieldCount = fieldCount - shouldRemoveAvgField.size();\n\t\t\t\tbuffer = packet.write(buffer, source, true);\n\t\t\t} else {\n\n\t\t\t\theader[3] = ++packetId;\n\t\t\t\tbuffer = source.writeToBuffer(header, buffer);\n\t\t\t}\n\n\t\t\tString primaryKey = null;\n\t\t\tif (rrs.hasPrimaryKeyToCache()) {\n\t\t\t\tString[] items = rrs.getPrimaryKeyItems();\n\t\t\t\tpriamaryKeyTable = items[0];\n\t\t\t\tprimaryKey = items[1];\n\t\t\t}\n\n\t\t\tMap<String, ColMeta> columToIndx = new HashMap<String, ColMeta>(\n\t\t\t\t\tfieldCount);\n\n\t\t\tfor (int i = 0, len = fieldCount; i < len; ++i) {\n\t\t\t\tboolean shouldSkip = false;\n\t\t\t\tbyte[] field = fields.get(i);\n\t\t\t\tif (needMerg) {\n\t\t\t\t\tFieldPacket fieldPkg = new FieldPacket();\n\t\t\t\t\tfieldPkg.read(field);\n\t\t\t\t\tfieldPackets.add(fieldPkg);\n                    String charset = session.getSource().getCharset();// CharsetUtil.getCharset(fieldPkg.charsetIndex);\n                    String fieldName = new String(fieldPkg.name, charset).toUpperCase();\n\t\t\t\t\tif (columToIndx != null\n\t\t\t\t\t\t\t&& !columToIndx.containsKey(fieldName)) {\n\t\t\t\t\t\tif (shouldRemoveAvgField.contains(fieldName)) {\n\t\t\t\t\t\t\tshouldSkip = true;\n\t\t\t\t\t\t\tfieldPackets.remove(fieldPackets.size() - 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (shouldRenameAvgField.contains(fieldName)) {\n\t\t\t\t\t\t\tString newFieldName = fieldName.substring(0,\n\t\t\t\t\t\t\t\t\tfieldName.length() - 3);\n\t\t\t\t\t\t\tfieldPkg.name = newFieldName.getBytes();\n\t\t\t\t\t\t\tfieldPkg.packetId = ++packetId;\n\t\t\t\t\t\t\tshouldSkip = true;\n\t\t\t\t\t\t\t// 处理AVG字段位数和精度, AVG位数 = SUM位数 - 14\n\t\t\t\t\t\t\tfieldPkg.length = fieldPkg.length - 14;\n\t\t\t\t\t\t\t// AVG精度 = SUM精度 + 4\n \t\t\t\t\t\t\tfieldPkg.decimals = (byte) (fieldPkg.decimals + 4);\n\t\t\t\t\t\t\tbuffer = fieldPkg.write(buffer, source, false);\n\n\t\t\t\t\t\t\t// 还原精度\n\t\t\t\t\t\t\tfieldPkg.decimals = (byte) (fieldPkg.decimals - 4);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tColMeta colMeta = new ColMeta(i, fieldPkg.type);\n\t\t\t\t\t\tcolMeta.decimals = fieldPkg.decimals;\n\t\t\t\t\t\tcolumToIndx.put(fieldName, colMeta);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tFieldPacket fieldPkg = new FieldPacket();\n\t\t\t\t\tfieldPkg.read(field);\n\t\t\t\t\tfieldPackets.add(fieldPkg);\n\t\t\t\t\tfieldCount = fields.size();\n\t\t\t\t\tif (primaryKey != null && primaryKeyIndex == -1) {\n\t\t\t\t\t// find primary key index\n\t\t\t\t\tString fieldName = new String(fieldPkg.name);\n\t\t\t\t\tif (primaryKey.equalsIgnoreCase(fieldName)) {\n\t\t\t\t\t\tprimaryKeyIndex = i;\n\t\t\t\t\t}\n\t\t\t\t}   }\n\t\t\t\tif (!shouldSkip) {\n\t\t\t\t\tfield[3] = ++packetId;\n\t\t\t\t\tbuffer = source.writeToBuffer(field, buffer);\n\t\t\t\t}\n\t\t\t}\n\t\t\teof[3] = ++packetId;\n\t\t\tbuffer = source.writeToBuffer(eof, buffer);\n\n\t\t\tif (null == middlerResultHandler) {\n\t\t\t\t//session.getSource().write(row);\n\t\t\t\tsource.write(buffer);\n\t\t     }\n\n \t\t\tif (dataMergeSvr != null) {\n\t\t\t\tdataMergeSvr.onRowMetaData(columToIndx, fieldCount);\n\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\thandleDataProcessException(e);\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\n\tpublic void handleDataProcessException(Exception e) {\n\t\tif (!errorRepsponsed.get()) {\n\t\t\tthis.error = e.toString();\n\t\t\tLOGGER.warn(this.toString() +\" caught exception \", e);\n\t\t\tsetFail(e.toString());\n\t\t\t//判断是否全部返回\n\t\t\tboolean finished = false;\n\t\t\tlock.lock();\n\t\t\ttry {\n\t\t\t\tfinished = (this.nodeCount == 0);\n\n\t\t\t} finally {\n\t\t\t\tlock.unlock();\n\t\t\t}\n\t\t\tthis.tryErrorFinished(finished);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void rowResponse(final byte[] row, final BackendConnection conn) {\n\n \t\tif (errorRepsponsed.get()||this.isFail()) {\n\t\t\t// the connection has been closed or set to \"txInterrupt\" properly\n\t\t\t//in tryErrorFinished() method! If we close it here, it can\n\t\t\t// lead to tx error such as blocking rollback tx for ever.\n\t\t\t// @author Uncle-pan\n\t\t\t// @since 2016-03-25\n\t\t\t//conn.close(error);\n\t\t\treturn;\n\t\t}\n\n\n\t\tlock.lock();\n\t\ttry {\n\n\t\t\tthis.selectRows++;\n\n\t\t\tRouteResultsetNode rNode = (RouteResultsetNode) conn.getAttachment();\n\t\t\tString dataNode = rNode.getName();\n\t\t\tif (dataMergeSvr != null) {\n\t\t\t\t// even through discarding the all rest data, we can't\n\t\t\t\t//close the connection for tx control such as rollback or commit.\n\t\t\t\t// So the \"isClosedByDiscard\" variable is unnecessary.\n\t\t\t\t// @author Uncle-pan\n\t\t\t\t// @since 2016-03-25\n\t\t\t\t\tdataMergeSvr.onNewRecord(dataNode, row);\n\n\t\t\t\tMiddlerResultHandler middlerResultHandler = session.getMiddlerResultHandler();\n \t\t\t\tif(null != middlerResultHandler ){\n \t\t\t\t\t if(middlerResultHandler instanceof MiddlerQueryResultHandler){\n \t\t\t\t\t\t byte[] rv = ResultSetUtil.getColumnVal(row, fields, 0);\n\t\t\t\t\t\t String rowValue =  rv==null? \"\":new String(rv);\n\t\t\t\t\t\t middlerResultHandler.add(rowValue);\n \t\t\t\t\t }\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trow[3] = ++packetId;\n\t\t\t\tRowDataPacket rowDataPkg =null;\n\t\t\t\t// cache primaryKey-> dataNode\n\t\t\t\tif (primaryKeyIndex != -1) {\n\t\t\t\t\t rowDataPkg = new RowDataPacket(fieldCount);\n\t\t\t\t\trowDataPkg.read(row);\n\t\t\t\t\tString primaryKey = new String(rowDataPkg.fieldValues.get(primaryKeyIndex));\n\t\t\t\t\tLayerCachePool pool = MycatServer.getInstance().getRouterservice().getTableId2DataNodeCache();\n\t\t\t\t\tif (priamaryKeyTable != null){\n\t\t\t\t\t\tpool.putIfAbsent(priamaryKeyTable.toUpperCase(), primaryKey, dataNode);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif( prepared ) {\n\t\t\t\t\tif(rowDataPkg==null) {\n\t\t\t\t\t\trowDataPkg = new RowDataPacket(fieldCount);\n\t\t\t\t\t\trowDataPkg.read(row);\n\t\t\t\t\t}\n\t\t\t\t\tBinaryRowDataPacket binRowDataPk = new BinaryRowDataPacket();\n\t\t\t\t\tbinRowDataPk.read(fieldPackets, rowDataPkg);\n\t\t\t\t\tbinRowDataPk.write(session.getSource());\n\t\t\t\t} else {\n\t\t\t\t\t//add huangyiming\n\t\t\t\t\tMiddlerResultHandler middlerResultHandler = session.getMiddlerResultHandler();\n\t\t\t\t\tif(null == middlerResultHandler ){\n \t\t\t\t\t\tsession.getSource().write(row);\n\t\t\t\t\t}else{\n\n\t\t\t\t\t\t if(middlerResultHandler instanceof MiddlerQueryResultHandler){\n\t\t\t\t\t\t\t String rowValue =  ResultSetUtil.getColumnValAsString(row, fields, 0);\n\t\t\t\t\t\t\t middlerResultHandler.add(rowValue);\n \t\t\t\t\t\t }\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\thandleDataProcessException(e);\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void clearResources() {\n        lock.lock();\n        try {\n            if (dataMergeSvr != null) {\n                dataMergeSvr.clear();\n            }\n        } finally {\n            lock.unlock();\n        }\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\t}\n\n\t@Override\n\tpublic void requestDataResponse(byte[] data, BackendConnection conn) {\n\t\tLoadDataUtil.requestFileDataResponse(data, conn);\n\t}\n\n\tpublic boolean isPrepared() {\n\t\treturn prepared;\n\t}\n\n\tpublic void setPrepared(boolean prepared) {\n\t\tthis.prepared = prepared;\n\t}\n\n    /**\n        * 最后一个OK/ERROR包返回后的后续流程： \n        *   1）如果是隐式事务，需要自动提交\n        *   2）显示事务，返回OK/ERROR给客户端，需要后面继续执行rollback或commit;\n        *   3）非事务：返回OK/ERROR给客户端\n        \n        * @param data\n        * @param isCommit  是否提交事务\n        * @param conn\n        */\n    protected void processFinishWork(byte[] data, boolean isCommit, BackendConnection conn) {\n        ServerConnection source = session.getSource();\n        source.getListener().fireEvent(SqlExecuteStage.END);\n        \n\t\tboolean isImplicitTransaction = false;\n\t\tif (source.isAutocommit()) {\n\t\t\tfor (Entry<RouteResultsetNode, BackendConnection> entry : session.getTargetMap().entrySet()) {\n\t\t\t\tBackendConnection c = entry.getValue();\n\t\t\t\tif (c.isModifiedSQLExecuted() && !c.isAutocommit()) {\n\t\t\t\t\tisImplicitTransaction = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n        \n        if (isImplicitTransaction) {\n            // 1隐式事务:修改类语句并且autocommit=true，mycat自动开启事务，需要自动提交掉\n            if (nodeCount < 0) {\n                return;\n            }\n\n            // Implicit Distributed Transaction,send commit or rollback automatically\n            if (isCommit) {\n                // data 包里面带有更新或改变了多少行数据的信息，需要在commit后发送给client\n                CommitNodeHandler commitHandler = new CommitNodeHandler(session, data);\n                commitHandler.commit();\n            } else {\n                RollbackNodeHandler rollbackHandler = new RollbackNodeHandler(session, data);\n                rollbackHandler.rollback();\n            }\n        } else {\n            // 2 非事务或者显式事务，其中显式事务需要 等待后续的commit或rollback\n            boolean inTransaction = !source.isAutocommit();\n            if (!inTransaction) {\n                // 释放可能没有被释放的连接session.target\n                for (BackendConnection errConn : errConnections) {\n                    errConn.setResponseHandler(null);\n                    session.closeConnection(errConn, \"Connection happening error  can't been reused,close directly\");\n                }\n            }\n\n            if (nodeCount == 0) {\n                // 事务下如果已经出错设置中断\n                if (inTransaction && !isCommit) {\n                    source.setTxInterrupt(\"ROLLBACK\");\n                }\n                // 发送语句执行结果给client\n                source.write(data);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/NewConnectionRespHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.BackendConnection;\n\npublic class NewConnectionRespHandler implements ResponseHandler{\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(NewConnectionRespHandler.class);\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\t\tLOGGER.warn(conn+\" connectionError \"+e);\n\t\t\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\t\t//\n\t\tLOGGER.info(\"connectionAcquired \"+conn);\n\t\t\n\t\tconn.release(); //  NewConnectionRespHandler 因为这个是由于空闲连接数低于配置，需要新建连接，但再新建连接的时候，\n\t\t\n\t}\n\n\t@Override\n\tpublic void errorResponse(byte[] err, BackendConnection conn) {\n\t\tLOGGER.warn(\"caught error resp: \" + conn + \" \" + new String(err));\n\t\tconn.release();\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] ok, BackendConnection conn) {\n\t\tLOGGER.info(\"okResponse: \" + conn );\n\t\tconn.release();\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\t\tLOGGER.info(\"fieldEofResponse: \" + conn );\n\t\t\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t\tLOGGER.info(\"rowResponse: \" + conn );\n\t\t\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t\tLOGGER.info(\"rowEofResponse: \" + conn );\n\t\tconn.release();\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\t\t\n\t\t\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/PrepareRequestHandler.java",
    "content": "package io.mycat.backend.mysql.nio.handler;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement;\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;\r\nimport com.alibaba.druid.sql.parser.SQLParserUtils;\r\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\r\nimport com.alibaba.druid.util.JdbcUtils;\r\n\r\nimport io.mycat.MycatServer;\r\nimport io.mycat.backend.BackendConnection;\r\nimport io.mycat.backend.datasource.PhysicalDBNode;\r\nimport io.mycat.backend.datasource.PhysicalDatasource;\r\nimport io.mycat.backend.mysql.nio.MySQLConnection;\r\nimport io.mycat.config.model.SchemaConfig;\r\nimport io.mycat.config.model.TableConfig;\r\nimport io.mycat.net.mysql.CommandPacket;\r\nimport io.mycat.net.mysql.EOFPacket;\r\nimport io.mycat.net.mysql.ErrorPacket;\r\nimport io.mycat.net.mysql.FieldPacket;\r\nimport io.mycat.net.mysql.MySQLPacket;\r\nimport io.mycat.net.mysql.PreparedOkPacket;\r\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\r\nimport io.mycat.server.ServerConnection;\r\nimport io.mycat.util.StringUtil;\r\n\r\n/**\r\n * 用于取prepare语句的原数据，直接把语句透传给Mysql\r\n * https://dev.mysql.com/doc/internals/en/com-stmt-prepare-response.html#packet-COM_STMT_PREPARE_OK\r\n * \r\n * 约束： 不支持库内分表等需要改写表名的语句\r\n * \r\n * @author funnyAnt\r\n *\r\n */\r\npublic class PrepareRequestHandler implements ResponseHandler {\r\n    private static final Logger LOGGER = LoggerFactory.getLogger(PrepareRequestHandler.class);\r\n\r\n    private String sql;\r\n    private ServerConnection sc;\r\n    private PrepareRequestCallback callbackHandler;\r\n    private volatile long statementId = -1L;\r\n    private List<FieldPacket> params;\r\n    private List<FieldPacket> fields;\r\n    private volatile boolean statementClosed;\r\n    private int paramSize;\r\n    private int fieldSize;\r\n    private volatile boolean lastPacket;\r\n    private static String SYNC_CHANNEL_STATUS_SQL = \"SELECT 1\";// 用于同步通道的状态信息\r\n\r\n    public PrepareRequestHandler(ServerConnection sc, String sql, PrepareRequestCallback callbackHandler) {\r\n        this.sc = sc;\r\n        this.sql = sql;\r\n        this.callbackHandler = callbackHandler;\r\n        this.params = new ArrayList<>(8);\r\n        this.fields = new ArrayList<>(8);\r\n    }\r\n\r\n    public void execute() {\r\n        try {\r\n            List<String> tables = parseTables(sql);\r\n            SchemaConfig schemaConfig = MycatServer.getInstance().getConfig().getSchemas().get(sc.getSchema());\r\n            // 根据表的规则确定路由节点，路由判断规则时剔除全局表后，选择剩余第一个表的第一个节点\r\n            String routeDataNode = null;\r\n            for (String table : tables) {\r\n                TableConfig tc = schemaConfig.getTables().get(table.toUpperCase());\r\n                if (tc != null) {\r\n                    routeDataNode = tc.getDataNodes().get(0);\r\n                } else {\r\n                    routeDataNode = schemaConfig.getDataNode();\r\n                }\r\n\r\n                if (tc != null && !tc.isGlobalTable()) {\r\n                    break;\r\n                }\r\n            }\r\n            if (routeDataNode == null) {\r\n                doFinished(true, \"can't find route node\");\r\n                return;\r\n            }\r\n\r\n            PhysicalDBNode dn = MycatServer.getInstance().getConfig().getDataNodes().get(routeDataNode);\r\n            PhysicalDatasource ds = dn.getDbPool().getSource();\r\n            if (ds.getConfig().getDbType().equalsIgnoreCase(\"MYSQL\")) {\r\n                ds.getConnection(dn.getDatabase(), true, this, null);\r\n            } else {\r\n                doFinished(true, \"Does not support getting metadata from non-mysql database\");\r\n            }\r\n        } catch (Exception e) {\r\n            LOGGER.info(\"can't get connection for sql ,error:\", e);\r\n            doFinished(true, e.getMessage());\r\n        }\r\n    }\r\n\r\n    private void doFinished(boolean failed, String errorMsg) {\r\n        if (!lastPacket) {\r\n            lastPacket = true;\r\n            callbackHandler.callback(!failed, errorMsg, params.toArray(new FieldPacket[0]),\r\n                    fields.toArray(new FieldPacket[0]));\r\n        }\r\n    }\r\n\r\n    private void closePrepareStmt(BackendConnection conn) {\r\n        //https://dev.mysql.com/doc/internals/en/com-stmt-close.html\r\n        if (!statementClosed) {\r\n            // PreparedClosePacket packet = new PreparedClosePacket(this.statementId);\r\n            CommandPacket closePreparePacket = new CommandPacket();\r\n            closePreparePacket.command = MySQLPacket.COM_STMT_CLOSE;\r\n            closePreparePacket.arg = String.valueOf(this.statementId).getBytes();\r\n            statementClosed = true;\r\n            closePreparePacket.write((MySQLConnection) conn);\r\n        }\r\n\r\n    }\r\n    /**\r\n     * 从sql 语句中解析出tableName\r\n     * \r\n     * @return\r\n     */\r\n    private List<String> parseTables(String sql) {\r\n        List<String> tables = new ArrayList<>();\r\n        try {\r\n            SQLStatementParser sqlStatementParser = SQLParserUtils.createSQLStatementParser(sql, JdbcUtils.MYSQL);\r\n            SQLStatement statement = sqlStatementParser.parseStatement();\r\n            if (statement instanceof MySqlUpdateStatement){\r\n                String simpleName = ((MySqlUpdateStatement) statement).getTableName().getSimpleName();\r\n                tables.add(StringUtil.removeBackquote(simpleName));\r\n            }else if (statement instanceof MySqlDeleteStatement){\r\n                String simpleName = ((MySqlDeleteStatement) statement).getTableName().getSimpleName();\r\n                tables.add(StringUtil.removeBackquote(simpleName));\r\n            }else if (statement instanceof MySqlInsertStatement){\r\n                String simpleName = ((MySqlInsertStatement) statement).getTableName().getSimpleName();\r\n                tables.add(StringUtil.removeBackquote(simpleName));\r\n            } else {\r\n                MycatSchemaStatVisitor visitor = new MycatSchemaStatVisitor();\r\n                statement.accept(visitor);\r\n                tables.addAll(visitor.getAliasMap().values());\r\n            }\r\n        } catch (Exception  e) {\r\n            LOGGER.error(\"can not get column count\", e);\r\n        }\r\n        return tables;\r\n    }\r\n\r\n    private void sendPrepareRequestCommand(BackendConnection conn) {\r\n        try {\r\n            CommandPacket preparePacket = new CommandPacket();\r\n            preparePacket.command = MySQLPacket.COM_STMT_PREPARE;\r\n            preparePacket.arg = this.sql.getBytes(sc.getCharset());\r\n            preparePacket.write((MySQLConnection) conn);\r\n        } catch (Exception e) {\r\n            doFinished(true, e.getMessage());\r\n        }\r\n    }\r\n\r\n    public boolean isLastPacket() {\r\n        return lastPacket;\r\n    }\r\n\r\n    @Override\r\n    public void connectionError(Throwable e, BackendConnection conn) {\r\n        // TODO Auto-generated method stub\r\n        LOGGER.info(\"can't get connection for sql :\" + sql);\r\n        doFinished(true, e.getMessage());\r\n    }\r\n\r\n    @Override\r\n    public void connectionAcquired(BackendConnection conn) {\r\n        // TODO Auto-generated method stub\r\n        if (LOGGER.isDebugEnabled()) {\r\n            LOGGER.debug(\"con query sql:\" + sql + \" to con:\" + conn);\r\n        }\r\n        conn.setResponseHandler(this);\r\n\r\n        try {\r\n            if (((MySQLConnection) conn).isNeedSyncSchema()) {\r\n                // 发送一个select 1语句触发同步schema；后面在rowEofResponse里面发送PrepareRequestCommand命令\r\n                conn.query(SYNC_CHANNEL_STATUS_SQL);\r\n            }else {\r\n                this.sendPrepareRequestCommand(conn);\r\n            }\r\n            \r\n        } catch (Exception e) {// (UnsupportedEncodingException e) {\r\n            doFinished(true, e.getMessage());\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void errorResponse(byte[] err, BackendConnection conn) {\r\n        ErrorPacket errPg = new ErrorPacket();\r\n        errPg.read(err);\r\n\r\n        String errMsg = \"error response errno:\" + errPg.errno + \", \" + new String(errPg.message) + \" from of sql :\"\r\n                + sql + \" at con:\" + conn;\r\n\r\n        // @see https://dev.mysql.com/doc/refman/5.6/en/error-messages-server.html\r\n        // ER_SPECIFIC_ACCESS_DENIED_ERROR\r\n        if (errPg.errno == 1227) {\r\n            LOGGER.warn(errMsg);\r\n\r\n        } else {\r\n            LOGGER.info(errMsg);\r\n        }\r\n\r\n        doFinished(true, errMsg);\r\n        // closePrepareStmt(conn);\r\n        conn.release();\r\n\r\n    }\r\n\r\n    @Override\r\n    public void okResponse(byte[] data, BackendConnection conn) {\r\n         // TODO Auto-generated method stub\r\n        if(statementId == -1L) {\r\n            boolean executeResponse = conn.syncAndExcute();\r\n            if (executeResponse) {\r\n                // 解析包\r\n                PreparedOkPacket preparedOkPacket = new PreparedOkPacket();\r\n                preparedOkPacket.read(data);\r\n                this.paramSize = preparedOkPacket.parametersNumber;\r\n                this.fieldSize = preparedOkPacket.columnsNumber;\r\n                this.statementId = preparedOkPacket.statementId;\r\n          }\r\n        }else {\r\n            switch (data[4]) {\r\n            case ErrorPacket.FIELD_COUNT:\r\n                errorResponse(data, conn);\r\n                break;\r\n            case EOFPacket.FIELD_COUNT:\r\n                if (fields.size() == fieldSize) {\r\n                    // field包后面的EOF包,整个协议结束。\r\n                    doFinished(false, null);\r\n                    closePrepareStmt(conn);\r\n                    conn.release();\r\n                }\r\n                break;\r\n            default:\r\n                // 解析field\r\n                FieldPacket packet = new FieldPacket();\r\n                packet.read(data);\r\n                // 数据库名称修改为mycat逻辑库\r\n                packet.db = this.sc.getSchema().getBytes();\r\n                packet.length = packet.calcPacketSize();\r\n\r\n                if (params.size() < paramSize) {\r\n                    params.add(packet);\r\n                } else {\r\n                    if (fields.size() < fieldSize) {\r\n                        fields.add(packet);\r\n                    }\r\n                }\r\n\r\n            }\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void fieldEofResponse(byte[] header, List<byte[]> fields, byte[] eof, BackendConnection conn) {\r\n        // TODO Auto-generated method stub\r\n\r\n    }\r\n\r\n    @Override\r\n    public void rowResponse(byte[] row, BackendConnection conn) {\r\n        // TODO Auto-generated method stub\r\n    }\r\n\r\n    @Override\r\n    public void rowEofResponse(byte[] eof, BackendConnection conn) {\r\n        // TODO Auto-generated method stub\r\n        // 说明同步通道的语句SELECT 1已经成功，发送真正的prepare包。这样做的原因是包类型不一样，目前接口无法做到一次性发送\r\n        this.sendPrepareRequestCommand(conn);\r\n    }\r\n\r\n    @Override\r\n    public void writeQueueAvailable() {\r\n        // TODO Auto-generated method stub\r\n\r\n    }\r\n\r\n    @Override\r\n    public void connectionClose(BackendConnection conn, String reason) {\r\n        // TODO Auto-generated method stub\r\n        doFinished(true, reason);\r\n    }\r\n\r\n    /**\r\n     * \r\n     * 用于回调返回PrepareRequest值\r\n     *\r\n     */\r\n    public interface PrepareRequestCallback {\r\n        void callback(boolean success, String msg, FieldPacket[] params, FieldPacket[] fields);\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/ResponseHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\n\nimport io.mycat.backend.BackendConnection;\n\n/**\n * @author mycat\n * @author mycat\n */\npublic interface ResponseHandler {\n\n\t/**\n\t * 无法获取连接\n\t * \n\t * @param e\n\t * @param conn\n\t */\n\tpublic void connectionError(Throwable e, BackendConnection conn);\n\n\t/**\n\t * 已获得有效连接的响应处理\n\t */\n\tvoid connectionAcquired(BackendConnection conn);\n\n\t/**\n\t * 收到错误数据包的响应处理\n\t */\n\tvoid errorResponse(byte[] err, BackendConnection conn);\n\n\t/**\n\t * 收到OK数据包的响应处理\n\t */\n\tvoid okResponse(byte[] ok, BackendConnection conn);\n\n\t/**\n\t * 收到字段数据包结束的响应处理\n\t */\n\tvoid fieldEofResponse(byte[] header, List<byte[]> fields, byte[] eof,\n\t\t\tBackendConnection conn);\n\n\t/**\n\t * 收到行数据包的响应处理\n\t */\n\tvoid rowResponse(byte[] row, BackendConnection conn);\n\n\t/**\n\t * 收到行数据包结束的响应处理\n\t */\n\tvoid rowEofResponse(byte[] eof, BackendConnection conn);\n\n\t/**\n\t * 写队列为空，可以写数据了\n\t * \n\t */\n\tvoid writeQueueAvailable();\n\n\t/**\n\t * on connetion close event\n\t */\n\tvoid connectionClose(BackendConnection conn, String reason);\n\n\t\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/RollbackNodeHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.mysql.nio.MySQLConnection;\nimport io.mycat.backend.mysql.xa.CoordinatorLogEntry;\nimport io.mycat.backend.mysql.xa.TxState;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.NonBlockingSession;\n\n/**\n * @author mycat\n */\npublic class RollbackNodeHandler extends MultiNodeHandler {\n    private static final Logger LOGGER = LoggerFactory.getLogger(RollbackNodeHandler.class);\n    protected byte[] responseData;\n\n\tpublic RollbackNodeHandler(NonBlockingSession session) {\n\t\tsuper(session);\n\t}\n\n    public RollbackNodeHandler(NonBlockingSession session, byte[] responseData) {\n        super(session);\n        this.responseData = responseData;\n    }\n\n\tpublic void rollback() {\n\t\tfinal int initCount = session.getTargetCount();\n\t\tlock.lock();\n\t\ttry {\n\t\t\treset(initCount);\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t\tif (session.closed()) {\n\t\t\tdecrementCountToZero();\n\t\t\treturn;\n\t\t}\n\t\tint start = 0 ;\n\t\tboolean hasClose = false;\n\t\tfor (final RouteResultsetNode node : session.getTargetKeys()) {\n\t\t\tif (node == null) {\n\t\t\t\tLOGGER.error(\"null is contained in RoutResultsetNodes, source = \"\n\t\t\t\t\t\t+ session.getSource());\n\t\t\t\thasClose = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tfinal BackendConnection conn = session.getTarget(node);\n\t\t\tif (conn != null) {\n\t\t\t\tboolean isClosed=conn.isClosedOrQuit();\n\t\t\t\tif(isClosed) {\n\t\t\t\t\thasClose = true;\n//\t\t\t\t\tbreak;\n\t\t\t\t} else {\n\t\t\t\t\tstart ++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//有连接已被关闭 直接关闭所有的连接， 非xa模式下 ，xa prepare状态下需要rollback\n\t\tif(hasClose && session.getXaTXID() == null) {\n\t\t\tLOGGER.warn(\"find close back conn close ,so close all back connection\"\n\t\t\t\t\t+ session.getSource());\n\t\t\tsession.setAutoCommitStatus(); //一定是先恢复状态 在写消息\n\t\t\tthis.setFail(\"receive rollback,but find backend con is closed or quit\");\n\t\t\tthis.tryErrorFinished(true);\n\n\t\t\treturn ;\n\t\t}\n\t\t\n\t\t// 执行\n\t\t//modify by zwy\n\t\t\n\t\t// 执行\n\t\tlock.lock();\n\t\ttry {\n\t\t\treset(start);\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t\tboolean writeCheckPoint = false;\n\t\tfor (final RouteResultsetNode node : session.getTargetKeys()) {\n\t\t\tif (node == null) {\n\t\t\t\t\tLOGGER.error(\"null is contained in RoutResultsetNodes, source = \"\n\t\t\t\t\t\t\t+ session.getSource());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfinal BackendConnection conn = session.getTarget(node);\n\n\t\t\tif (conn != null) {\n\t\t\t\tboolean isClosed=conn.isClosedOrQuit();\n\t\t\t\t    if(isClosed)\n\t\t\t\t\t{\t\t\t\t    \t\n\t\t\t\t    \tthis.setFail(\"receive rollback,but find backend con is closed or quit\");\n\t\t\t\t\t//\tsession.getSource().writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR,\n\t\t\t\t\t//\t\t\t\"receive rollback,but find backend con is closed or quit\");\n\t\t\t\t\t\tLOGGER.error( conn+\"receive rollback,but fond backend con is closed or quit\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\t\tLOGGER.debug(\"rollback job run for \" + conn);\n\t\t\t\t}\n\t\t\t\tif (clearIfSessionClosed(session)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconn.setResponseHandler(RollbackNodeHandler.this);\n\n\t\t\t\t//support the XA rollback \n\t\t\t\t//to do to write xa recover log and judge xa statue to judge if send xa end \n\t\t\t\tif(session.getXaTXID()!=null && conn instanceof  MySQLConnection) {\n\t\t\t\t\tMySQLConnection mysqlCon = (MySQLConnection) conn;\n\t\t\t\t\t\n\t\t\t\t\t//recovery log\n\t\t\t\t\tString xaTxId = session.getXaTXID();\n\t\t\t\t\tCoordinatorLogEntry coordinatorLogEntry = MultiNodeCoordinator.inMemoryRepository.get(xaTxId);\n\t\t\t\t\tif(coordinatorLogEntry != null) {\n\t\t\t\t\t\twriteCheckPoint = true;\n\t\t\t\t\t\t//已经prepare的修改recover log\n\t\t\t\t\t\tfor(int i=0; i<coordinatorLogEntry.participants.length;i++){\n\t\t\t\t\t\t\tif(coordinatorLogEntry.participants[i].resourceName.equals(conn.getSchema())){\n\t\t\t\t\t\t\t\tcoordinatorLogEntry.participants[i].txState = TxState.TX_ROLLBACKED_STATE;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tMultiNodeCoordinator.inMemoryRepository.put(xaTxId,coordinatorLogEntry);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\txaTxId = session.getXaTXID() +\",'\"+ mysqlCon.getSchema()+\"'\";\n\t\t\t\t\t//exeBatch cmd issue : the 2nd package can not receive the response\n//\t\t\t\t\tmysqlCon.execCmd(\"XA END \" + xaTxId  + \";\");\n//\t\t\t\t\tmysqlCon.execCmd(\"XA ROLLBACK \" + xaTxId + \";\");\t\t\n\t\t\t\t\tif(mysqlCon.getXaStatus() == TxState.TX_STARTED_STATE ){\n\t\t\t\t\t\t String[] cmds = new String[]{\"XA END \" + xaTxId  + \"\",\n\t\t\t\t\t\t\t\t \" XA ROLLBACK \" + xaTxId + \";\"};\n\t\t\t\t\t\t   mysqlCon.execBatchCmd(cmds);\n\t\t\t\t\t} else if(mysqlCon.getXaStatus() == TxState.TX_PREPARED_STATE ){\t\t\t\t\t\t\n\t\t\t\t\t\t String[] cmds = new String[]{\" XA ROLLBACK \" + xaTxId + \";\"};\t\t\t\t\t\t   \n\t\t\t\t\t\t mysqlCon.execBatchCmd(cmds);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tLOGGER.warn(\"{} xaStat is {} ,to rollback is error\" ,mysqlCon, mysqlCon.getXaStatus());\n\t\t\t\t\t}\t\t\t\n\t\t\t\t\tmysqlCon.setXaStatus(TxState.TX_ROLLBACKED_STATE);\n\t\t\t\t}else {\n\t\t\t\t\t//modify by zwy\n\t\t\t\t\tif(!conn.isClosedOrQuit()) {\n\t\t\t\t\t\tconn.rollback();\n\t\t\t\t\t\t//++started;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif(writeCheckPoint) {\n\t\t\tMultiNodeCoordinator.fileRepository.writeCheckpoint(session.getXaTXID(), MultiNodeCoordinator.inMemoryRepository.getAllCoordinatorLogEntries());\n\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] ok, BackendConnection conn) {\n\t\t\n\t\tif(session.getXaTXID()!=null) {\n\t\t\t//xa \n\t\t\tif( conn instanceof  MySQLConnection) {\n\t\t\t\tMySQLConnection mysqlCon = (MySQLConnection) conn;\n\t\t\t\tif (!mysqlCon.batchCmdFinished()) { \n\t\t\t\t\t// \n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\t\t\t\n\t\t}\t\t\n\t\tif (decrementCountBy(1)) {\n\t\t\t// clear all resources\n\t\t\tsession.clearResources(false);\n\t\t\t//回复之前的事务状态 by zhangwy 2018.07\n\t\t\tsession.setAutoCommitStatus();\n\t\t\tif (this.isFail() || session.closed()) {\n\t\t\t\ttryErrorFinished(true);\n\t\t\t} else {\n\t\t        if(session.getSource().canResponse()) {\n                    if (responseData != null) {\n                        session.getSource().write(responseData);\n                    } else {\n                        OkPacket okPacket = new OkPacket();\n                        okPacket.read(ok);\n                        okPacket.packetId = 1;\n                        session.getSource().write(okPacket.writeToBytes());\n                    }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t\tLOGGER.error(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": field's eof\").toString());\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\t\tLOGGER.error(\"unexpected invocation: connectionAcquired from rollback\");\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\t\tLOGGER.error(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": field's eof\").toString());\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t\tLOGGER.error(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": field's eof\").toString());\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\n\t}\n\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\t\tthis.setFail(\"closed connection:\" + reason + \" con:\" + conn);\n\t\tboolean finished = false;\n\n\t\tif (finished == false) {\n\t\t\tfinished = this.decrementCountBy(1);\n\t\t}\n\t\tif (error == null) {\n\t\t\terror = \"back connection closed \";\n\t\t}\n\t\tif(finished) {\n\t\t\tsession.setAutoCommitStatus();\n\t\t\ttryErrorFinished(finished);\n\n\t\t}\n\t}\n\tprotected void tryErrorFinished(boolean allEnd) {\n        if (allEnd && !session.closed()) {\n\n            // clear session resources,release all\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"error all end ,clear session resource \");\n            }\n            // 关闭所有的错误后端连接 清理资源\n            session.closeAndClearResources(error);\n            // 避免高并发 重新在清空一次\n            session.getSource().clearTxInterrupt();\n            // createErrPkg(this.error).write(session.getSource());\n            if (responseData != null) {\n                LOGGER.error(this.error);\n                session.getSource().write(responseData);\n            } else\n                session.getSource().writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR, this.error);\n            }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/RollbackReleaseHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.BackendConnection;\n\n/**\n * @author mycat\n */\npublic class RollbackReleaseHandler implements ResponseHandler {\n\tprivate static final Logger logger = LoggerFactory\n\t\t\t.getLogger(RollbackReleaseHandler.class);\n\n\tpublic RollbackReleaseHandler() {\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\t\tlogger.error(\"unexpected invocation: connectionAcquired from rollback-release\");\n\t}\n\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\n\t}\n\n\t@Override\n\tpublic void errorResponse(byte[] err, BackendConnection conn) {\n\t\tconn.quit();\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] ok, BackendConnection conn) {\n\t\tlogger.debug(\"autocomit is false,but no commit or rollback ,so mycat rollbacked backend conn \"+conn);\n\t\tconn.release();\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/SecondHandler.java",
    "content": "package io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\n\n/**\n * 查询分解后的第二部处理\n * @author huangyiming\n *\n */\npublic interface SecondHandler {\n\t\n\tpublic void doExecute(List params);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/SecondQueryHandler.java",
    "content": "package io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\n\npublic class SecondQueryHandler implements SecondHandler {\n\n\tpublic MiddlerResultHandler middlerResultHandler;\n\tpublic SecondQueryHandler(MiddlerResultHandler middlerResultHandler){\n\t\tthis.middlerResultHandler =  middlerResultHandler;\n\t}\n\n\t@Override\n\tpublic void doExecute(List params) {\n\t\t// TODO Auto-generated method stub\n\t\t\n\t}\n\t \n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/SimpleLogHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.BackendConnection;\n\npublic class SimpleLogHandler implements ResponseHandler{\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(SimpleLogHandler.class);\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\t\tLOGGER.warn(conn+\" connectionError \"+e);\n\t\t\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\t\tLOGGER.info(\"connectionAcquired \"+conn);\n\t\t\n\t}\n\n\t@Override\n\tpublic void errorResponse(byte[] err, BackendConnection conn) {\n\t\tLOGGER.warn(\"caught error resp: \" + conn + \" \" + bytesToHex(err));\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] ok, BackendConnection conn) {\n\t\tLOGGER.info(\"okResponse: \" + conn + \",\" + bytesToHex(ok) );\n\t\t\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\t\tLOGGER.info(\"fieldEofResponse : \" + conn );\n\t\tLOGGER.info(\"SimpleLogHandler.fieldEofResponse header: \" + bytesToHex(header) );\n\t\tfor(byte[] field : fields) {\n\t\t\tLOGGER.info(\"SimpleLogHandler.fieldEofResponse fields: \" + bytesToHex(field) );\n\t\t}\n\t\tLOGGER.info(\"SimpleLogHandler.fieldEofResponse eof: \" + bytesToHex(eof) );\n\t\t\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t\tLOGGER.info(\"rowResponse: \" + conn );\n\t\tSystem.out.println(\"SimpleLogHandler.rowResponse: \" + bytesToHex(row) );\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t\tLOGGER.info(\"rowEofResponse: \" + conn );\n\t\tLOGGER.info(\"SimpleLogHandler.rowEofResponse: \" + bytesToHex(eof) );\n\t\t\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\t\t\n\t\t\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\t\t\n\t\t\n\t}\n\n\tpublic static String bytesToHex(byte[] bytes) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (byte b : bytes) {\n\t\t\tsb.append(String.format(\"%02x \", b));\n\t\t}\n\t\treturn sb.toString();\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/SingleNodeHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.base.Strings;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.mysql.LoadDataUtil;\nimport io.mycat.backend.mysql.listener.SqlExecuteStage;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.net.mysql.BinaryRowDataPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.NonBlockingSession;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.parser.ServerParseShow;\nimport io.mycat.server.response.ShowFullTables;\nimport io.mycat.server.response.ShowTables;\nimport io.mycat.statistic.stat.QueryResult;\nimport io.mycat.statistic.stat.QueryResultDispatcher;\nimport io.mycat.util.ResultSetUtil;\nimport io.mycat.util.StringUtil;\n/**\n * @author mycat\n */\npublic class SingleNodeHandler implements ResponseHandler, Terminatable, LoadDataResponseHandler {\n\t\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(SingleNodeHandler.class);\n\t\n\tprivate final RouteResultsetNode node;\n\tprivate final RouteResultset rrs;\n\tprivate final NonBlockingSession session;\n\t\n\t// only one thread access at one time no need lock\n\tprivate volatile byte packetId;\n\tprotected volatile ByteBuffer buffer;\n\tprivate volatile boolean isRunning;\n\tprivate Runnable terminateCallBack;\n\tprotected long startTime;\n\tprotected long netInBytes;\n\tprotected long netOutBytes;\n\tprivate long selectRows;\n\tprotected long affectedRows;\n\tprotected final AtomicBoolean errorRepsponsed = new AtomicBoolean(false);\n\t\n\tprivate boolean prepared;\n\tprotected int fieldCount;\n\tprotected List<FieldPacket> fieldPackets = new ArrayList<FieldPacket>();\n\n    private volatile boolean isDefaultNodeShowTable;\n    private volatile boolean isDefaultNodeShowFullTable;\n    private  Set<String> shardingTablesSet;\n\tprivate byte[] header = null;\n\tprivate List<byte[]> fields = null;\n\tpublic SingleNodeHandler(RouteResultset rrs, NonBlockingSession session) {\n\t\tthis.rrs = rrs;\n\t\tthis.node = rrs.getNodes()[0];\n\t\t\n\t\tif (node == null) {\n\t\t\tthrow new IllegalArgumentException(\"routeNode is null!\");\n\t\t}\n\t\t\n\t\tif (session == null) {\n\t\t\tthrow new IllegalArgumentException(\"session is null!\");\n\t\t}\n\t\t\n\t\tthis.session = session;\n\t\tServerConnection source = session.getSource();\n\t\tString schema = source.getSchema();\n\t\tif (schema != null && ServerParse.SHOW == rrs.getSqlType()) {\n\t\t\tSchemaConfig schemaConfig = MycatServer.getInstance().getConfig().getSchemas().get(schema);\n\t\t\tint type = ServerParseShow.tableCheck(rrs.getStatement(), 0);\n\t\t\tisDefaultNodeShowTable = (ServerParseShow.TABLES == type && !Strings.isNullOrEmpty(schemaConfig.getDataNode()));\n\t\t\tisDefaultNodeShowFullTable = (ServerParseShow.FULLTABLES == type && !Strings.isNullOrEmpty(schemaConfig.getDataNode()));\n\t\t\tif (isDefaultNodeShowTable) {\n\t\t\t\tshardingTablesSet = ShowTables.getTableSet(source, rrs.getStatement());\n\t\t\t\t\n\t\t\t} else if (isDefaultNodeShowFullTable) {\n\t\t\t\tshardingTablesSet = ShowFullTables.getTableSet(source, rrs.getStatement());\n\t\t\t}\n\t\t}\n        \n\t\tif ( rrs != null && rrs.getStatement() != null) {\n\t\t\tnetInBytes += rrs.getStatement().getBytes().length;\n\t\t}\n        \n\t}\n\tpublic NonBlockingSession getSession() {\n\t\treturn this.session;\n\t}\n\tpublic RouteResultsetNode getRouteResultsetNode() {\n\t\treturn this.node;\n\t}\n\tpublic RouteResultset getRouteResultset(){\n\t\treturn this.rrs;\n\t}\n\tpublic ByteBuffer getBuffer() {\n\t\treturn this.buffer;\n\t}\n\t@Override\n\tpublic void terminate(Runnable callback) {\n\t\tboolean zeroReached = false;\n\n\t\tif (isRunning) {\n\t\t\tterminateCallBack = callback;\n\t\t} else {\n\t\t\tzeroReached = true;\n\t\t}\n\n\t\tif (zeroReached) {\n\t\t\tcallback.run();\n\t\t}\n\t}\n\n\tprotected void endRunning() {\n\t\tRunnable callback = null;\n\t\tif (isRunning) {\n\t\t\tisRunning = false;\n\t\t\tcallback = terminateCallBack;\n\t\t\tterminateCallBack = null;\n\t\t}\n\n\t\tif (callback != null) {\n\t\t\tcallback.run();\n\t\t}\n\t}\n\n\tprivate void recycleResources() {\n\n\t\tByteBuffer buf = buffer;\n\t\tif (buf != null) {\n\t\t\tsession.getSource().recycle(buffer);\n\t\t\tbuffer = null;\n\t\t}\n\t}\n\n\tpublic void execute() throws Exception {\n\t\tstartTime=System.currentTimeMillis();\n\t\tServerConnection sc = session.getSource();\n\t\tthis.isRunning = true;\n\t\tif (rrs.isLoadData()) {\n\t\t\tthis.packetId = session.getSource().getLoadDataInfileHandler().getLastPackId();\n\t\t} else {\n\t\t\tthis.packetId = 0;\n\t\t}\n\n\t\tfinal BackendConnection conn = session.getTarget(node);\n\t\tLOGGER.debug(\"rrs.getRunOnSlave() \" + rrs.getRunOnSlaveDebugInfo());\n\t\tnode.setRunOnSlave(rrs.getRunOnSlave());\t// 实现 master/slave注解\n\t\tLOGGER.debug(\"node.getRunOnSlave() \" + node.getRunOnSlaveDebugInfo());\n\t\t \n\t\ttry {\n\t\t\tif (session.tryExistsCon(conn, node)) {\n\t\t\t\t_execute(conn);\n\t\t\t} else {\n\t\t\t\t// create new connection\n\t\n\t\t\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\t\t\t\t\t\t\n\t\t\t\tLOGGER.debug(\"node.getRunOnSlave() \" + node.getRunOnSlave());\n\t\t\t\tnode.setRunOnSlave(rrs.getRunOnSlave());\t// 实现 master/slave注解\n\t\t\t\tLOGGER.debug(\"node.getRunOnSlave() \" + node.getRunOnSlave());\n\t\t\t\t \t\t\n\t\t\t\tPhysicalDBNode dn = conf.getDataNodes().get(node.getName());\n\t\t\t\tdn.getConnection(dn.getDatabase(), sc.isAutocommit(), node, this, node);\n\t\t\t}\n\t\t}catch (Exception e) {\n\t\t\tServerConnection source = session.getSource();\n\t        LOGGER.warn(new StringBuilder().append(source).append(rrs).toString(), e);\n\t\t\t//设置错误\t\t\t\n\t        connectionError(e, null);\n\t\n\t\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(final BackendConnection conn) {\n\t\tsession.bindConnection(node, conn);\n\t\t_execute(conn);\n\n\t}\n\n\tprivate void _execute(BackendConnection conn) {\n\t\tif (session.closed()) {\n\t\t\tendRunning();\n\t\t\tsession.clearResources(true);\n\t\t\treturn;\n\t\t}\n\t\tconn.setResponseHandler(this);\n\t\ttry {\n\t\t\tconn.execute(node, session.getSource(), session.getSource()\n\t\t\t\t\t.isAutocommit());\n\t\t} catch (Exception e1) {\n\t\t\texecuteException(conn, e1);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate void executeException(BackendConnection c, Exception e) {\n\t\tErrorPacket err = new ErrorPacket();\n\t\terr.packetId = ++packetId;\n\t\terr.errno = ErrorCode.ERR_FOUND_EXCEPTION;\n\t\tString message = e.toString();\n\t\tLOGGER.error(message);\n\t\terr.message = StringUtil.encode(message, session.getSource().getCharset());\n\t\tthis.backConnectionErr(err, c);\n\t}\n\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\n\t\tendRunning();\n//\t\tErrorPacket err = new ErrorPacket();\n//\t\terr.packetId = ++packetId;\n//\t\terr.errno = ErrorCode.ER_NEW_ABORTING_CONNECTION;\n//\t\terr.message = StringUtil.encode(e.getMessage(), session.getSource().getCharset());\n\t\t\n\t\tServerConnection source = session.getSource();\n//\t\tsource.write(err.write(allocBuffer(), source, true));\n\t\t//modify by zwy 2018.07\n\t\tif(errorRepsponsed.compareAndSet(false, true)) {\n//\t\t\tsource.setTxInterrupt(e.getMessage());\n\t\t\tsource.writeErrMessage(ErrorCode.ER_NEW_ABORTING_CONNECTION, e.getMessage());\n\t\t}\n\n        source.getListener().fireEvent(SqlExecuteStage.END);\n\t}\n\n\t@Override\n\tpublic void errorResponse(byte[] data, BackendConnection conn) {\n\t\tErrorPacket err = new ErrorPacket();\n\t\terr.read(data);\n\t\terr.packetId = ++packetId;\n\t\tbackConnectionErr(err, conn);\n\t}\n\n\tprivate void backConnectionErr(ErrorPacket errPkg, BackendConnection conn) {\n\t\tendRunning();\n\t\t\n\t\tServerConnection source = session.getSource();\n\t\tString errUser = source.getUser();\n\t\tString errHost = source.getHost();\n\t\tint errPort = source.getLocalPort();\n\t\t\n\t\tString errmgs = \" errno:\" + errPkg.errno + \" \" + new String(errPkg.message);\n\t\tLOGGER.warn(\"execute  sql err :\" + errmgs + \" con:\" + conn \n\t\t\t\t+ \" frontend host:\" + errHost + \"/\" + errPort + \"/\" + errUser);\n\t\t\n\t\tsession.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(), false);\n\t\t\n\t\tsource.setTxInterrupt(errmgs);\n\t\t\n\t\t/**\n\t\t * TODO: 修复全版本BUG\n\t\t * \n\t\t * BUG复现：\n\t\t * 1、MysqlClient:  SELECT 9223372036854775807 + 1;\n\t\t * 2、MyCatServer:  ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'\n\t\t * 3、MysqlClient: ERROR 2013 (HY000): Lost connection to MySQL server during query\n\t\t * \n\t\t * Fixed后\n\t\t * 1、MysqlClient:  SELECT 9223372036854775807 + 1;\n\t\t * 2、MyCatServer:  ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'\n\t\t * 3、MysqlClient: ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'\n\t\t * \n\t\t */\t\t\n\t\t// 由于 pakcetId != 1 造成的问题 \n\t\t//todo 统一调用writeErr\n\t\terrPkg.packetId = 1;\t\t\n\t\t//errPkg.write(source);\n\t\t//modify by zwy\n\t\tif (errorRepsponsed.compareAndSet(false, true)) {\n\t\t\tsource.writeErrMessage(errPkg.errno, new String(errPkg.message));\n\t\t}\n\t\trecycleResources();\n        source.getListener().fireEvent(SqlExecuteStage.END);\n\t}\n\n\n\t/**\n\t * insert/update/delete\n\t * \n\t * okResponse()：读取data字节数组，组成一个OKPacket，并调用ok.write(source)将结果写入前端连接FrontendConnection的写缓冲队列writeQueue中，\n\t * 真正发送给应用是由对应的NIOSocketWR从写队列中读取ByteBuffer并返回的\n\t */\n\t@Override\n\tpublic void okResponse(byte[] data, BackendConnection conn) {      \n\t\t//\n\t\tthis.netOutBytes += data.length;\n\t\t\n\t\tboolean executeResponse = conn.syncAndExcute();\t\t\n\t\tif (executeResponse) {\n\t\t\tServerConnection source = session.getSource();\n\t\t\tOkPacket ok = new OkPacket();\n\t\t\tok.read(data);\n\t\t\tthis.affectedRows += ok.affectedRows;\n\n\t\t\tif (ok.hasMoreResultsExists()) {\n\t\t\t\t// funnyAnt:当是批量update/delete语句，提示后面还有ok包\n\t\t\t\treturn;\n\t\t\t}\n\n            boolean isCanClose2Client =(!rrs.isCallStatement()) ||(rrs.isCallStatement() &&!rrs.getProcedure().isResultSimpleValue());\n\t\t\tif (rrs.isLoadData()) {\t\t\t\t\n\t\t\t\t// byte lastPackId = source.getLoadDataInfileHandler().getLastPackId();\n\t\t\t\tok.packetId = ++packetId;// OK_PACKET\n\t\t\t\tsource.getLoadDataInfileHandler().clear();\n\t\t\t\t\n\t\t\t} else if (isCanClose2Client) {\n\t\t\t\tok.packetId = ++packetId;// OK_PACKET\n\t\t\t}\n\n\n\t\t\tif (isCanClose2Client) {\n\t\t\t\tsession.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(), false);\n\t\t\t\tendRunning();\n\t\t\t}\n\t\t\tok.serverStatus = source.isAutocommit() ? 2 : 1;\n\t\t\trecycleResources();\n\n\t\t\tif (isCanClose2Client) {\n\t\t\t\tsource.setLastInsertId(ok.insertId);\n\t\t\t\t//modify by zwy 2018.07\n\t\t\t\tif(!errorRepsponsed.get() && !session.closed() && source.canResponse()) {\n\t\t\t\t\tok.write(source);\t\n\t\t\t\t}\t\n\t\t\t}\n            \n\t\t\tsource.setExecuteSql(null);\n            source.getListener().fireEvent(SqlExecuteStage.END);\n\t\t\t// add by lian\n\t\t\t// 解决sql统计中写操作永远为0\n            QueryResult queryResult = new QueryResult(session.getSource().getSchema(), session.getSource().getUser(),\n\t\t\t\t\trrs.getSqlType(), rrs.getStatement(), affectedRows, netInBytes, netOutBytes, startTime, System.currentTimeMillis(),0, source.getHost());\n\t\t\tQueryResultDispatcher.dispatchQuery( queryResult );\n\t\t}\n\t}\n\n\t\n\t/**\n\t * select \n\t * \n\t * 行结束标志返回时触发，将EOF标志写入缓冲区，最后调用source.write(buffer)将缓冲区放入前端连接的写缓冲队列中，等待NIOSocketWR将其发送给应用\n\t */\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t\t\n\t\tthis.netOutBytes += eof.length;\n\t\t\n\t\tServerConnection source = session.getSource();\n\t\tconn.recordSql(source.getHost(), source.getSchema(), node.getStatement());\n        // 判断是调用存储过程的话不能在这里释放链接\n\t\tif (!rrs.isCallStatement()||(rrs.isCallStatement()&&rrs.getProcedure().isResultSimpleValue())) \n\t\t{\n\t\t\tsession.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(), false);\n\t\t\tendRunning();\n\t\t}\n\n\t\teof[3] = ++packetId;\n\t\tbuffer = source.writeToBuffer(eof, allocBuffer());\n\t\tint resultSize = source.getWriteQueue().size()*MycatServer.getInstance().getConfig().getSystem().getBufferPoolPageSize();\n\t\tresultSize=resultSize+buffer.position();\n\t\tMiddlerResultHandler middlerResultHandler = session.getMiddlerResultHandler();\n\n\t\tif(middlerResultHandler !=null ){\n\t\t\tmiddlerResultHandler.secondEexcute(); \n\t\t} else{\n\t\t\t//modify by zwy 2018.07\n\t\t\tif(!errorRepsponsed.get()&& !session.closed()&& source.canResponse()) {\n\t\t\t\tsource.write(buffer);\n\t\t\t}\n\t\t}\n\t\tsource.setExecuteSql(null);\n        source.getListener().fireEvent(SqlExecuteStage.END);\n\t\t//TODO: add by zhuam\n\t\t//查询结果派发\n        QueryResult queryResult = new QueryResult(session.getSource().getSchema(), session.getSource().getUser(),\n\t\t\t\trrs.getSqlType(), rrs.getStatement(), affectedRows, netInBytes, netOutBytes, startTime, System.currentTimeMillis(),resultSize, source.getHost());\n\t\tQueryResultDispatcher.dispatchQuery( queryResult );\n\t\t\n\t}\n\n\t/**\n\t * lazy create ByteBuffer only when needed\n\t * \n\t * @return\n\t */\n\tprotected ByteBuffer allocBuffer() {\n\t\tif (buffer == null) {\n\t\t\tbuffer = session.getSource().allocate();\n\t\t}\n\t\treturn buffer;\n\t}\n\n\t/**\n\t * select\n\t * \n\t * 元数据返回时触发，将header和元数据内容依次写入缓冲区中\n\t */\t\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\t\tthis.header = header;\n\t\tthis.fields = fields;\n\t\tMiddlerResultHandler middlerResultHandler = session.getMiddlerResultHandler();\n        if(null !=middlerResultHandler ){\n\t\t\treturn;\n\t\t}\n\t\tthis.netOutBytes += header.length;\n\t\tfor (int i = 0, len = fields.size(); i < len; ++i) {\n\t\t\tbyte[] field = fields.get(i);\n\t\t\tthis.netOutBytes += field.length;\n\t\t}\n\n\t\theader[3] = ++packetId;\n\t\tServerConnection source = session.getSource();\n\t\tbuffer = source.writeToBuffer(header, allocBuffer());\n\t\tfor (int i = 0, len = fields.size(); i < len; ++i) {\n\t\t\tbyte[] field = fields.get(i);\n\t\t\tfield[3] = ++packetId;\n\t\t\t\n\t\t\t // 保存field信息\n \t\t\tFieldPacket fieldPk = new FieldPacket();\n \t\t\tfieldPk.read(field);\n \t\t\tfieldPackets.add(fieldPk);\n\t\t\t\n\t\t\tbuffer = source.writeToBuffer(field, buffer);\n\t\t}\n\t\t\n\t\tfieldCount = fieldPackets.size();\n\t\t\n\t\teof[3] = ++packetId;\n\t\tbuffer = source.writeToBuffer(eof, buffer);\n\n\t\tif (isDefaultNodeShowTable) {\n\t\t\t\n\t\t\tfor (String name : shardingTablesSet) {\n\t\t\t\tRowDataPacket row = new RowDataPacket(1);\n\t\t\t\trow.add(StringUtil.encode(name.toLowerCase(), source.getCharset()));\n\t\t\t\trow.packetId = ++packetId;\n\t\t\t\tbuffer = row.write(buffer, source, true);\n\t\t\t}\n\t\t\t\n\t\t} else if (isDefaultNodeShowFullTable) {\n\t\t\t\n\t\t\tfor (String name : shardingTablesSet) {\n\t\t\t\tRowDataPacket row = new RowDataPacket(1);\n\t\t\t\trow.add(StringUtil.encode(name.toLowerCase(), source.getCharset()));\n\t\t\t\trow.add(StringUtil.encode(\"BASE TABLE\", source.getCharset()));\n\t\t\t\trow.packetId = ++packetId;\n\t\t\t\tbuffer = row.write(buffer, source, true);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * select \n\t * \n\t * 行数据返回时触发，将行数据写入缓冲区中\n\t */\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t\t//已经有错误了直接不处理返回 modify by zwy2018.07\n\t\tif(errorRepsponsed.get()) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tthis.netOutBytes += row.length;\n\t\tthis.selectRows++;\n\t\t\n\t\tif (isDefaultNodeShowTable || isDefaultNodeShowFullTable) {\n\t\t\tRowDataPacket rowDataPacket = new RowDataPacket(1);\n\t\t\trowDataPacket.read(row);\n\t\t\tString table = StringUtil.decode(rowDataPacket.fieldValues.get(0), session.getSource().getCharset());\n\t\t\tif (shardingTablesSet.contains(table.toUpperCase())) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\trow[3] = ++packetId;\n\t\t\n\t\tif ( prepared ) {\t\t\t\n\t\t\tRowDataPacket rowDataPk = new RowDataPacket(fieldCount);\n\t\t\trowDataPk.read(row);\t\t\t\n\t\t\tBinaryRowDataPacket binRowDataPk = new BinaryRowDataPacket();\n\t\t\tbinRowDataPk.read(fieldPackets, rowDataPk);\n\t\t\tbinRowDataPk.packetId = rowDataPk.packetId;\n//\t\t\tbinRowDataPk.write(session.getSource());\n\t\t\t/*\n\t\t\t * [fix bug] : 这里不能直接将包写到前端连接,\n\t\t\t * 因为在fieldEofResponse()方法结束后buffer还没写出,\n\t\t\t * 所以这里应该将包数据顺序写入buffer(如果buffer满了就写出),然后再将buffer写出\n\t\t\t */\n\t\t\tbuffer = binRowDataPk.write(buffer, session.getSource(), true);\n\t\t} else {\n\n\t\t\tMiddlerResultHandler middlerResultHandler = session.getMiddlerResultHandler();\n\t        if(null ==middlerResultHandler ){\n\t        \t buffer = session.getSource().writeToBuffer(row, allocBuffer());\n\t\t\t}else{\n\t\t        if(middlerResultHandler instanceof MiddlerQueryResultHandler){\n\t\t        \tbyte[] rv = ResultSetUtil.getColumnVal(row, fields, 0);\n\t\t\t\t\t \t String rowValue =  rv==null?\"\":new String(rv);\n\t\t\t\t\t\t middlerResultHandler.add(rowValue);\t\n \t\t\t\t }\n\t\t\t}\n\t\t \n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\t\tErrorPacket err = new ErrorPacket();\n\t\terr.packetId = ++packetId;\n\t\terr.errno = ErrorCode.ER_ERROR_ON_CLOSE;\n\t\tLOGGER.error(reason);\n\t\terr.message = StringUtil.encode(reason, session.getSource()\n\t\t\t\t.getCharset());\n\t\tthis.backConnectionErr(err, conn);\n\n\t}\n\n\tpublic void clearResources() {\n\n\t}\n\n\t@Override\n\tpublic void requestDataResponse(byte[] data, BackendConnection conn) {\n\t\tLoadDataUtil.requestFileDataResponse(data, conn);\n\t}\n\t\n\tpublic boolean isPrepared() {\n\t\treturn prepared;\n\t}\n\n\tpublic void setPrepared(boolean prepared) {\n\t\tthis.prepared = prepared;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"SingleNodeHandler [node=\" + node + \", packetId=\" + packetId + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/Terminatable.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.backend.mysql.nio.handler;\n\n/**\n * @author mycat\n */\npublic interface Terminatable {\n    void terminate(Runnable runnable);\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/nio/handler/UnLockTablesHandler.java",
    "content": "package io.mycat.backend.mysql.nio.handler;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.NonBlockingSession;\nimport io.mycat.server.parser.ServerParse;\n\n/**\n * unlock tables 语句处理器\n * @author songdabin\n *\n */\npublic class UnLockTablesHandler extends MultiNodeHandler implements ResponseHandler {\n\t\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(UnLockTablesHandler.class);\n\n\tprivate final NonBlockingSession session;\n\tprivate final boolean autocommit;\n\tprivate final String srcStatement;\n\n\tpublic UnLockTablesHandler(NonBlockingSession session, boolean autocommit, String sql) {\n\t\tsuper(session);\n\t\tthis.session = session;\n\t\tthis.autocommit = autocommit;\n\t\tthis.srcStatement = sql;\n\t}\n\n\tpublic void execute() {\n\t\tMap<RouteResultsetNode, BackendConnection> lockedConns = session.getTargetMap();\n\t\tSet<RouteResultsetNode> dnSet = lockedConns.keySet();\n\t\tthis.reset(lockedConns.size());\n\t\t// 客户端直接发送unlock tables命令，由于之前未发送lock tables语句，无法获取后端绑定的连接，此时直接返回OK包\n\t\tif (lockedConns.size() == 0) {\n\t\t\tLOGGER.warn(\"find no locked backend connection!\"+session.getSource());\n\t\t\tOkPacket ok = new OkPacket();\n\t\t\tok.packetId = ++ packetId;\n\t\t\tok.packetLength = 7; // unlock table 命令返回MySQL协议包长度为7\n\t\t\tok.serverStatus = session.getSource().isAutocommit() ? 2:1;\n\t\t\tok.write(session.getSource());\n\t\t\treturn;\n\t\t}\n\t\tfor (RouteResultsetNode dataNode : dnSet) {\n\t\t\tRouteResultsetNode node = new RouteResultsetNode(dataNode.getName(), ServerParse.UNLOCK, srcStatement);\n\t\t\tBackendConnection conn = lockedConns.get(dataNode);\n\t\t\tif (clearIfSessionClosed(session)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconn.setResponseHandler(this);\n\t\t\ttry {\n\t\t\t\tconn.execute(node, session.getSource(), autocommit);\n\t\t\t} catch (Exception e) {\n\t\t\t\tconnectionError(e, conn);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\t\tsuper.connectionError(e, conn);\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\t\tLOGGER.error(\"unexpected invocation: connectionAcquired from unlock tables\");\n\t}\n\n\t@Override\n\tpublic void errorResponse(byte[] err, BackendConnection conn) {\n\t\tsuper.errorResponse(err, conn);\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] data, BackendConnection conn) {\n\t\tboolean executeResponse = conn.syncAndExcute();\n\t\tif (executeResponse) {\n\t\t\tboolean isEndPack = decrementCountBy(1);\n\t\t\tsession.releaseConnection(conn);\n\t\t\tif (isEndPack) {\n\t\t\t\tif (this.isFail() || session.closed()) {\n\t\t\t\t\ttryErrorFinished(true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tOkPacket ok = new OkPacket();\n\t\t\t\tok.read(data);\n\t\t\t\tlock.lock();\n\t\t\t\ttry {\n\t\t\t\t\tok.packetId = ++ packetId;\n\t\t\t\t\tok.serverStatus = session.getSource().isAutocommit() ? 2:1;\n\t\t\t\t} finally {\n\t\t\t\t\tlock.unlock();\n\t\t\t\t}\n\t\t\t\tok.write(session.getSource());\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields, byte[] eof, BackendConnection conn) {\n\t\tLOGGER.error(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": field's eof\").toString());\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t\tLOGGER.warn(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": row data packet\").toString());\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t\tLOGGER.error(new StringBuilder().append(\"unexpected packet for \")\n\t\t\t\t.append(conn).append(\" bound by \").append(session.getSource())\n\t\t\t\t.append(\": row's eof\").toString());\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/CoordinatorLogEntry.java",
    "content": "package io.mycat.backend.mysql.xa;\n\nimport io.mycat.util.TimeUtil;\n\nimport java.io.Serializable;\n\n/**\n * Created by zhangchao on 2016/10/17.\n */\npublic class CoordinatorLogEntry implements Serializable {\n\n    private static final long serialVersionUID = -919666492191340531L;\n\n    public final String id;\n\n//    public final boolean wasCommitted;\n    public long createTime;\n    public final ParticipantLogEntry[] participants;\n\n\n    public CoordinatorLogEntry(String coordinatorId,\n                               ParticipantLogEntry[] participantDetails) {\n        this(coordinatorId, false, participantDetails, null, TimeUtil.currentTimeMillis());\n    }\n\n    public CoordinatorLogEntry(String coordinatorId, boolean wasCommitted,\n                               ParticipantLogEntry[] participants) {\n        createTime = TimeUtil.currentTimeMillis();\n        this.id = coordinatorId;\n//        this.wasCommitted = wasCommitted;\n        this.participants = participants;\n    }\n\n    public CoordinatorLogEntry(String coordinatorId, boolean wasCommitted,\n                               ParticipantLogEntry[] participants, String superiorCoordinatorId, long creteTime) {\n        this.createTime = creteTime;\n        this.id = coordinatorId;\n        //        this.wasCommitted = wasCommitted;\n        this.participants = participants;\n    }\n\n\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/Deserializer.java",
    "content": "package io.mycat.backend.mysql.xa;\n\nimport io.mycat.backend.mysql.xa.recovery.DeserialisationException;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by zhangchao on 2016/10/17.\n */\npublic class Deserializer {\n\n    private static final String JSON_ARRAY_END = \"]\";\n\n    private static final String JSON_ARRAY_START = \"[\";\n\n    private static final String OBJECT_START= \"{\";\n\n    private static final String OBJECT_END= \"}\";\n\n    List<String> tokenize(String content) {\n        List<String> result = new ArrayList<String>();\n        int endObject = content.indexOf(OBJECT_END);\n        while(endObject >0){\n            String object = content.substring(0,endObject+1);\n            result.add(object);\n            content = content.substring(endObject+1);\n            endObject = content.indexOf(OBJECT_END);\n        }\n        return result;\n    }\n\n    String extractArrayPart(String content) {\n        if(!content.contains(JSON_ARRAY_START) && !content.contains(JSON_ARRAY_END)) {\n            //no array...\n            return \"\";\n        }\n        //else\n        int start=content.indexOf(JSON_ARRAY_START);\n        int end=content.indexOf(JSON_ARRAY_END);\n\n        return content.substring(start+1, end);\n    }\n    public CoordinatorLogEntry fromJSON(String coordinatorLogEntryStr) throws DeserialisationException {\n        try {\n            String jsonContent = coordinatorLogEntryStr.trim();\n            validateJSONContent(jsonContent);\n            Map<String, String> header = extractHeader(jsonContent);\n            String coordinatorId = header.get(\"id\");\n            String arrayContent = extractArrayPart(jsonContent);\n            List<String> elements = tokenize(arrayContent);\n\n            ParticipantLogEntry[] participantLogEntries = new ParticipantLogEntry[elements.size()];\n\n            for (int i = 0; i < participantLogEntries.length; i++) {\n                participantLogEntries[i]=recreateParticipantLogEntry(coordinatorId,elements.get(i));\n            }\n\n\n            CoordinatorLogEntry actual = new CoordinatorLogEntry(header.get(\"id\"),Boolean.valueOf(header.get(\"wasCommitted\")),\n                    participantLogEntries,header.get(\"superiorCoordinatorId\"), Long.valueOf(header.get(\"createTime\")));\n            return actual;\n        } catch (Exception unexpectedEOF) {\n            throw new DeserialisationException(coordinatorLogEntryStr);\n        }\n    }\n\n    private void validateJSONContent(String coordinatorLogEntryStr)\n            throws DeserialisationException {\n        if (!coordinatorLogEntryStr.startsWith(OBJECT_START)){\n            throw new DeserialisationException(coordinatorLogEntryStr);\n        }\n        if (!coordinatorLogEntryStr.endsWith(OBJECT_END)){\n            throw new DeserialisationException(coordinatorLogEntryStr);\n        }\n    }\n\n    private Map<String, String> extractHeader(String coordinatorLogEntryStr) {\n        Map<String,String> header = new HashMap<String, String>(2);\n        String[] attributes = coordinatorLogEntryStr.split(\",\");\n        for (String attribute : attributes) {\n            String[] pair = attribute.split(\":\");\n            header.put(pair[0].replaceAll(\"\\\\{\", \"\").replace(\"\\\"\", \"\"), pair[1].replace(\"\\\"\", \"\"));\n        }\n        return header;\n    }\n\n    ParticipantLogEntry recreateParticipantLogEntry(String coordinatorId,\n                                                    String participantLogEntry) {\n        participantLogEntry = participantLogEntry.replaceAll(\"\\\\{\", \"\").replaceAll(\"\\\\}\", \"\");\n\n        Map<String,String> content = new HashMap<String, String>(5);\n        String[] attributes = participantLogEntry.split(\",\");\n        for (String attribute : attributes) {\n            String[] pair = attribute.split(\":\");\n            if(pair.length>1){\n                content.put(pair[0].replace(\"\\\"\", \"\"), pair[1].replace(\"\\\"\", \"\"));\n            }\n\n        }\n\n        ParticipantLogEntry actual = new ParticipantLogEntry(coordinatorId,\n                content.get(\"uri\"), Long.valueOf(content.get(\"expires\")), content.get(\"resourceName\"), Integer.parseInt(content.get(\"state\")));\n        return actual;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/LogFileLock.java",
    "content": "package io.mycat.backend.mysql.xa;\n\nimport io.mycat.backend.mysql.xa.recovery.LogException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.nio.channels.FileLock;\nimport java.nio.channels.OverlappingFileLockException;\n\n/**\n * Created by zhangchao on 2016/10/17.\n */\npublic class LogFileLock {\n    public static final Logger logger = LoggerFactory\n            .getLogger(LogFileLock.class);\n    private static final String FILE_SEPARATOR = String.valueOf(File.separatorChar);\n    private File lockfileToPreventDoubleStartup_;\n    private FileOutputStream lockfilestream_ = null;\n    private FileLock lock_ = null;\n\n    private String dir;\n\n    private String fileName;\n\n    public LogFileLock(String dir, String fileName) {\n        if(!dir.endsWith(FILE_SEPARATOR)) {\n            dir += FILE_SEPARATOR;\n        }\n        this.dir = dir;\n        this.fileName = fileName;\n    }\n\n    public void acquireLock() throws LogException {\n        try {\n            File parent = new File(dir);\n            if(!parent.exists()) {\n                parent.mkdir();\n            }\n            lockfileToPreventDoubleStartup_ = new File(dir, fileName + \".lck\");\n            lockfilestream_ = new FileOutputStream(lockfileToPreventDoubleStartup_);\n            lock_ = lockfilestream_.getChannel().tryLock();\n            lockfileToPreventDoubleStartup_.deleteOnExit();\n        } catch (OverlappingFileLockException failedToGetLock) {\n            // happens on windows\n            lock_ = null;\n        } catch (IOException failedToGetLock) {\n            // happens on windows\n            lock_ = null;\n        }\n        if (lock_ == null) {\n            logger.error(\"ERROR: the specified log seems to be in use already: \" + fileName + \" in \" + dir + \". Make sure that no other instance is running, or kill any pending process if needed.\");\n            throw new LogException(\"Log already in use? \" + fileName + \" in \"+ dir);\n        }\n    }\n\n    public void releaseLock() {\n        try {\n            if (lock_ != null) {\n                lock_.release();\n            }\n            if (lockfilestream_ != null)\n                lockfilestream_.close();\n        } catch (IOException e) {\n            logger.warn(\"Error releasing file lock: \" + e.getMessage());\n        } finally {\n            lock_ = null;\n        }\n\n        if (lockfileToPreventDoubleStartup_ != null) {\n            lockfileToPreventDoubleStartup_.delete();\n            lockfileToPreventDoubleStartup_ = null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/ParticipantLogEntry.java",
    "content": "package io.mycat.backend.mysql.xa;\n\nimport java.io.Serializable;\n\n/**\n * Created by zhangchao on 2016/10/17.\n */\npublic class ParticipantLogEntry implements Serializable {\n\n    private static final long serialVersionUID = 1728296701394899871L;\n\n    /**\n     * The ID of the global transaction as known by the transaction core.\n     */\n\n    public String coordinatorId;\n\n    /**\n     * Identifies the participant within the global transaction.\n     */\n\n    public String uri;\n\n    /**\n     * When does this participant expire (expressed in millis since Jan 1, 1970)?\n     */\n\n    public long expires;\n\n    /**\n     * Best-known state of the participant.\n     */\n    public int txState;\n\n    /**\n     * For diagnostic purposes, null if not relevant.\n     */\n    public String resourceName;\n\n    public ParticipantLogEntry(String coordinatorId, String uri,\n                               long expires, String resourceName, int txState) {\n        this.coordinatorId = coordinatorId;\n        this.uri = uri;\n        this.expires = expires;\n        this.resourceName = resourceName;\n        this.txState = txState;\n    }\n\n\n\n    @Override\n    public boolean equals(Object other) {\n        boolean ret = false;\n        if (other instanceof ParticipantLogEntry) {\n            ParticipantLogEntry o = (ParticipantLogEntry) other;\n            if (o.coordinatorId.equals(coordinatorId) && o.uri.equals(uri)) ret = true;\n        }\n        return ret;\n    }\n\n    @Override\n    public int hashCode() {\n        return coordinatorId.hashCode();\n    }\n\n\n\n    @Override\n    public String toString() {\n        return \"ParticipantLogEntry [id=\" + coordinatorId\n                + \", uri=\" + uri + \", expires=\" + expires\n                + \", state=\" + txState + \", resourceName=\" + resourceName + \"]\";\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/Serializer.java",
    "content": "package io.mycat.backend.mysql.xa;\n\n/**\n * Created by zhangchao on 2016/10/17.\n */\npublic class Serializer {\n    private static final String PROPERTY_SEPARATOR = \",\";\n    private static final String QUOTE = \"\\\"\";\n    private static final String END_ARRAY = \"]\";\n    private static final String START_ARRAY = \"[\";\n    private static final String START_OBJECT = \"{\";\n    private static final String END_OBJECT = \"}\";\n    private static final String LINE_SEPARATOR = System.getProperty(\"line.separator\");\n\n    public String toJSON(CoordinatorLogEntry coordinatorLogEntry) {\n        StringBuilder strBuilder = new StringBuilder(600);\n        strBuilder.append(START_OBJECT);\n        strBuilder.append(QUOTE).append(\"id\").append(QUOTE).append(\":\").append(QUOTE).append(coordinatorLogEntry.id).append(QUOTE);\n        strBuilder.append(PROPERTY_SEPARATOR);\n        strBuilder.append(QUOTE).append(\"createTime\").append(QUOTE).append(\":\").append(QUOTE).append(coordinatorLogEntry.createTime).append(QUOTE);\n        strBuilder.append(PROPERTY_SEPARATOR);\n        //strBuilder.append(QUOTE).append(\"wasCommitted\").append(QUOTE).append(\":\").append(coordinatorLogEntry.wasCommitted);\n        //strBuilder.append(PROPERTY_SEPARATOR);\n\n        String prefix = \"\";\n        if(coordinatorLogEntry.participants.length>0){\n            strBuilder.append(QUOTE).append(\"participants\").append(QUOTE);\n            strBuilder.append(\":\");\n            strBuilder.append(START_ARRAY);\n\n            for(ParticipantLogEntry participantLogEntry :coordinatorLogEntry.participants){\n                if(participantLogEntry==null){continue;}\n                strBuilder.append(prefix);\n                prefix = PROPERTY_SEPARATOR;\n                strBuilder.append(START_OBJECT);\n                strBuilder.append(QUOTE).append(\"uri\").append(QUOTE).append(\":\").append(QUOTE).append(participantLogEntry.uri).append(QUOTE);\n                strBuilder.append(PROPERTY_SEPARATOR);\n                strBuilder.append(QUOTE).append(\"state\").append(QUOTE).append(\":\").append(QUOTE).append(participantLogEntry.txState).append(QUOTE);\n                strBuilder.append(PROPERTY_SEPARATOR);\n                strBuilder.append(QUOTE).append(\"expires\").append(QUOTE).append(\":\").append(participantLogEntry.expires);\n                if (participantLogEntry.resourceName!=null) {\n                    strBuilder.append(PROPERTY_SEPARATOR);\n                    strBuilder.append(QUOTE).append(\"resourceName\").append(QUOTE).append(\":\").append(QUOTE).append(participantLogEntry.resourceName).append(QUOTE);\n                }\n                strBuilder.append(END_OBJECT);\n            }\n//            for (ParticipantLogEntry participantLogEntry : coordinatorLogEntry.participants) {\n//\n//            }\n            strBuilder.append(END_ARRAY);\n        }\n        strBuilder.append(END_OBJECT);\n        strBuilder.append(LINE_SEPARATOR);\n        return strBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/TxState.java",
    "content": "package io.mycat.backend.mysql.xa;\n\n/**\n * Created by zhangchao on 2016/10/13.\n */\npublic class TxState {\n    /** XA INIT STATUS **/\n    public static final int TX_INITIALIZE_STATE = 0;\n    /** XA STARTED STATUS **/\n    public static final int TX_STARTED_STATE = 1;\n    /** XA is prepared **/\n    public static final int TX_PREPARED_STATE = 2;\n    /** XA is commited **/\n    public static final int TX_COMMITED_STATE = 3;\n    /** XA is rollbacked **/\n    public static final int TX_ROLLBACKED_STATE = 4;\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/VersionedFile.java",
    "content": "package io.mycat.backend.mysql.xa;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.FilenameFilter;\nimport java.io.IOException;\nimport java.io.RandomAccessFile;\nimport java.nio.channels.FileChannel;\n\n/**\n * Created by zhangchao on 2016/10/17.\n */\npublic class VersionedFile {\n\n    private static final String FILE_SEPARATOR = String.valueOf(File.separatorChar);\n    private String baseDir;\n    private String suffix;\n    private String baseName;\n\n    //state attributes below\n\n    private long version;\n    private FileInputStream inputStream;\n\n    private RandomAccessFile randomAccessFile;\n\n\n    /**\n     * Creates a new instance based on the given name parameters.\n     * The actual complete name(s) of the physical file(s) will be based on a version number\n     * inserted in between, to identify versions.\n     *\n     * @param baseDir The base folder.\n     * @param baseName The base name for of the file path/name.\n     * @param suffix The suffix to append to the complete file name.\n     */\n    public VersionedFile ( String baseDir , String baseName , String suffix )\n    {\n\n        if(!baseDir.endsWith(FILE_SEPARATOR)) {\n            baseDir += FILE_SEPARATOR;\n        }\n        this.baseDir = baseDir;\n        this.suffix = suffix;\n        this.baseName = baseName;\n        resetVersion();\n    }\n\n    private void resetVersion()\n    {\n        this.version = extractLastValidVersionNumberFromFileNames();\n    }\n\n    private long extractLastValidVersionNumberFromFileNames() {\n        long version = -1;\n        File cd = new File ( getBaseDir() );\n        String[] names = cd.list ( new FilenameFilter() {\n            public boolean accept ( File dir , String name )\n            {\n                return (name.startsWith ( getBaseName() ) && name\n                        .endsWith ( getSuffix() ));\n            }\n        } );\n        if ( names!= null ) {\n            for ( int i = 0; i < names.length; i++ ) {\n                long sfx = extractVersion ( names[i] );\n                if ( version < 0 || sfx < version )\n                    version = sfx;\n            }\n        }\n\n        return version;\n    }\n\n    private long extractVersion ( String name )\n    {\n        long ret  = 0;\n        int lastpos = name.lastIndexOf ( '.' );\n        int startpos = getBaseName().length ();\n        String suffix = name.substring ( startpos, lastpos );\n        try {\n\n            ret = Long.valueOf( suffix );\n        } catch ( NumberFormatException e ) {\n            IllegalArgumentException err = new IllegalArgumentException ( \"Error extracting version from file: \" + name+\" in \" + getBaseDir() );\n            err.initCause ( e );\n            throw err;\n        }\n        return ret;\n    }\n\n    private String getBackupVersionFileName()\n    {\n        return getBaseUrl() + (version - 1) + getSuffix();\n    }\n\n    public String getCurrentVersionFileName()\n    {\n        return getBaseUrl() + version + getSuffix();\n    }\n\n    public String getBaseUrl()\n    {\n        return baseDir + baseName;\n    }\n\n    public String getBaseDir()\n    {\n        return this.baseDir;\n    }\n\n    public String getBaseName()\n    {\n        return this.baseName;\n    }\n\n    public String getSuffix()\n    {\n        return this.suffix;\n    }\n\n    /**\n     * Opens the last valid version for reading.\n     *\n     * @return A stream to read the last valid contents\n     * of the file: either the backup version (if present)\n     * or the current (and only) version if no backup is found.\n     *\n     * @throws IllegalStateException If a newer version was opened for writing.\n     * @throws FileNotFoundException If no last version was found.\n     */\n    public FileInputStream openLastValidVersionForReading()\n            throws IllegalStateException, FileNotFoundException\n    {\n        if ( randomAccessFile != null ) throw new IllegalStateException ( \"Already started writing.\" );\n        inputStream = new FileInputStream ( getCurrentVersionFileName() );\n        return inputStream;\n    }\n\n    /**\n     * Opens a new version for writing to. Note that\n     * this new version is tentative and cannot be read\n     * by {@link #openLastValidVersionForReading()} until\n     * {@link #discardBackupVersion()} is called.\n     *\n     * @return A stream for writing to.\n     * @throws IllegalStateException If called more than once\n     * without a close in between.\n     * @throws IOException If the file cannot be opened for writing.\n     */\n    public FileOutputStream openNewVersionForWriting() throws IOException\n    {\n        openNewVersionForNioWriting();\n        return new FileOutputStream(randomAccessFile.getFD());\n    }\n\n    /**\n     * Opens a new version for writing to. Note that\n     * this new version is tentative and cannot be read\n     * by {@link #openLastValidVersionForReading()} until\n     * {@link #discardBackupVersion()} is called.\n     *\n     * @return A file for writing to.\n     * @throws IOException\n     *\n     * @throws IllegalStateException If called more than once\n     * without a close in between.\n     * @throws FileNotFoundException If the file cannot be opened for writing.\n     * @throws IOException\n     */\n    public FileChannel openNewVersionForNioWriting() throws FileNotFoundException\n    {\n        if ( randomAccessFile != null ) throw new IllegalStateException ( \"Already writing a new version.\" );\n        //version++;\n        randomAccessFile = new RandomAccessFile(getCurrentVersionFileName(), \"rw\");\n        return randomAccessFile.getChannel();\n    }\n    /**\n     * Discards the backup version (if any).\n     * After calling this method, the newer version\n     * produced after calling {@link #openNewVersionForWriting()}\n     * becomes valid for reading next time when\n     * {@link #openLastValidVersionForReading()} is called.\n     *\n     * Note: it is the caller's responsibility to make sure that\n     * all new data has been flushed to disk before calling this method!\n     *\n     * @throws IllegalStateException If {@link #openNewVersionForWriting()} has not been called yet.\n     * @throws IOException If the previous version exists but could no be deleted.\n     */\n    public void discardBackupVersion() throws IllegalStateException, IOException\n    {\n        if ( randomAccessFile == null ) throw new IllegalStateException ( \"No new version yet!\" );\n        String fileName = getBackupVersionFileName();\n\n        File temp = new File ( fileName );\n        if ( temp.exists() && !temp.delete() ) throw new IOException ( \"Failed to delete backup version: \" + fileName );\n\n    }\n\n    /**\n     * Closes any open resources and resets the file for reading again.\n     * @throws IOException If the output stream could not be closed.\n     */\n\n    public void close() throws IOException\n    {\n        resetVersion();\n        if ( inputStream != null ) {\n            try {\n                inputStream.close();\n            } catch (IOException e) {\n                //don't care and won't happen: closing an input stream\n                //does nothing says the JDK javadoc!\n            } finally {\n                inputStream = null;\n            }\n        }\n        if ( randomAccessFile != null ) {\n            try {\n                if ( randomAccessFile.getFD().valid() ) randomAccessFile.close();\n            } finally {\n                randomAccessFile = null;\n            }\n        }\n    }\n\n    public long getSize()\n    {\n        long res = -1;\n        File f = new File ( getCurrentVersionFileName() );\n        res = f.length();\n        return res;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/XACommitCallback.java",
    "content": "\npackage io.mycat.backend.mysql.xa;\n\nimport io.mycat.backend.mysql.nio.handler.MultiNodeCoordinator;\nimport io.mycat.sqlengine.SQLQueryResult;\nimport io.mycat.sqlengine.SQLQueryResultListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Map;\n\n/**\n * Created by zhangwy on 2018/12/16.\n */\npublic class XACommitCallback implements SQLQueryResultListener<SQLQueryResult<Map<String, String>>> {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(XACommitCallback.class);\n\n    private final String xaId;\n    private final ParticipantLogEntry participantLogEntry;\n\n    public XACommitCallback(String xaId, ParticipantLogEntry participantLogEntry) {\n        this.xaId = xaId;\n        this.participantLogEntry = participantLogEntry;\n    }\n\n    public void onResult(SQLQueryResult<Map<String, String>> result) {\n        //\t\tSQLQueryResult<Map<String, String>> queryRestl=new SQLQueryResult<Map<String, String>>(this.result,!failed, dataNode,errorMsg);\n        if (result.isSuccess()) {\n            //recovery log\n            LOGGER.debug(\"onResult success on xa commit {},{}\", xaId, participantLogEntry.resourceName);\n            CoordinatorLogEntry coordinatorLogEntry = MultiNodeCoordinator.inMemoryRepository.get(xaId);\n            for (int i = 0; i < coordinatorLogEntry.participants.length; i++) {\n                if (coordinatorLogEntry.participants[i].resourceName.equals(participantLogEntry.resourceName)) {\n                    coordinatorLogEntry.participants[i].txState = TxState.TX_COMMITED_STATE;\n                }\n            }\n            MultiNodeCoordinator.inMemoryRepository.put(xaId, coordinatorLogEntry);\n            MultiNodeCoordinator.fileRepository.writeCheckpoint(xaId, MultiNodeCoordinator.inMemoryRepository.getAllCoordinatorLogEntries());\n\n        } else {\n            String errorMsg = result.getErrMsg();\n            LOGGER.error(errorMsg);\n            if (errorMsg.indexOf(\"Unknown XID\") > -1) {\n                //todo unknow xaId how do\n                CoordinatorLogEntry coordinatorLogEntry = MultiNodeCoordinator.inMemoryRepository.get(xaId);\n                for (int i = 0; i < coordinatorLogEntry.participants.length; i++) {\n                    if (coordinatorLogEntry.participants[i].resourceName.equals(participantLogEntry.resourceName)) {\n                        coordinatorLogEntry.participants[i].txState = TxState.TX_COMMITED_STATE;\n                    }\n                }\n                MultiNodeCoordinator.inMemoryRepository.put(xaId, coordinatorLogEntry);\n                MultiNodeCoordinator.fileRepository.writeCheckpoint(xaId, MultiNodeCoordinator.inMemoryRepository.getAllCoordinatorLogEntries());\n            }\n        }\n\n        LOGGER.debug(\"[CALLBACK][XA COMMIT] when Mycat start\");\n\n\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/XARollbackCallback.java",
    "content": "package io.mycat.backend.mysql.xa;\n\nimport io.mycat.backend.mysql.nio.handler.MultiNodeCoordinator;\nimport io.mycat.sqlengine.SQLQueryResult;\nimport io.mycat.sqlengine.SQLQueryResultListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Map;\n\n/**\n * Created by zhangchao on 2016/10/18.\n */\npublic class XARollbackCallback implements SQLQueryResultListener<SQLQueryResult<Map<String, String>>> {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(XARollbackCallback.class);\n\n    private final  String xaId;\n    private final ParticipantLogEntry participantLogEntry;\n\n    public XARollbackCallback(String xaId, ParticipantLogEntry participantLogEntry) {\n        this.xaId = xaId;\n        this.participantLogEntry = participantLogEntry;\n    }\n\n    public void onResult(SQLQueryResult<Map<String, String>> result) {\n        //\t\tSQLQueryResult<Map<String, String>> queryRestl=new SQLQueryResult<Map<String, String>>(this.result,!failed, dataNode,errorMsg);\n        if(result.isSuccess()) {\n            LOGGER.debug(\"onResult success on xa rollback {},{}\", xaId, participantLogEntry.resourceName);\n            //recovery log\n            CoordinatorLogEntry coordinatorLogEntry = MultiNodeCoordinator.inMemoryRepository.get(xaId);\n            for(int i=0; i<coordinatorLogEntry.participants.length;i++){\n                if(coordinatorLogEntry.participants[i].resourceName.equals(participantLogEntry.resourceName)){\n                    coordinatorLogEntry.participants[i].txState = TxState.TX_ROLLBACKED_STATE;\n                }\n            }\n            MultiNodeCoordinator.inMemoryRepository.put(xaId, coordinatorLogEntry);\n            MultiNodeCoordinator.fileRepository.writeCheckpoint(xaId, MultiNodeCoordinator.inMemoryRepository.getAllCoordinatorLogEntries());\n\n        } else {\n            String errorMsg = result.getErrMsg();\n            LOGGER.error( errorMsg );\n            if(errorMsg.indexOf(\"Unknown XID\") > -1) {\n                //todo unknow xaId\n                CoordinatorLogEntry coordinatorLogEntry = MultiNodeCoordinator.inMemoryRepository.get(xaId);\n                for(int i=0; i<coordinatorLogEntry.participants.length;i++){\n                    if(coordinatorLogEntry.participants[i].resourceName.equals(participantLogEntry.resourceName)){\n                        coordinatorLogEntry.participants[i].txState = TxState.TX_ROLLBACKED_STATE;\n                    }\n                }\n                MultiNodeCoordinator.inMemoryRepository.put(xaId,coordinatorLogEntry);\n                MultiNodeCoordinator.fileRepository.writeCheckpoint(xaId, MultiNodeCoordinator.inMemoryRepository.getAllCoordinatorLogEntries());\n            }\n\n        }\n\n        LOGGER.debug(\"[CALLBACK][XA ROLLBACK] when Mycat start\");\n\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/recovery/DeserialisationException.java",
    "content": "package io.mycat.backend.mysql.xa.recovery;\n\n/**\n * Created by zhangchao on 2016/10/17.\n */\npublic class DeserialisationException extends Exception{\n    private static final long serialVersionUID = -3835526236269555460L;\n\n    public DeserialisationException(String content) {\n        super(content);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/recovery/LogException.java",
    "content": "package io.mycat.backend.mysql.xa.recovery;\n\n/**\n * Created by zhangchao on 2016/10/13.\n */\npublic class LogException extends Exception{\n    private static final long serialVersionUID = 3259337218182873867L;\n\n    public LogException() {\n        super();\n    }\n\n    public LogException(String message) {\n        super(message);\n    }\n\n    public LogException(Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/recovery/LogReadException.java",
    "content": "package io.mycat.backend.mysql.xa.recovery;\n\n/**\n * Created by zhangchao on 2016/10/17.\n */\npublic class LogReadException extends LogException{\n\n    private static final long serialVersionUID = -4835268355879075429L;\n\n    public LogReadException() {\n        super();\n    }\n\n    public LogReadException(Throwable cause) {\n        super(cause);\n    }\n\n    public LogReadException(String message) {\n        super(message);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/recovery/LogWriteException.java",
    "content": "package io.mycat.backend.mysql.xa.recovery;\n\n/**\n * Created by zhangchao on 2016/10/17.\n */\npublic class LogWriteException extends LogException{\n\n    private static final long serialVersionUID = 5648208124041649641L;\n\n    public LogWriteException() {\n        super();\n    }\n    public LogWriteException(Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/recovery/Repository.java",
    "content": "package io.mycat.backend.mysql.xa.recovery;\n\nimport io.mycat.backend.mysql.xa.CoordinatorLogEntry;\n\nimport java.util.Collection;\n\n/**\n * Created by zhangchao on 2016/10/13.\n */\npublic interface Repository {\n\n    void init() ;\n\n    void put(String id, CoordinatorLogEntry coordinatorLogEntry);\n\n    CoordinatorLogEntry get(String coordinatorId);\n\n    Collection<CoordinatorLogEntry> findAllCommittingCoordinatorLogEntries() ;\n\n    Collection<CoordinatorLogEntry>  getAllCoordinatorLogEntries() ;\n\n    void writeCheckpoint(String id, Collection<CoordinatorLogEntry> checkpointContent) ;\n\n    void close();\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/recovery/impl/FileSystemRepository.java",
    "content": "package io.mycat.backend.mysql.xa.recovery.impl;\n\nimport java.io.BufferedReader;\nimport java.io.EOFException;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.ObjectStreamException;\nimport java.io.StreamCorruptedException;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.xa.CoordinatorLogEntry;\nimport io.mycat.backend.mysql.xa.Deserializer;\nimport io.mycat.backend.mysql.xa.Serializer;\nimport io.mycat.backend.mysql.xa.VersionedFile;\nimport io.mycat.backend.mysql.xa.recovery.DeserialisationException;\nimport io.mycat.backend.mysql.xa.recovery.Repository;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.SystemConfig;\n\n\n/**\n * Created by zhangchao on 2016/10/13.\n */\npublic class FileSystemRepository implements Repository{\n    public static final Logger logger = LoggerFactory\n            .getLogger(FileSystemRepository.class);\n    private VersionedFile file;\n    private FileChannel rwChannel = null;\n    private  Map<String, String > writeStorage = new HashMap<String, String>();\n\n    public FileSystemRepository()  {\n           init();\n    }\n\n    @Override\n    public void init(){\n//        ConfigProperties configProperties = Configuration.getConfigProperties();\n//        String baseDir = configProperties.getLogBaseDir();\n//        String baseName = configProperties.getLogBaseName();\n        MycatConfig mycatconfig = MycatServer.getInstance().getConfig();\n        SystemConfig systemConfig = mycatconfig.getSystem();\n\n        String baseDir =systemConfig.getXARecoveryLogBaseDir();\n        String baseName = systemConfig.getXARecoveryLogBaseName();\n\n        logger.debug(\"baseDir \" + baseDir);\n        logger.debug(\"baseName \" + baseName);\n\n        //Judge whether exist the basedir\n        createBaseDir(baseDir);\n\n        file = new VersionedFile(baseDir, baseName, \".log\");\n\n    }\n\n    private Serializer serializer = new Serializer();\n\n    /*没有被调用*/\n    @Override\n    public void put(String id, CoordinatorLogEntry coordinatorLogEntry) {\n\n//        try {\n//            initChannelIfNecessary();\n//            write(coordinatorLogEntry, true);\n//        } catch (IOException e) {\n//            logger.error(e.getMessage(),e);\n//        }\n    }\n\n    private synchronized void initChannelIfNecessary()\n            throws FileNotFoundException {\n        if (rwChannel == null) {\n            rwChannel = file.openNewVersionForNioWriting();\n        }\n    }\n\n    private int write(CoordinatorLogEntry coordinatorLogEntry,\n                       boolean flushImmediately) throws IOException {\n        String str = serializer.toJSON(coordinatorLogEntry);\n        //缓存一下\n        writeStorage.put(coordinatorLogEntry.id, str);\n//        logger.info(str);\n        byte[] buffer = str.getBytes();\n        ByteBuffer buff = ByteBuffer.wrap(buffer);\n        writeToFile(buff, flushImmediately);\n        return buffer.length;\n    }\n\n    private synchronized void writeToFile(ByteBuffer buff, boolean force)\n            throws IOException {\n        rwChannel.write(buff);\n        rwChannel.force(force);\n    }\n\n    @Override\n    public CoordinatorLogEntry get(String coordinatorId) {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Collection<CoordinatorLogEntry> findAllCommittingCoordinatorLogEntries() {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Collection<CoordinatorLogEntry> getAllCoordinatorLogEntries() {\n        FileInputStream fis = null;\n        try {\n            fis = file.openLastValidVersionForReading();\n        } catch (FileNotFoundException firstStart) {\n            // the file could not be opened for reading;\n            // merely return the default empty vector\n        }\n        if (fis != null) {\n            return readFromInputStream(fis);\n        }\n        //else\n        return Collections.emptyList();\n    }\n\n    public static Collection<CoordinatorLogEntry> readFromInputStream(\n            InputStream in) {\n        Map<String, CoordinatorLogEntry> coordinatorLogEntries = new HashMap<String, CoordinatorLogEntry>();\n        BufferedReader br = null;\n        try {\n            InputStreamReader isr = new InputStreamReader(in);\n            br = new BufferedReader(isr);\n            coordinatorLogEntries = readContent(br);\n        } catch (Exception e) {\n            logger.error(\"Error in recover\", e);\n        } finally {\n            closeSilently(br);\n        }\n        return coordinatorLogEntries.values();\n    }\n\n    static Map<String, CoordinatorLogEntry> readContent(BufferedReader br)\n            throws IOException {\n\n        Map<String, CoordinatorLogEntry> coordinatorLogEntries = new HashMap<String, CoordinatorLogEntry>();\n        try {\n            String line;\n            while ((line = br.readLine()) != null) {\n                CoordinatorLogEntry coordinatorLogEntry = deserialize(line);\n                coordinatorLogEntries.put(coordinatorLogEntry.id,\n                        coordinatorLogEntry);\n            }\n\n        } catch (EOFException unexpectedEOF) {\n            logger.info(\n                    \"Unexpected EOF - logfile not closed properly last time?\",\n                    unexpectedEOF);\n            // merely return what was read so far...\n        } catch (StreamCorruptedException unexpectedEOF) {\n            logger.info(\n                    \"Unexpected EOF - logfile not closed properly last time?\",\n                    unexpectedEOF);\n            // merely return what was read so far...\n        } catch (ObjectStreamException unexpectedEOF) {\n            logger.info(\n                    \"Unexpected EOF - logfile not closed properly last time?\",\n                    unexpectedEOF);\n            // merely return what was read so far...\n        } catch (DeserialisationException unexpectedEOF) {\n            logger.info(\"Unexpected EOF - logfile not closed properly last time? \"\n                    + unexpectedEOF);\n        }\n        return coordinatorLogEntries;\n    }\n\n    private static void closeSilently(BufferedReader fis) {\n        try {\n            if (fis != null)\n                fis.close();\n        } catch (IOException io) {\n            logger.warn(\"Fail to close logfile after reading - ignoring\");\n        }\n    }\n\n    private static Deserializer deserializer = new Deserializer();\n\n    private static CoordinatorLogEntry deserialize(String line)\n            throws DeserialisationException {\n        return deserializer.fromJSON(line);\n    }\n\n    @Override\n    public void close() {\n        try {\n            closeOutput();\n        } catch (Exception e) {\n            logger.warn(\"Error closing file - ignoring\", e);\n        }\n\n    }\n\n    protected void closeOutput() throws IllegalStateException {\n        try {\n            if (file != null) {\n                file.close();\n            }\n        } catch (IOException e) {\n            throw new IllegalStateException(\"Error closing previous output\", e);\n        }\n    }\n\n    @Override\n    public synchronized void writeCheckpoint(String id,\n                                             Collection<CoordinatorLogEntry> checkpointContent)\n             {\n\n        try {\n            if(rwChannel == null) {\n                initChannelIfNecessary();\n            }\n//            closeOutput();\n//            rwChannel = file.openNewVersionForNioWriting();\n\n            //判断xaId这条记录是否被修改\n            boolean isUpdate = true;\n            for (CoordinatorLogEntry coordinatorLogEntry : checkpointContent) {\n                if(coordinatorLogEntry.id.equals(id)) {\n                    isUpdate = checkForUpdate(id, coordinatorLogEntry);\n                    break;\n                }\n            }\n            if(isUpdate == false ){\n                return ;\n            }\n            //清空所有的缓存\n            writeStorage.clear();\n            rwChannel.position(0);\n            long writeSize = 0 ;\n            for (CoordinatorLogEntry coordinatorLogEntry : checkpointContent) {\n                writeSize += write(coordinatorLogEntry, false);\n            }\n//            logger.info(\"xaId {} writeCheckpoint {}\",id, writeStorage.get(id));\n            rwChannel.truncate(writeSize);\n            rwChannel.force(true);\n            file.discardBackupVersion();\n        } catch (FileNotFoundException firstStart) {\n            // the file could not be opened for reading;\n            // merely return the default empty vector\n        } catch (Exception e) {\n            logger.error(\"Failed to write checkpoint\", e);\n        }\n\n    }\n\n    private boolean checkForUpdate(String id, CoordinatorLogEntry coordinatorLogEntry) {\n        String backCoordinatorLogEntryStr = writeStorage.get(id);\n        String str = serializer.toJSON(coordinatorLogEntry);\n        if(null == backCoordinatorLogEntryStr || !str.equals(backCoordinatorLogEntryStr)) {\n            writeStorage.put(id, str);\n            return true;\n        }\n        return false;\n    }\n\n\n\n    /**\n     * create the log base dir\n     * @param baseDir\n     */\n    public void createBaseDir(String baseDir){\n        File baseDirFolder = new File (baseDir);\n        if (!baseDirFolder.exists()){\n                baseDirFolder.mkdirs();\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/mysql/xa/recovery/impl/InMemoryRepository.java",
    "content": "package io.mycat.backend.mysql.xa.recovery.impl;\n\nimport io.mycat.backend.mysql.nio.handler.MultiNodeCoordinator;\nimport io.mycat.backend.mysql.xa.CoordinatorLogEntry;\nimport io.mycat.backend.mysql.xa.ParticipantLogEntry;\nimport io.mycat.backend.mysql.xa.TxState;\nimport io.mycat.backend.mysql.xa.recovery.Repository;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Created by zhangchao on 2016/10/18.\n */\npublic class InMemoryRepository implements Repository {\n\n    private Map<String, CoordinatorLogEntry> storage = new ConcurrentHashMap<String, CoordinatorLogEntry>();\n\n\n    private boolean closed = true;\n    long count = 0 ;\n    @Override\n    public void init() {\n        closed=false;\n    }\n\n    @Override\n    public synchronized void put(String id, CoordinatorLogEntry coordinatorLogEntry) {\n    \tcount++ ;\n    \tif(count > 1000){\n    \t\tcount = 0;\n            clear(id);\n    \t\t\n    \t}\n        storage.put(id, coordinatorLogEntry);\n    }\n    private void clear(String id) {\n    \tCollection<CoordinatorLogEntry>  checkpointContent = storage.values();;\n    \tfor (CoordinatorLogEntry coordinatorLogEntry : checkpointContent) {\n    \t\tParticipantLogEntry[] participants = coordinatorLogEntry.participants;\n        \tboolean hasAllFinish = true;\n        \tfor(int i = 0 ; i < participants.length; i++) {\n        \t\tif(participants[i].txState != TxState.TX_ROLLBACKED_STATE \n        \t\t\t\t&& participants[i].txState != TxState.TX_COMMITED_STATE) {\n        \t\t\thasAllFinish = false;\n        \t\t\tbreak;\n        \t\t}\n        \t}\n            if(hasAllFinish && !id.equals(coordinatorLogEntry.id)) {\n            \tstorage.remove(coordinatorLogEntry.id);\n//                ((FileSystemRepository)MultiNodeCoordinator.fileRepository).writeStorage.remove(id);\n        \t}\n        }\n    }\n    @Override\n    public synchronized CoordinatorLogEntry get(String coordinatorId) {\n        return storage.get(coordinatorId);\n    }\n\n    @Override\n    public synchronized Collection<CoordinatorLogEntry> findAllCommittingCoordinatorLogEntries() {\n//        Set<CoordinatorLogEntry> res = new HashSet<CoordinatorLogEntry>();\n//        Collection<CoordinatorLogEntry> allCoordinatorLogEntry = storage.values();\n//        for (CoordinatorLogEntry coordinatorLogEntry : allCoordinatorLogEntry) {\n//            if(coordinatorLogEntry.getResultingState() == TxState.TX_PREPARED_STATE){\n//                res.add(coordinatorLogEntry);\n//            }\n//        }\n//        return res;\n        return null;\n    }\n\n    @Override\n    public void close() {\n        storage.clear();\n        closed=true;\n    }\n\n    @Override\n    public Collection<CoordinatorLogEntry> getAllCoordinatorLogEntries() {\n        return storage.values();\n    }\n\n    @Override\n    public void writeCheckpoint( String id,\n            Collection<CoordinatorLogEntry> checkpointContent) {\n        storage.clear();\n        for (CoordinatorLogEntry coordinatorLogEntry : checkpointContent) {\n            storage.put(coordinatorLogEntry.id, coordinatorLogEntry);\n        }\n\n    }\n\n\n\n    public boolean isClosed() {\n        return closed;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/PostgreSQLBackendConnection.java",
    "content": "package io.mycat.backend.postgresql;\n\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.NetworkChannel;\nimport java.nio.channels.SocketChannel;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport io.mycat.backend.jdbc.ShowVariables;\nimport io.mycat.backend.mysql.CharsetUtil;\nimport io.mycat.backend.mysql.nio.MySQLConnectionHandler;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.backend.postgresql.packet.Query;\nimport io.mycat.backend.postgresql.packet.Terminate;\nimport io.mycat.backend.postgresql.utils.PIOUtils;\nimport io.mycat.backend.postgresql.utils.PacketUtils;\nimport io.mycat.backend.postgresql.utils.PgSqlApaterUtils;\nimport io.mycat.config.Isolations;\nimport io.mycat.net.BackendAIOConnection;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.util.exception.UnknownTxIsolationException;\n\n/*************************************************************\n * PostgreSQL Native Connection impl\n * \n * @author Coollf\n *\n */\npublic class PostgreSQLBackendConnection extends BackendAIOConnection {\n\n\tpublic static enum BackendConnectionState {\n\t\tclosed, connected, connecting\n\t}\n\n\tprivate static class StatusSync {\n\t\tprivate final Boolean autocommit;\n\t\tprivate final Integer charsetIndex;\n\t\tprivate final String schema;\n\t\tprivate final AtomicInteger synCmdCount;\n\t\tprivate final Integer txtIsolation;\n\t\tprivate final boolean xaStarted;\n\t\tprivate Boolean txReadonly;\n\n\t\tpublic StatusSync(boolean xaStarted, String schema, Integer charsetIndex, Integer txtIsolation,\n\t\t\t\tBoolean autocommit, int synCount, Boolean txReadonly) {\n\t\t\tsuper();\n\t\t\tthis.xaStarted = xaStarted;\n\t\t\tthis.schema = schema;\n\t\t\tthis.charsetIndex = charsetIndex;\n\t\t\tthis.txtIsolation = txtIsolation;\n\t\t\tthis.autocommit = autocommit;\n\t\t\tthis.synCmdCount = new AtomicInteger(synCount);\n\t\t\tthis.txReadonly = txReadonly;\n\t\t}\n\n\t\tpublic boolean synAndExecuted(PostgreSQLBackendConnection conn) {\n\t\t\tint remains = synCmdCount.decrementAndGet();\n\t\t\tif (remains == 0) {// syn command finished\n\t\t\t\tthis.updateConnectionInfo(conn);\n\t\t\t\tconn.metaDataSyned = true;\n\t\t\t\treturn false;\n\t\t\t} else if (remains < 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate void updateConnectionInfo(PostgreSQLBackendConnection conn)\n\n\t\t{\n\t\t\tconn.xaStatus = (xaStarted) ? 1 : 0;\n\t\t\tif (schema != null) {\n\t\t\t\tconn.schema = schema;\n\t\t\t\tconn.oldSchema = conn.schema;\n\t\t\t}\n\t\t\tif (charsetIndex != null) {\n\t\t\t\tconn.setCharset(CharsetUtil.getCharset(charsetIndex));\n\t\t\t}\n\t\t\tif (txtIsolation != null) {\n\t\t\t\tconn.txIsolation = txtIsolation;\n\t\t\t}\n\t\t\tif (autocommit != null) {\n\t\t\t\tconn.autocommit = autocommit;\n\t\t\t}\n\t\t\tif (txReadonly != null) {\n\t\t\t\tconn.txReadonly = txReadonly;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate static final Query _COMMIT = new Query(\"commit\");\n\n\tprivate static final Query _ROLLBACK = new Query(\"rollback\");\n\n\tprivate static void getCharsetCommand(StringBuilder sb, int clientCharIndex) {\n\t\tsb.append(\"SET names '\").append(CharsetUtil.getCharset(clientCharIndex).toUpperCase()).append(\"';\");\n\t}\n\n\t/**\n\t * 获取 更改事物级别sql\n\t * \n\t * @param\n\t * @param txIsolation\n\t */\n\tprivate static void getTxIsolationCommand(StringBuilder sb, int txIsolation) {\n\t\tswitch (txIsolation) {\n\t\tcase Isolations.READ_UNCOMMITTED:\n\t\t\tsb.append(\"SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;\");\n\t\t\treturn;\n\t\tcase Isolations.READ_COMMITTED:\n\t\t\tsb.append(\"SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;\");\n\t\t\treturn;\n\t\tcase Isolations.REPEATED_READ:\n\t\t\tsb.append(\"SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;\");\n\t\t\treturn;\n\t\tcase Isolations.SERIALIZABLE:\n\t\t\tsb.append(\"SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;\");\n\t\t\treturn;\n\t\tdefault:\n\t\t\tthrow new UnknownTxIsolationException(\"txIsolation:\" + txIsolation);\n\t\t}\n\t}\n\n\tprivate Object attachment;\n\n\tprivate volatile boolean autocommit=true;\n\n\tprivate volatile boolean txReadonly=false;\n\n\tprivate volatile boolean borrowed;\n\n\tprotected volatile String charset = \"utf8\";\n\n\n\t/***\n\t * 当前事物ID\n\t */\n\tprivate volatile String currentXaTxId;\n\n\t/**\n\t * 来自子接口\n\t */\n\tprivate volatile boolean fromSlaveDB;\n\n\t/****\n\t * PG是否在事物中\n\t */\n\tprivate volatile boolean inTransaction = false;\n\n\tprivate AtomicBoolean isQuit = new AtomicBoolean(false);\n\n\tprivate volatile long lastTime;\n\n\t/**\n\t * 元数据同步\n\t */\n\tprivate volatile boolean metaDataSyned = true;\n\tprivate volatile boolean modifiedSQLExecuted = false;\n\tprivate volatile String oldSchema;\n\n\t/**\n\t * 密码\n\t */\n\tprivate volatile String password;\n\n\t/**\n\t * 数据源配置\n\t */\n\tprivate PostgreSQLDataSource pool;\n\n\t/***\n\t * 响应handler\n\t */\n\tprivate volatile ResponseHandler responseHandler;\n\t/***\n\t * 对应数据库空间\n\t */\n\tprivate volatile String schema;\n\t// PostgreSQL服务端密码\n\tprivate volatile int serverSecretKey;\n\tprivate volatile BackendConnectionState state = BackendConnectionState.connecting;\n\tprivate volatile StatusSync statusSync;\n\n\tprivate volatile int txIsolation;\n\n\t/***\n\t * 用户名\n\t */\n\tprivate volatile String user;\n\n\tprivate volatile int xaStatus;\n\n\tpublic PostgreSQLBackendConnection(NetworkChannel channel, boolean fromSlaveDB) {\n\t\tsuper(channel);\n\t\tthis.fromSlaveDB = fromSlaveDB;\n\t}\n\n\t@Override\n\tpublic void commit() {\n\t\tByteBuffer buf = this.allocate();\n\t\t_COMMIT.write(buf);\n\t\tthis.write(buf);\n\t}\n\n\t@Override\n\tpublic void execute(RouteResultsetNode rrn, ServerConnection sc, boolean autocommit) throws IOException {\t\n\t\tint sqlType = rrn.getSqlType();\n\t\tString orgin = rrn.getStatement();\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"{}查询任务。。。。{}\", id, rrn.getStatement());\n\t\t\tLOGGER.debug(orgin);\n\t\t}\t\t\n\t\t\n\t\t//FIX BUG  https://github.com/MyCATApache/Mycat-Server/issues/1185\n\t\tif (sqlType == ServerParse.SELECT || sqlType == ServerParse.SHOW) {\t\t\t\n\t\t\tif (sqlType == ServerParse.SHOW) {\n\t\t\t\t//此处进行部分SHOW 语法适配\t\t\t\n\t\t\t\tString _newSql = PgSqlApaterUtils.apater(orgin);\t\t\t\t\n\t\t\t\tif(_newSql.trim().substring(0,4).equalsIgnoreCase(\"show\")){//未能适配成功\n\t\t\t\t\tShowVariables.execute(sc, orgin, this);\n\t\t\t\t\treturn;\t\n\t\t\t\t}\n\t\t\t} else if (\"SELECT CONNECTION_ID()\".equalsIgnoreCase(orgin)) {\n\t\t\t\tShowVariables.justReturnValue(sc, String.valueOf(sc.getId()), this);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (!modifiedSQLExecuted && rrn.isModifySQL()) {\n\t\t\tmodifiedSQLExecuted = true;\n\t\t}\n\t\tString xaTXID = null;\n\t\tif(sc.getSession2().getXaTXID()!=null){\n\t\t\txaTXID = sc.getSession2().getXaTXID() +\",'\"+getSchema()+\"'\";\n\t\t}\n\t\tsynAndDoExecute(xaTXID, rrn, sc.getCharsetIndex(), sc.getTxIsolation(), autocommit, sc.isTxReadonly());\n\t}\n\n\t@Override\n\tpublic Object getAttachment() {\n\t\treturn attachment;\n\t}\n\n\tprivate void getAutocommitCommand(StringBuilder sb, boolean autoCommit) {\n\t\tif (autoCommit) {\n\t\t\tsb.append(/*\"SET autocommit=1;\"*/\"\");//Fix bug  由于 PG9.0 开始不支持此选项，默认是为自动提交逻辑。\n\t\t} else {\n\t\t\tsb.append(\"begin transaction;\");\n\t\t}\n\t}\n\tprivate void getTxReadonly(StringBuilder sb, boolean txReadonly) {\n\t\tif (txReadonly) {\n\t\t\tsb.append(\"SET SESSION TRANSACTION READ ONLY;\");\n\t\t} else {\n\t\t\tsb.append(\"SET SESSION TRANSACTION READ WRITE;\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic long getLastTime() {\n\t\treturn lastTime;\n\t}\n\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\n\tpublic PostgreSQLDataSource getPool() {\n\t\treturn pool;\n\t}\n\n\tpublic ResponseHandler getResponseHandler() {\n\t\treturn responseHandler;\n\t}\n\n\t@Override\n\tpublic String getSchema() {\n\t\treturn this.schema;\n\t}\n\n\tpublic int getServerSecretKey() {\n\t\treturn serverSecretKey;\n\t}\n\n\tpublic BackendConnectionState getState() {\n\t\treturn state;\n\t}\n\n\t@Override\n\tpublic int getTxIsolation() {\n\t\treturn txIsolation;\n\t}\n\n\tpublic String getUser() {\n\t\treturn user;\n\t}\n\n\t@Override\n\tpublic boolean isAutocommit() {\n\t\treturn autocommit;\n\t}\n\n\t@Override\n\tpublic boolean isTxReadonly() {\n\t\treturn txReadonly;\n\t}\n\n\t@Override\n\tpublic int getSqlSelectLimit() {\n\t\t// todo\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic boolean isBorrowed() {\n\t\treturn borrowed;\n\t}\n\n\t@Override\n\tpublic boolean isClosedOrQuit() {\n\t\treturn isClosed() || isQuit.get();\n\t}\n\n\t@Override\n\tpublic boolean isFromSlaveDB() {\n\t\treturn fromSlaveDB;\n\t}\n\n\tpublic boolean isInTransaction() {\n\t\treturn inTransaction;\n\t}\n\n\t@Override\n\tpublic boolean isModifiedSQLExecuted() {\n\t\treturn modifiedSQLExecuted;\n\t}\n\n\t@Override\n\tpublic void onConnectFailed(Throwable t) {\n\t\tif (handler instanceof MySQLConnectionHandler) {\n\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onConnectfinish() {\n\t\tLOGGER.debug(\"连接后台真正完成\");\n\t\ttry {\n\t\t\tSocketChannel chan = (SocketChannel) this.channel;\n\t\t\tByteBuffer buf = PacketUtils.makeStartUpPacket(user, schema);\n\t\t\tbuf.flip();\n\t\t\tchan.write(buf);\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(\"Connected PostgreSQL Send StartUpPacket ERROR\", e);\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprotected final int getPacketLength(ByteBuffer buffer, int offset) {\n\t\t// Pg 协议获取包长度的方法和mysql 不一样\n\t\treturn PIOUtils.redInteger4(buffer, offset + 1) + 1;\n\t}\n\n\t/**********\n\t * 此查询用于心跳检查和获取连接后的健康检查\n\t */\n\t@Override\n\tpublic void query(String query) throws UnsupportedEncodingException {\n\t\tRouteResultsetNode rrn = new RouteResultsetNode(\"default\", ServerParse.SELECT, query);\n\t\tsynAndDoExecute(null, rrn, this.charsetIndex, this.txIsolation, true, this.txReadonly);\n\t}\n\n\t@Override\n\tpublic void quit() {\n\t\tif (isQuit.compareAndSet(false, true) && !isClosed()) {\n\t\t\tif (state == BackendConnectionState.connected) {// 断开 与PostgreSQL连接\n\t\t\t\tTerminate terminate = new Terminate();\n\t\t\t\tByteBuffer buf = this.allocate();\n\t\t\t\tterminate.write(buf);\n\t\t\t\twrite(buf);\n\t\t\t} else {\n\t\t\t\tclose(\"normal\");\n\t\t\t}\n\t\t}\n\t}\n\n\t/*******\n\t * 记录sql执行信息\n\t */\n\t@Override\n\tpublic void recordSql(String host, String schema, String statement) {\n\t\tLOGGER.debug(String.format(\"executed sql: host=%s,schema=%s,statement=%s\", host, schema, statement));\n\t}\n\n\t@Override\n\tpublic void release() {\n\t\tif (!metaDataSyned) {/*\n\t\t\t\t\t\t\t\t * indicate connection not normalfinished ,and\n\t\t\t\t\t\t\t\t * we can't know it's syn status ,so close it\n\t\t\t\t\t\t\t\t */\n\n\t\t\tLOGGER.warn(\"can't sure connection syn result,so close it \" + this);\n\t\t\tthis.responseHandler = null;\n\t\t\tthis.close(\"syn status unkown \");\n\t\t\treturn;\n\t\t}\n\t\tmetaDataSyned = true;\n\t\tattachment = null;\n\t\tstatusSync = null;\n\t\tmodifiedSQLExecuted = false;\n\t\tsetResponseHandler(null);\n\t\tpool.releaseChannel(this);\n\t}\n\n\t@Override\n\tpublic void rollback() {\n\t\tByteBuffer buf = this.allocate();\n\t\t_ROLLBACK.write(buf);\n\t\tthis.write(buf);\n\t}\n\n\t@Override\n\tpublic void setAttachment(Object attachment) {\n\t\tthis.attachment = attachment;\n\t}\n\n\t@Override\n\tpublic void setBorrowed(boolean borrowed) {\n\t\tthis.borrowed = borrowed;\n\t}\n\n\tpublic void setInTransaction(boolean inTransaction) {\n\t\tthis.inTransaction = inTransaction;\n\t}\n\n\t@Override\n\tpublic void setLastTime(long currentTimeMillis) {\n\t\tthis.lastTime = currentTimeMillis;\n\t}\n\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\n\tpublic void setPool(PostgreSQLDataSource pool) {\n\t\tthis.pool = pool;\n\t}\n\n\t@Override\n\tpublic boolean setResponseHandler(ResponseHandler commandHandler) {\n\t\tthis.responseHandler = commandHandler;\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void setSchema(String newSchema) {\n\t\tString curSchema = schema;\n\t\tif (curSchema == null) {\n\t\t\tthis.schema = newSchema;\n\t\t\tthis.oldSchema = newSchema;\n\t\t} else {\n\t\t\tthis.oldSchema = curSchema;\n\t\t\tthis.schema = newSchema;\n\t\t}\n\t}\n\n\tpublic void setServerSecretKey(int serverSecretKey) {\n\t\tthis.serverSecretKey = serverSecretKey;\n\t}\n\n\tpublic void setState(BackendConnectionState state) {\n\t\tthis.state = state;\n\t}\n\n\tpublic void setUser(String user) {\n\t\tthis.user = user;\n\t}\n\n\tprivate void synAndDoExecute(String xaTxID, RouteResultsetNode rrn, int clientCharSetIndex, int clientTxIsoLation,\n\t\t\tboolean clientAutoCommit, boolean clientTxReadonly) {\n\t\tString xaCmd = null;\n\n\t\tboolean conAutoComit = this.autocommit;\n\t\tboolean conTxReadonly = this.txReadonly;\n\t\tString conSchema = this.schema;\n\t\t// never executed modify sql,so auto commit\n\t\tboolean expectAutocommit = !modifiedSQLExecuted || isFromSlaveDB() || clientAutoCommit;\n\t\tif (!expectAutocommit && xaTxID != null && xaStatus == 0) {\n\t\t\tclientTxIsoLation = Isolations.SERIALIZABLE;\n\t\t\txaCmd = \"XA START \" + xaTxID + ';';\n\t\t\tcurrentXaTxId = xaTxID;\n\t\t}\n\t\tint schemaSyn = conSchema.equals(oldSchema) ? 0 : 1;\n\t\tint charsetSyn = (this.charsetIndex == clientCharSetIndex) ? 0 : 1;\n\t\tint txIsoLationSyn = (txIsolation == clientTxIsoLation) ? 0 : 1;\n\t\tint autoCommitSyn = (conAutoComit == expectAutocommit) ? 0 : 1;\n\t\tint txReadonlySyn = (conTxReadonly == clientTxReadonly) ? 0 : 1;\n\t\tint synCount = schemaSyn + charsetSyn + txIsoLationSyn + autoCommitSyn + txReadonlySyn;\n\n\t\tif (synCount == 0) {\n\t\t\tString sql = rrn.getStatement();\n\t\t\tQuery query = new Query(PgSqlApaterUtils.apater(sql));\n\t\t\tByteBuffer buf = this.allocate();// XXX 此处处理问题\n\t\t\tquery.write(buf);\n\t\t\tthis.write(buf);\n\t\t\treturn;\n\t\t}\n\n\t\t// TODO COOLLF 此处大锅待实现. 相关 事物, 切换 库,自动提交等功能实现\n\t\tStringBuilder sb = new StringBuilder();\n\t\tif (charsetSyn == 1) {\n\t\t\tgetCharsetCommand(sb, clientCharSetIndex);\n\t\t}\n\t\tif (txIsoLationSyn == 1) {\n\t\t\tgetTxIsolationCommand(sb, clientTxIsoLation);\n\t\t}\n\t\tif (autoCommitSyn == 1) {\n\t\t\tgetAutocommitCommand(sb, expectAutocommit);\n\t\t}\n\t\tif (txReadonlySyn == 1) {\n\t\t\tgetTxReadonly(sb, clientTxReadonly);\n\t\t}\n\t\tif (xaCmd != null) {\n\t\t\tsb.append(xaCmd);\n\t\t}\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"con need syn ,total syn cmd \" + synCount + \" commands \" + sb.toString() + \"schema change:\"\n\t\t\t\t\t+ (\"\" != null) + \" con:\" + this);\n\t\t}\n\n\t\tmetaDataSyned = false;\n\t\tstatusSync = new StatusSync(xaCmd != null, conSchema, clientCharSetIndex, clientTxIsoLation, expectAutocommit,\n\t\t\t\tsynCount, clientTxReadonly);\n\t\tString sql = sb.append(PgSqlApaterUtils.apater(rrn.getStatement())).toString();\n\t\tif(LOGGER.isDebugEnabled()){\n\t\t\tLOGGER.debug(\"con={}, SQL={}\", this, sql);\n\t\t}\n\t\tQuery query = new Query(sql);\n\t\tByteBuffer buf = allocate();// 申请ByetBuffer\n\t\tquery.write(buf);\n\t\tthis.write(buf);\n\t\tmetaDataSyned = true;\n\t}\n\n\tpublic void close(String reason) {\n\t\tif (!isClosed.get()) {\n\t\t\tisQuit.set(true);\n\t\t\tsuper.close(reason);\n\t\t\tpool.connectionClosed(this);\n\t\t\tif (this.responseHandler != null) {\n\t\t\t\tthis.responseHandler.connectionClose(this, reason);\n\t\t\t\tresponseHandler = null;\n\t\t\t}\n\t\t}\n\t}\n\n    @Override\n    public void closeWithoutRsp(String reason) {\n        // TODO Auto-generated method stub\n        this.responseHandler = null;\n        close(reason);\n    }\n\n\t@Override\n\tpublic boolean syncAndExcute() {\n\t\tStatusSync sync = this.statusSync;\n\t\tif (sync != null) {\n\t\t\tboolean executed = sync.synAndExecuted(this);\n\t\t\tif (executed) {\n\t\t\t\tstatusSync = null;\n\t\t\t}\n\t\t\treturn executed;\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"PostgreSQLBackendConnection [id=\" + id + \", host=\" + host + \", port=\" + port + \", localPort=\"\n\t\t\t\t+ localPort + \"]\";\n\t}\n\n\t@Override\n\tpublic void query(String sql, int charsetIndex) {\n\t\ttry {\n\t\t\tquery(sql);\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\te.printStackTrace();\n\t\t\tLOGGER.debug(\"UnsupportedEncodingException :\"+ e.getMessage());\n\t\t}\n\t}\n\n    @Override\n    public void disableRead() {\n        // TODO Auto-generated method stub\n\n    }\n\n    @Override\n    public void enableRead() {\n        // TODO Auto-generated method stub\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/PostgreSQLBackendConnectionFactory.java",
    "content": "package io.mycat.backend.postgresql;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.config.model.DBHostConfig;\nimport io.mycat.net.NIOConnector;\nimport io.mycat.net.factory.BackendConnectionFactory;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.nio.channels.AsynchronousSocketChannel;\nimport java.nio.channels.CompletionHandler;\nimport java.nio.channels.NetworkChannel;\n\npublic class PostgreSQLBackendConnectionFactory extends\n\t\tBackendConnectionFactory {\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tpublic PostgreSQLBackendConnection make(PostgreSQLDataSource pool,\n\t\t\tResponseHandler handler, final String schema) throws IOException {\n\n\t\tfinal DBHostConfig dsc = pool.getConfig();\n\t\tNetworkChannel channel = this.openSocketChannel(MycatServer\n\t\t\t\t.getInstance().isAIO());\n\n\t\tfinal PostgreSQLBackendConnection c = new PostgreSQLBackendConnection(\n\t\t\t\tchannel, pool.isReadNode());\n\t\tMycatServer.getInstance().getConfig().setSocketParams(c, false);\n\t\t// 设置NIOHandler\n\t\tc.setHandler(new PostgreSQLBackendConnectionHandler(c));\n\t\tc.setHost(dsc.getIp());\n\t\tc.setPort(dsc.getPort());\n\t\tc.setUser(dsc.getUser());\n\t\tc.setPassword(dsc.getPassword());\n\t\tc.setSchema(schema);\n\t\tc.setPool(pool);\n\t\tc.setResponseHandler(handler);\n\t\tc.setIdleTimeout(pool.getConfig().getIdleTimeout());\n\t\tif (channel instanceof AsynchronousSocketChannel) {\n\t\t\t((AsynchronousSocketChannel) channel).connect(\n\t\t\t\t\tnew InetSocketAddress(dsc.getIp(), dsc.getPort()), c,\n\t\t\t\t\t(CompletionHandler) MycatServer.getInstance()\n\t\t\t\t\t\t\t.getConnector());\n\t\t} else {\n\t\t\t((NIOConnector) MycatServer.getInstance().getConnector())\n\t\t\t\t\t.postConnect(c);\n\n\t\t}\n\t\treturn c;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/PostgreSQLBackendConnectionHandler.java",
    "content": "package io.mycat.backend.postgresql;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.backend.postgresql.PostgreSQLBackendConnection.BackendConnectionState;\nimport io.mycat.backend.postgresql.packet.AuthenticationPacket;\nimport io.mycat.backend.postgresql.packet.AuthenticationPacket.AuthType;\nimport io.mycat.backend.postgresql.packet.BackendKeyData;\nimport io.mycat.backend.postgresql.packet.CommandComplete;\nimport io.mycat.backend.postgresql.packet.CopyInResponse;\nimport io.mycat.backend.postgresql.packet.CopyOutResponse;\nimport io.mycat.backend.postgresql.packet.DataRow;\nimport io.mycat.backend.postgresql.packet.EmptyQueryResponse;\nimport io.mycat.backend.postgresql.packet.ErrorResponse;\nimport io.mycat.backend.postgresql.packet.NoticeResponse;\nimport io.mycat.backend.postgresql.packet.NotificationResponse;\nimport io.mycat.backend.postgresql.packet.ParameterStatus;\nimport io.mycat.backend.postgresql.packet.PasswordMessage;\nimport io.mycat.backend.postgresql.packet.PostgreSQLPacket;\nimport io.mycat.backend.postgresql.packet.ReadyForQuery;\nimport io.mycat.backend.postgresql.packet.ReadyForQuery.TransactionState;\nimport io.mycat.backend.postgresql.packet.RowDescription;\nimport io.mycat.backend.postgresql.utils.PacketUtils;\nimport io.mycat.backend.postgresql.utils.PgPacketApaterUtils;\nimport io.mycat.buffer.BufferArray;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.net.handler.BackendAsyncHandler;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.fastjson.JSON;\n\npublic class PostgreSQLBackendConnectionHandler extends BackendAsyncHandler {\n\tstatic class SelectResponse {\n\t\tprivate List<DataRow> dataRows = new ArrayList<>();\n\n\t\tprivate RowDescription description;\n\n\t\tpublic SelectResponse(RowDescription description) {\n\t\t\tthis.description = description;\n\t\t}\n\n\t\tpublic void addDataRow(DataRow packet) {\n\t\t\tthis.dataRows.add(packet);\n\t\t}\n\n\t\tpublic List<DataRow> getDataRows() {\n\t\t\treturn dataRows;\n\t\t}\n\n\t\tpublic RowDescription getDescription() {\n\t\t\treturn description;\n\t\t}\n\n\t\tpublic void setDataRows(List<DataRow> dataRows) {\n\t\t\tthis.dataRows = dataRows;\n\t\t}\n\n\t}\n\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(PostgreSQLBackendConnection.class);\n\tprivate static final int RESULT_STATUS_INIT = 0;\n\n\tprivate byte packetId = 1;\n\n\t/*****\n\t * 每个后台响应有唯一的连接\n\t */\n\tprivate final PostgreSQLBackendConnection source;\n\t\n\t/**\n\t * 响应数据\n\t */\n\tprivate volatile SelectResponse response = null;\n\t\n\t/**\n\t * 响应状态\n\t */\n\tprivate int resultStatus;\n\n\tpublic PostgreSQLBackendConnectionHandler(PostgreSQLBackendConnection source) {\n\t\tthis.source = source;\n\t}\n\n\t/***\n\t * 进行连接处理\n\t * \n\t * @param con\n\t * @param buf\n\t * @param start\n\t * @param readedLength\n\t */\n\tprivate void doConnecting(PostgreSQLBackendConnection con, ByteBuffer buf,\n\t\t\tint start, int readedLength) {\n\t\ttry {\n\t\t\tList<PostgreSQLPacket> packets = PacketUtils.parsePacket(buf, 0,\n\t\t\t\t\treadedLength);\n\t\t\tLOGGER.debug(JSON.toJSONString(packets));\n\t\t\tif (!packets.isEmpty()\n\t\t\t\t\t&& packets.get(0) instanceof AuthenticationPacket) {\n\t\t\t\t// pg认证信息\n\t\t\t\t\tAuthenticationPacket packet = (AuthenticationPacket) packets\n\t\t\t\t\t\t\t.get(0);\n\t\t\t\t\tAuthType aut = packet.getAuthType();\n\t\t\t\t\tif (aut != AuthType.Ok) {\n\t\t\t\t\t\tPasswordMessage pak = new PasswordMessage(\n\t\t\t\t\t\t\t\tcon.getUser(), con.getPassword(), aut,\n\t\t\t\t\t\t\t\t((AuthenticationPacket) packet).getSalt());\n\t\t\t\t\t\t\n\t\t\t\t\t\tByteBuffer buffer = con.allocate(); //allocate(pak.getLength() + 1);\n\t\t\t\t\t\tpak.write(buffer);\n\t\t\t\t\t\t\n\t\t\t\t\t\tcon.write(buffer);\n\t\t\t\t\t} else {// 登入成功了....\n\n\t\t\t\t\t\tfor (int i = 1; i < packets.size(); i++) {\n\t\t\t\t\t\t\tPostgreSQLPacket _p = packets.get(i);\n\t\t\t\t\t\t\tif (_p instanceof BackendKeyData) {\n\t\t\t\t\t\t\t\tcon.setServerSecretKey(((BackendKeyData) _p)\n\t\t\t\t\t\t\t\t\t\t.getSecretKey());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tLOGGER.debug(\"SUCCESS Connected TO PostgreSQL , con id is {}\",con.getId());\n\t\t\t\t\t\tcon.setState(BackendConnectionState.connected);\n\t\t\t\t\t\tcon.getResponseHandler().connectionAcquired(con);// 连接已经可以用来\n\n\t\t\t\t\t}\n\n\n\t\t\t}\n\n\t\t} catch (IOException e) {\n\t\t\tLOGGER.error(\"error\",e);\n\t\t}\n\t}\n\n\t/***\n\t * 进行业务处理\n\t * \n\t * @param con\n\t * @param buf\n\t * @param start\n\t * @param readedLength\n\t */\n\tprivate void doHandleBusinessMsg(PostgreSQLBackendConnection con,\n\t\t\tByteBuffer buf, int start, int readedLength) {\n\t\ttry {\n\t\t\tList<PostgreSQLPacket> packets = PacketUtils.parsePacket(buf, 0,\n\t\t\t\t\treadedLength);\n\t\t\tif (packets == null || packets.isEmpty()) {\n\t\t\t\treturn ;\n\t\t\t\t//throw new RuntimeException(\"数据包解析出错\");\n\t\t\t}\n\t\t\t\n\t\t\tfor (PostgreSQLPacket packet : packets) {\n\t\t\t\tif (packet instanceof ErrorResponse) {\n\t\t\t\t\tdoProcessErrorResponse(con, (ErrorResponse) packet);\n\t\t\t\t} else if (packet instanceof RowDescription) {\n\t\t\t\t\tresponse = new SelectResponse((RowDescription) packet);\n\t\t\t\t} else if (packet instanceof DataRow) {\n\t\t\t\t\tresponse.addDataRow((DataRow) packet);\n\t\t\t\t} else if (packet instanceof ParameterStatus) {\n\t\t\t\t\tdoProcessParameterStatus(con, (ParameterStatus) packet);\n\t\t\t\t} else if (packet instanceof CommandComplete) {\n\t\t\t\t\tdoProcessCommandComplete(con, (CommandComplete) packet,\n\t\t\t\t\t\t\tresponse);\n\t\t\t\t} else if (packet instanceof NoticeResponse) {\n\t\t\t\t\tdoProcessNoticeResponse(con, (NoticeResponse) packet);\n\t\t\t\t} else if (packet instanceof ReadyForQuery) {\n\t\t\t\t\tdoProcessReadyForQuery(con, (ReadyForQuery) packet);\n\t\t\t\t} else if (packet instanceof NotificationResponse) {\n\t\t\t\t\tdoProcessNotificationResponse(con,\n\t\t\t\t\t\t\t(NotificationResponse) packet);\n\t\t\t\t} else if (packet instanceof CopyInResponse) {\n\t\t\t\t\tdoProcessCopyInResponse(con, (CopyInResponse) packet);\n\t\t\t\t} else if (packet instanceof CopyOutResponse) {\n\t\t\t\t\tdoProcessCopyOutResponse(con, (CopyOutResponse) packet);\n\t\t\t\t} else if (packet instanceof EmptyQueryResponse) {\n\t\t\t\t\tdoProcessEmptyQueryResponse(con,\n\t\t\t\t\t\t\t(EmptyQueryResponse) packet);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(\"处理出异常了\", e);\n\t\t\tErrorPacket err = new ErrorPacket();\n\t\t\terr.packetId = ++packetId;\n\t\t\terr.message = (\"内部服务器处理出错!\" + e.getMessage()).getBytes();\n\t\t\terr.errno = ErrorCode.ERR_NOT_SUPPORTED;\n\t\t\tResponseHandler respHand = con.getResponseHandler();\n\t\t\tif (respHand != null) {\n\t\t\t\trespHand.errorResponse(err.writeToBytes(), con);\n\t\t\t} else {\n\t\t\t LOGGER.error(\"{},respHand 为空\",this);\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/***************\n\t *  处理简单查询结果 ,每一个查询都是一件 CommandComplete 为结束\n\t * @param con PostgreSQL 后端连接\n\t * @param response\n\t * @param commandComplete\n     */\n\tprivate void doProcessBusinessQuery(PostgreSQLBackendConnection con,\n\t\t\tSelectResponse response, CommandComplete commandComplete) {\n\t\tRowDescription rowHd = response.getDescription();\n\t\tList<FieldPacket> fieldPks = PgPacketApaterUtils\n\t\t\t\t.rowDescConvertFieldPacket(rowHd);\n\t\tList<RowDataPacket> rowDatas = new ArrayList<>();\n\t\tfor (DataRow dataRow : response.getDataRows()) {\n\t\t\trowDatas.add(PgPacketApaterUtils\n\t\t\t\t\t.rowDataConvertRowDataPacket(dataRow));\n\t\t}\n\n\t\tBufferArray bufferArray = MycatServer.getInstance().getBufferPool()\n\t\t\t\t.allocateArray();\n\t\tResultSetHeaderPacket headerPkg = new ResultSetHeaderPacket();\n\t\theaderPkg.fieldCount = fieldPks.size();\n\t\theaderPkg.packetId = ++packetId;\n\t\theaderPkg.write(bufferArray);\n\n\t\tbyte[] header = bufferArray.writeToByteArrayAndRecycle();\n\n\t\tList<byte[]> fields = new ArrayList<byte[]>(fieldPks.size());\n\t\tIterator<FieldPacket> itor = fieldPks.iterator();\n\t\twhile (itor.hasNext()) {\n\t\t\tbufferArray = MycatServer.getInstance().getBufferPool()\n\t\t\t\t\t.allocateArray();\n\t\t\tFieldPacket curField = itor.next();\n\t\t\tcurField.packetId = ++packetId;\n\t\t\tcurField.write(bufferArray);\n\t\t\tbyte[] field = bufferArray.writeToByteArrayAndRecycle();\n\t\t\tfields.add(field);\n\t\t\titor.remove();\n\t\t}\n\n\t\tbufferArray = MycatServer.getInstance().getBufferPool().allocateArray();\n\t\tEOFPacket eofPckg = new EOFPacket();\n\t\teofPckg.packetId = ++packetId;\n\t\teofPckg.write(bufferArray);\n\t\tbyte[] eof = bufferArray.writeToByteArrayAndRecycle();\n\t\tif (con.getResponseHandler() != null) {\n\t\t\tcon.getResponseHandler().fieldEofResponse(header, fields, eof, con);\n\t\t} else {\n\t\t\tLOGGER.error(\"响应句柄为空\");\n\t\t}\n\t\t// output row\n\t\tfor (RowDataPacket curRow : rowDatas) {\n\t\t\tbufferArray = MycatServer.getInstance().getBufferPool()\n\t\t\t\t\t.allocateArray();\n\t\t\tcurRow.packetId = ++packetId;\n\t\t\tcurRow.write(bufferArray);\n\t\t\tbyte[] row = bufferArray.writeToByteArrayAndRecycle();\n\t\t\tcon.getResponseHandler().rowResponse(row, con);\n\t\t}\n\n\t\t// end row\n\t\tbufferArray = MycatServer.getInstance().getBufferPool().allocateArray();\n\t\teofPckg = new EOFPacket();\n\t\teofPckg.packetId = ++packetId;\n\t\teofPckg.write(bufferArray);\n\t\teof = bufferArray.writeToByteArrayAndRecycle();\n\t\tif (con.getResponseHandler() != null) {\n\t\t\tcon.getResponseHandler().rowEofResponse(eof, con);\n\t\t} else {\n\t\t\tLOGGER.error(\"响应句柄为空\");\n\t\t}\n\t}\n\n\tprivate void doProcessCommandComplete(PostgreSQLBackendConnection con,\n\t\t\tCommandComplete commandComplete, SelectResponse response) {\n\t\tif (commandComplete.isSelectComplete()) {\n\t\t\tif (response == null) {\n\t\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\t\"the select proess err ,the SelectResponse is empty\");\n\t\t\t}\n\t\t\tdoProcessBusinessQuery(con, response, commandComplete);\n\t\t} else {\n\t\t\tOkPacket okPck = new OkPacket();\n\t\t\t\n\t\t\tokPck.affectedRows =commandComplete.getAffectedRows();\n\t\t\tokPck.insertId =commandComplete.getInsertId();\n\t\t\tokPck.packetId = ++packetId;\n\t\t\tokPck.message = commandComplete.getCommandResponse().getBytes();\n\t\t\tcon.getResponseHandler().okResponse(okPck.writeToBytes(), con);\n\t\t}\n\t}\n\n\tprivate void doProcessCopyInResponse(PostgreSQLBackendConnection con,\n\t\t\tCopyInResponse packet) {\n\t\t// TODO(复制数据暂时不需要)\n\t}\n\n\tprivate void doProcessCopyOutResponse(PostgreSQLBackendConnection con,\n\t\t\tCopyOutResponse packet) {\n\t\t// TODO(复制数据暂时不需要)\n\t}\n\n\tprivate void doProcessEmptyQueryResponse(PostgreSQLBackendConnection con,\n\t\t\tEmptyQueryResponse packet) {\n\t\t// TODO(现阶段无空白sql)\n\t}\n\n\t/***\n\t * 处理查询出错数据包\n\t * \n\t * @param con\n\t * @param errorResponse\n\t */\n\tprivate void doProcessErrorResponse(PostgreSQLBackendConnection con,\n\t\t\tErrorResponse errorResponse) {\n\t\tLOGGER.debug(\"查询出错了!\");\n\t\tErrorPacket err = new ErrorPacket();\n\t\terr.packetId = ++packetId;\n\t\terr.message = errorResponse.getErrMsg().trim().replaceAll(\"\\0\", \" \")\n\t\t\t\t.getBytes();\n\t\terr.errno = ErrorCode.ER_UNKNOWN_ERROR;\n\t\tcon.getResponseHandler().errorResponse(err.writeToBytes(), con);\n\n\t}\n\n\t/******\n\t * 执行成功但是又警告信息\n\t * \n\t * @param con\n\t * @param noticeResponse\n\t */\n\tprivate void doProcessNoticeResponse(PostgreSQLBackendConnection con,\n\t\t\tNoticeResponse noticeResponse) {\n\t\t// TODO (通知提醒信息)\n\t}\n\n\tprivate void doProcessNotificationResponse(PostgreSQLBackendConnection con,\n\t\t\tNotificationResponse notificationResponse) {\n\t\t// TODO(后台参数改变通知)\n\t}\n\n\tprivate void doProcessParameterStatus(PostgreSQLBackendConnection con,\n\t\t\tParameterStatus parameterStatus) {\n\t\t// TODO(设置参数响应)\n\t}\n\n\n\t/****\n\t * PostgreSQL 已经处理完成一个任务等等下一个任务\n\t * @param con\n\t * @param readyForQuery\n     */\n\tprivate void doProcessReadyForQuery(PostgreSQLBackendConnection con,\n\t\t\tReadyForQuery readyForQuery) {\n\t\tif (con.isInTransaction() != (readyForQuery.getState() == TransactionState.IN)) {// 设置连接的后台事物状态\n\t\t\tcon.setInTransaction((readyForQuery.getState() == TransactionState.IN));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void handle(byte[] data) {\n\t\tofferData(data, source.getProcessor().getExecutor());\n\t}\n\n\t/*\n\t * 真正处理 数据库发过来的数据\n\t * \n\t * \n\t * \n\t * @see io.mycat.net.handler.BackendAsyncHandler#handleData(byte[])\n\t */\n\t@Override\n\tprotected void handleData(byte[] data) {\n\t\tByteBuffer theBuf = null;\n\t\ttry {\n\t\t\ttheBuf = source.allocate();\n\t\t\ttheBuf.put(data);\n\t\t\tswitch (source.getState()) {\n\t\t\tcase connecting: {\n\t\t\t\tdoConnecting(source, theBuf, 0, data.length);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase connected: {\n\t\t\t\ttry {\n\t\t\t\t\tdoHandleBusinessMsg(source, theBuf , 0,\n\t\t\t\t\t\t\tdata.length);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOGGER.warn(\"caught err of con \" + source, e);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tLOGGER.warn(\"not handled connecton state  err \"\n\t\t\t\t\t\t+ source.getState() + \" for con \" + source);\n\t\t\t\tbreak;\n\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(\"读取数据包出错\",e);\n\t\t}finally{\n\t\t\tif(theBuf!=null){\n\t\t\t\tsource.recycle(theBuf);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tprotected void offerDataError() {\n\t\tresultStatus = RESULT_STATUS_INIT;\n\t\tthrow new RuntimeException(\"offer data error!\");\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/PostgreSQLDataSource.java",
    "content": "package io.mycat.backend.postgresql;\n\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.heartbeat.DBHeartbeat;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.backend.postgresql.heartbeat.PostgreSQLHeartbeat;\nimport io.mycat.config.model.DBHostConfig;\nimport io.mycat.config.model.DataHostConfig;\n\nimport java.io.IOException;\n\n/*******************\n * PostgreSQL 后端数据源实现\n * @author Coollf\n *\n */\npublic class PostgreSQLDataSource extends PhysicalDatasource {\n\tprivate final PostgreSQLBackendConnectionFactory factory;\n\n\tpublic PostgreSQLDataSource(DBHostConfig config, DataHostConfig hostConfig,\n\t\t\tboolean isReadNode) {\n\t\tsuper(config, hostConfig, isReadNode);\n\t\tthis.factory = new PostgreSQLBackendConnectionFactory();\n\t}\n\n\t@Override\n\tpublic DBHeartbeat createHeartBeat() {\n\t\treturn new PostgreSQLHeartbeat(this);\n\t}\n\n\t@Override\n\tpublic void createNewConnection(ResponseHandler handler, String schema)\n\t\t\tthrows IOException {\n\t\tfactory.make(this, handler, schema);\n\t}\n\n\t@Override\n\tpublic boolean testConnection(String schema) throws IOException {\n\t\t// TODO Auto-generated method stub\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/heartbeat/PostgreSQLDetector.java",
    "content": "package io.mycat.backend.postgresql.heartbeat;\n\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.heartbeat.DBHeartbeat;\nimport io.mycat.backend.heartbeat.MySQLHeartbeat;\nimport io.mycat.backend.postgresql.PostgreSQLDataSource;\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.sqlengine.OneRawSQLQueryResultHandler;\nimport io.mycat.sqlengine.SQLJob;\nimport io.mycat.sqlengine.SQLQueryResult;\nimport io.mycat.sqlengine.SQLQueryResultListener;\nimport io.mycat.util.TimeUtil;\n\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\npublic class PostgreSQLDetector implements\n\t\tSQLQueryResultListener<SQLQueryResult<Map<String, String>>> {\n\n\tprivate static final String[] MYSQL_SLAVE_STAUTS_COLMS = new String[] {\n\t\t\t\"Seconds_Behind_Master\", \"Slave_IO_Running\", \"Slave_SQL_Running\" };\n\n\tprivate PostgreSQLHeartbeat heartbeat;\n\n\tprivate final AtomicBoolean isQuit;\n\n\tprivate volatile long heartbeatTimeout;\n\n\tprivate volatile long lastSendQryTime;\n\n\tprivate volatile SQLJob sqlJob;\n\n\tprivate long lasstReveivedQryTime;\n\n\tpublic PostgreSQLDetector(PostgreSQLHeartbeat heartbeat) {\n\t\tthis.heartbeat = heartbeat;\n\t\tthis.isQuit = new AtomicBoolean(false);\n\t}\n\n\t@Override\n\tpublic void onResult(SQLQueryResult<Map<String, String>> result) {\n\t\tif (result.isSuccess()) {\n\t\t\tint balance = heartbeat.getSource().getDbPool().getBalance();\n\t\t\tPhysicalDatasource source = heartbeat.getSource();\n\t\t\tMap<String, String> resultResult = result.getResult();\n\t\t\tif (source.getHostConfig().isShowSlaveSql()\n\t\t\t\t\t&& (source.getHostConfig().getSwitchType() == DataHostConfig.SYN_STATUS_SWITCH_DS || PhysicalDBPool.BALANCE_NONE != balance)) {\n\n\t\t\t\tString Slave_IO_Running = resultResult != null ? resultResult\n\t\t\t\t\t\t.get(\"Slave_IO_Running\") : null;\n\t\t\t\tString Slave_SQL_Running = resultResult != null ? resultResult\n\t\t\t\t\t\t.get(\"Slave_SQL_Running\") : null;\n\t\t\t\tif (Slave_IO_Running != null\n\t\t\t\t\t\t&& Slave_IO_Running.equals(Slave_SQL_Running)\n\t\t\t\t\t\t&& Slave_SQL_Running.equals(\"Yes\")) {\n\t\t\t\t\theartbeat.setDbSynStatus(DBHeartbeat.DB_SYN_NORMAL);\n\t\t\t\t\tString Seconds_Behind_Master = resultResult\n\t\t\t\t\t\t\t.get(\"Seconds_Behind_Master\");\n\t\t\t\t\tif (null != Seconds_Behind_Master\n\t\t\t\t\t\t\t&& !\"\".equals(Seconds_Behind_Master)) {\n\t\t\t\t\t\theartbeat.setSlaveBehindMaster(Integer\n\t\t\t\t\t\t\t\t.valueOf(Seconds_Behind_Master));\n\t\t\t\t\t}\n\t\t\t\t} else if (source.isSalveOrRead()) {\n\t\t\t\t\tMySQLHeartbeat.LOGGER\n\t\t\t\t\t\t\t.warn(\"found MySQL master/slave Replication err !!! \"\n\t\t\t\t\t\t\t\t\t+ heartbeat.getSource().getConfig());\n\t\t\t\t\theartbeat.setDbSynStatus(DBHeartbeat.DB_SYN_ERROR);\n\t\t\t\t}\n\n\t\t\t}\n\t\t\theartbeat.setResult(PostgreSQLHeartbeat.OK_STATUS, this, null);\n\t\t} else {\n\t\t\theartbeat.setResult(PostgreSQLHeartbeat.ERROR_STATUS, this, null);\n\t\t}\n\t\tlasstReveivedQryTime = System.currentTimeMillis();\n\t}\n\n\tpublic PostgreSQLHeartbeat getHeartbeat() {\n\t\treturn heartbeat;\n\t}\n\n\tpublic long getHeartbeatTimeout() {\n\t\treturn heartbeatTimeout;\n\t}\n\n\tpublic void heartbeat() {\n\t\tlastSendQryTime = System.currentTimeMillis();\n\t\tPostgreSQLDataSource ds = heartbeat.getSource();\n\t\tString databaseName = ds.getDbPool().getSchemas()[0];\n\t\tString[] fetchColms = {};\n\t\tif (heartbeat.getSource().getHostConfig().isShowSlaveSql()) {\n\t\t\tfetchColms = MYSQL_SLAVE_STAUTS_COLMS;\n\t\t}\n\t\tOneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler(\n\t\t\t\tfetchColms, this);\n\t\tsqlJob = new SQLJob(heartbeat.getHeartbeatSQL(), databaseName,\n\t\t\t\tresultHandler, ds);\n\t\tsqlJob.run();\n\t}\n\n\tpublic void close(String msg) {\n\t\tSQLJob curJob = sqlJob;\n\t\tif (curJob != null && !curJob.isFinished()) {\n\t\t\tcurJob.teminate(msg);\n\t\t\tsqlJob = null;\n\t\t}\n\t}\n\n\tpublic boolean isHeartbeatTimeout() {\n\t\treturn TimeUtil.currentTimeMillis() > Math.max(lastSendQryTime,\n\t\t\t\tlasstReveivedQryTime) + heartbeatTimeout;\n\t}\n\n\tpublic long getLastSendQryTime() {\n\t\treturn lastSendQryTime;\n\t}\n\n\tpublic long getLasstReveivedQryTime() {\n\t\treturn lasstReveivedQryTime;\n\t}\n\n\tpublic void quit() {\n\t}\n\n\tpublic boolean isQuit() {\n\t\treturn isQuit.get();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/heartbeat/PostgreSQLHeartbeat.java",
    "content": "package io.mycat.backend.postgresql.heartbeat;\r\n\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Date;\r\nimport java.util.concurrent.locks.ReentrantLock;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport io.mycat.backend.datasource.PhysicalDBPool;\r\nimport io.mycat.backend.datasource.PhysicalDatasource;\r\nimport io.mycat.backend.heartbeat.DBHeartbeat;\r\nimport io.mycat.backend.postgresql.PostgreSQLDataSource;\r\nimport io.mycat.config.model.DataHostConfig;\r\n\r\npublic class PostgreSQLHeartbeat extends DBHeartbeat {\r\n\r\n\tprivate static final int MAX_RETRY_COUNT = 5;\r\n\r\n\tpublic static final Logger LOGGER = LoggerFactory.getLogger(PostgreSQLHeartbeat.class);\r\n\r\n\tprivate PostgreSQLDataSource source;\r\n\r\n\tprivate ReentrantLock lock;\r\n\r\n\tprivate int maxRetryCount;\r\n\r\n\tprivate PostgreSQLDetector detector;\r\n\r\n\tpublic PostgreSQLHeartbeat(PostgreSQLDataSource source) {\r\n\t\tthis.source = source;\r\n\t\tthis.lock = new ReentrantLock(false);\r\n\t\tthis.maxRetryCount = MAX_RETRY_COUNT;\r\n\t\tthis.status = INIT_STATUS;\r\n\t\tthis.heartbeatSQL = source.getHostConfig().getHearbeatSQL();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void start() {\r\n\t\tfinal ReentrantLock lock = this.lock;\r\n\t\tlock.lock();\r\n\t\ttry {\r\n\t\t\tisStop.compareAndSet(true, false);\r\n\t\t\tsuper.status = DBHeartbeat.OK_STATUS;\r\n\t\t} finally {\r\n\t\t\tlock.unlock();\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void stop() {\r\n\t\tfinal ReentrantLock lock = this.lock;\r\n\t\tlock.lock();\r\n\t\ttry {\r\n\t\t\tif (isStop.compareAndSet(false, true)) {\r\n\t\t\t\tif (isChecking.get()) {\r\n\t\t\t\t\t// nothing\r\n\t\t\t\t} else {\r\n\t\t\t\t\tPostgreSQLDetector detector = this.detector;\r\n\t\t\t\t\tif (detector != null) {\r\n\t\t\t\t\t\tdetector.quit();\r\n\t\t\t\t\t\tisChecking.set(false);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} finally {\r\n\t\t\tlock.unlock();\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getLastActiveTime() {\r\n\t\tPostgreSQLDetector detector = this.detector;\r\n\t\tif (detector == null) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tlong t = detector.getLasstReveivedQryTime();\r\n\t\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\r\n\t\treturn sdf.format(new Date(t));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic long getTimeout() {\r\n\t\tPostgreSQLDetector detector = this.detector;\r\n\t\tif (detector == null) {\r\n\t\t\treturn -1L;\r\n\t\t}\r\n\t\treturn detector.getHeartbeatTimeout();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void heartbeat() {\r\n\t\tfinal ReentrantLock lock = this.lock;\r\n\t\tlock.lock();\r\n\t\ttry {\r\n\t\t\tif (isChecking.compareAndSet(false, true)) {\r\n\t\t\t\tPostgreSQLDetector detector = this.detector;\r\n\t\t\t\tif (detector == null || detector.isQuit()) {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tdetector = new PostgreSQLDetector(this);\r\n\t\t\t\t\t\tdetector.heartbeat();\r\n\t\t\t\t\t} catch (Exception e) {\r\n\t\t\t\t\t\tLOGGER.warn(source.getConfig().toString(), e);\r\n\t\t\t\t\t\tsetResult(ERROR_STATUS, detector, null);\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tthis.detector = detector;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tdetector.heartbeat();\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tPostgreSQLDetector detector = this.detector;\r\n\t\t\t\tif (detector != null) {\r\n\t\t\t\t\tif (detector.isQuit()) {\r\n\t\t\t\t\t\tisChecking.compareAndSet(true, false);\r\n\t\t\t\t\t} else if (detector.isHeartbeatTimeout()) {\r\n\t\t\t\t\t\tsetResult(TIMEOUT_STATUS, detector, null);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} finally {\r\n\t\t\tlock.unlock();\r\n\t\t}\r\n\t}\r\n\r\n\tpublic PostgreSQLDataSource getSource() {\r\n\t\treturn source;\r\n\t}\r\n\r\n\tpublic void setResult(int result, PostgreSQLDetector detector, Object attr) {\r\n\t\tthis.isChecking.set(false);\r\n\t\tswitch (result) {\r\n\t\tcase OK_STATUS:\r\n\t\t\tsetOk(detector);\r\n\t\t\tbreak;\r\n\t\tcase ERROR_STATUS:\r\n\t\t\tsetError(detector);\r\n\t\t\tbreak;\r\n\t\tcase TIMEOUT_STATUS:\r\n\t\t\tsetTimeout(detector);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (this.status != OK_STATUS) {\r\n\t\t\tswitchSourceIfNeed(\"heartbeat error\");\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void switchSourceIfNeed(String reason) {\r\n\t\tint switchType = source.getHostConfig().getSwitchType();\r\n\t\tif (switchType == DataHostConfig.NOT_SWITCH_DS) {\r\n\t\t\tif (LOGGER.isDebugEnabled()) {\r\n\t\t\t\tLOGGER.debug(\"not switch datasource ,for switchType is \" + DataHostConfig.NOT_SWITCH_DS);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tPhysicalDBPool pool = this.source.getDbPool();\r\n\t\tint curDatasourceHB = pool.getSource().getHeartbeat().getStatus();\r\n\t\t// read node can't switch ,only write node can switch\r\n\t\tif (pool.getWriteType() == PhysicalDBPool.WRITE_ONLYONE_NODE && !source.isReadNode()\r\n\t\t\t\t&& curDatasourceHB != DBHeartbeat.OK_STATUS && pool.getSources().length > 1) {\r\n\t\t\tsynchronized (pool) {\r\n\t\t\t\t// try to see if need switch datasource\r\n\t\t\t\tcurDatasourceHB = pool.getSource().getHeartbeat().getStatus();\r\n\t\t\t\tif (curDatasourceHB != DBHeartbeat.INIT_STATUS && curDatasourceHB != DBHeartbeat.OK_STATUS) {\r\n\t\t\t\t\tint curIndex = pool.getActivedIndex();\r\n\t\t\t\t\tint nextId = pool.next(curIndex);\r\n\t\t\t\t\tPhysicalDatasource[] allWriteNodes = pool.getSources();\r\n\t\t\t\t\twhile (true) {\r\n\t\t\t\t\t\tif (nextId == curIndex) {\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tPhysicalDatasource theSource = allWriteNodes[nextId];\r\n\t\t\t\t\t\tDBHeartbeat theSourceHB = theSource.getHeartbeat();\r\n\t\t\t\t\t\tint theSourceHBStatus = theSourceHB.getStatus();\r\n\t\t\t\t\t\tif (theSourceHBStatus == DBHeartbeat.OK_STATUS) {\r\n\t\t\t\t\t\t\tif (switchType == DataHostConfig.SYN_STATUS_SWITCH_DS) {\r\n\t\t\t\t\t\t\t\tif (Integer.valueOf(0).equals(theSourceHB.getSlaveBehindMaster())) {\r\n\t\t\t\t\t\t\t\t\tLOGGER.info(\"try to switch datasource ,slave is synchronized to master \"\r\n\t\t\t\t\t\t\t\t\t\t\t+ theSource.getConfig());\r\n\t\t\t\t\t\t\t\t\tpool.switchSourceOrVoted(nextId, true, reason);\r\n\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\t\t\tLOGGER.warn(\r\n\t\t\t\t\t\t\t\t\t\t\t\"ignored  datasource ,slave is not  synchronized to master , slave behind master :\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t+ theSourceHB.getSlaveBehindMaster() + \" \" + theSource.getConfig());\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\t\t// normal switch\r\n\t\t\t\t\t\t\t\tLOGGER.info(\"try to switch datasource ,not checked slave synchronize status \"\r\n\t\t\t\t\t\t\t\t\t\t+ theSource.getConfig());\r\n\t\t\t\t\t\t\t\tpool.switchSourceOrVoted(nextId, true, reason);\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tnextId = pool.next(nextId);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void setTimeout(PostgreSQLDetector detector) {\r\n\t\tthis.isChecking.set(false);\r\n\t\tstatus = DBHeartbeat.TIMEOUT_STATUS;\r\n\t}\r\n\r\n\tprivate void setError(PostgreSQLDetector detector) {\r\n\t\t// should continues check error status\r\n\t\tif (\t\t\tthis.errorCount.incrementAndGet() < maxRetryCount) {\r\n\r\n\t\t\tif (detector != null && !detector.isQuit()) {\r\n\t\t\t\theartbeat(); // error count not enough, heart beat again\r\n\t\t\t}\r\n\t\t\t// return;\r\n\t\t} else {\r\n\t\t\tif (detector != null) {\r\n\t\t\t\tdetector.quit();\r\n\t\t\t}\r\n\r\n\t\t\tthis.status = ERROR_STATUS;\r\n\t\t\tthis.errorCount.set(0);;\r\n\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void setOk(PostgreSQLDetector detector) {\r\n\t\trecorder.set(detector.getLasstReveivedQryTime() - detector.getLastSendQryTime());\r\n\t\tswitch (status) {\r\n\t\tcase DBHeartbeat.TIMEOUT_STATUS:\r\n\t\t\tthis.status = DBHeartbeat.INIT_STATUS;\r\n\t\t\tthis.errorCount.set(0);;\r\n\t\t\tif (isStop.get()) {\r\n\t\t\t\tdetector.quit();\r\n\t\t\t} else {\r\n\t\t\t\theartbeat();// timeout, heart beat again\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase DBHeartbeat.OK_STATUS:\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tthis.status = OK_STATUS;\r\n\t\t\tthis.errorCount.set(0);;\r\n\t\t}\r\n\t\tif (isStop.get()) {\r\n\t\t\tdetector.quit();\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/package-info.java",
    "content": "/**\r\n * @author Coollf\r\n *\r\n */\r\npackage io.mycat.backend.postgresql;\r\n/*\r\n\r\npostgresql mycat 相关支持\r\n\r\nconfig demo\r\n============================================================================================================\r\n\r\n\r\n    <schema-config>\r\n        <schema name=\"mycat\" checkSQLschema=\"true\" sqlMaxLimit=\"100\" dataNode=\"dn1\" />        \t\r\n\t\t<dataNode name=\"dn1\" dataHost=\"pghost\" database=\"mycat\" />      \r\n\t    <dataHost name=\"pghost\" maxCon=\"100\" minCon=\"5\" balance=\"1\" \r\n\t       writeType=\"0\" dbType=\"PostgreSQL\" dbDriver=\"native\" switchType=\"1\">\r\n\t\t\t<heartbeat>select 1</heartbeat>\r\n\t\t\t<writeHost host=\"host_a\" url=\"localhost:5432\" user=\"postgres\"\r\n\t\t\t\tpassword=\"coollf\"/>\r\n\t\t</dataHost>\t\t\r\n    </schema-config>\r\n\r\n=============================================================================================================\r\n\r\n\r\n*/"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/AuthenticationPacket.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\nimport io.mycat.backend.postgresql.utils.PIOUtils;\r\n\r\npublic class AuthenticationPacket extends PostgreSQLPacket {\r\n\tpublic static enum AuthType {\r\n\t\tOk(0), KerberosV5(2), CleartextPassword(3), CryptPassword(4), MD5Password(5), SCMCredential(6);\r\n\r\n\t\tprivate int value;\r\n\r\n\t\tAuthType(int v) {\r\n\t\t\tthis.value = v;\r\n\t\t}\r\n\r\n\t\tpublic int getValue() {\r\n\t\t\treturn value;\r\n\t\t}\r\n\r\n\t\tpublic static AuthType valueOf(int v) {\r\n\t\t\tif (v == Ok.value) {\r\n\t\t\t\treturn Ok;\r\n\t\t\t}\r\n\t\t\tif (v == KerberosV5.value) {\r\n\t\t\t\treturn KerberosV5;\r\n\t\t\t}\r\n\t\t\tif (v == CleartextPassword.value) {\r\n\t\t\t\treturn CleartextPassword;\r\n\t\t\t}\r\n\t\t\tif (v == MD5Password.value) {\r\n\t\t\t\treturn MD5Password;\r\n\t\t\t}\r\n\t\t\tif (v == SCMCredential.value) {\r\n\t\t\t\treturn SCMCredential;\r\n\t\t\t}\r\n\r\n\t\t\treturn null;\r\n\t\t}\r\n\t}\r\n\r\n\t/***\r\n\t * 标记\r\n\t */\r\n\tprivate char marker = PacketMarker.B_Auth.getValue();\r\n\r\n\t/****\r\n\t * 数据包长度\r\n\t */\r\n\tprivate int length;\r\n\r\n\t/***\r\n\t * 盐粒\r\n\t */\r\n\tprivate byte[] salt;\r\n\r\n\tprivate AuthType authType;\r\n\r\n\tpublic AuthType getAuthType() {\r\n\t\treturn authType;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn marker;\r\n\t}\r\n\r\n\tpublic byte[] getSalt() {\r\n\t\treturn salt;\r\n\t}\r\n\r\n\tpublic void setSalt(byte[] salt) {\r\n\t\tthis.salt = salt;\r\n\t}\r\n\r\n\tpublic static AuthenticationPacket parse(ByteBuffer buffer, int offset){\r\n\t\tif (buffer.get(offset) != PacketMarker.B_Auth.getValue()) {\r\n\t\t\tthrow new IllegalArgumentException(\"this packetData not is AuthenticationPacket\");\r\n\t\t}\r\n\t\tAuthenticationPacket packet = new AuthenticationPacket();\r\n\t\tpacket.length = PIOUtils.redInteger4(buffer, offset + 1);\r\n\t\tpacket.authType = AuthType.valueOf(PIOUtils.redInteger4(buffer, offset + 1 + 4));\r\n\t\tif (packet.authType == AuthType.MD5Password) {\r\n\t\t\tpacket.salt = PIOUtils.redByteArray(buffer, offset + 1 + 4 + 4, 4);\r\n\t\t}\r\n\t\treturn packet;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/BackendKeyData.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\n/**\r\n * 后端数据包信息\r\n * \r\n * @author Coollf\r\n *\r\n */\r\n\r\n// BackendKeyData (B)\r\n// Byte1('K')\r\n// 标识该消息是一个取消键字数据。 如果前端希望能够在稍后发出 CancelRequest 消息， 那么它必须保存这个值。\r\n//\r\n// Int32(12)\r\n// 以字节记的消息内容的长度，包括长度本身。\r\n//\r\n// Int32\r\n// 后端的进程号（PID）。\r\n//\r\n// Int32\r\n// 此后端的密钥（secret key ）。\r\npublic class BackendKeyData extends PostgreSQLPacket {\r\n\t/**\r\n\t * 长度\r\n\t */\r\n\tprivate int length;\r\n\r\n\t/***\r\n\t * 进程ID\r\n\t */\r\n\tprivate int pid;\r\n\r\n\t/***\r\n\t * 此后端的密钥（secret key ）\r\n\t */\r\n\tprivate int secretKey;\r\n\r\n\tpublic int getPid() {\r\n\t\treturn pid;\r\n\t}\r\n\r\n\tpublic int getSecretKey() {\r\n\t\treturn secretKey;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn PacketMarker.B_BackendKey.getValue();\r\n\t}\r\n\r\n\t/***\r\n\t * 解析数据包\r\n\t * \r\n\t * @param buffer\r\n\t * @param offset\r\n\t * @return\r\n\t * @throws IllegalArgumentException\r\n\t */\r\n\tpublic static BackendKeyData parse(ByteBuffer buffer, int offset) {\r\n\t\tif (buffer.get(offset) != PacketMarker.B_BackendKey.getValue()) {\r\n\t\t\tthrow new IllegalArgumentException(\"this packet not is BackendKeyData\");\r\n\t\t}\r\n\t\tBackendKeyData pac = new BackendKeyData();\r\n\t\tpac.length = buffer.getInt(offset + 1);\r\n\t\tpac.pid = buffer.getInt(offset + 1 + 4);\r\n\t\tpac.secretKey = buffer.getInt(offset + 1 + 4 + 4);\r\n\t\treturn pac;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/Bind.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\n\r\n\r\n//\t\tBind (F)\r\n//\t\tByte1('B')\r\n//\t\t标识该信息是一个绑定命令。\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t以字节记的消息内容的长度，包括长度本身。\r\n//\t\t\r\n//\t\tString\r\n//\t\t目标入口的名字（空字串则选取未命名的入口）。\r\n//\t\t\r\n//\t\tString\r\n//\t\t源准备好语句的名字（空字串则选取未命名的准备好语句）。\r\n//\t\t\r\n//\t\tInt16\r\n//\t\t\r\n//\t\t\t后面跟着的参数格式代码的数目（在下面的 C 中说明）。 这个数值可以是零，表示没有参数，或者是参数都使用缺省格式（文本）； 或者是一，这种情况下声明的格式代码应用于所有参数；或者它可以等于实际数目的参数。\r\n//\t\t\r\n//\t\tInt16[C]\r\n//\t\t参数格式代码。目前每个都必须是零（文本）或者一（二进制）。\r\n//\t\t\r\n//\t\tInt16\r\n//\t\t后面跟着的参数值的数目（可能为零）。这些必须和查询需要的参数个数匹配。\r\n//\t\t\r\n//\t\t然后，每个参数都会出现下面的字段对：\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t参数值的长度，以字节记（这个长度并不包含长度本身）。可以为零。 一个特殊的情况是，-1 表示一个 NULL 参数值。在 NULL 的情况下， 后面不会跟着数值字节。\r\n//\t\t\r\n//\t\tByten\r\n//\t\t参数值，格式是关联的格式代码标明的。n 是上面的长度。\r\n//\t\t\r\n//\t\t在最后一个参数之后，出现下面的字段：\r\n//\t\t\r\n//\t\tInt16\r\n//\t\t\r\n//\t\t\t后面跟着的结果字段格式代码数目（下面的 R 描述）。 这个数目可以是零表示没有结果字段，或者结果字段都使用缺省格式（文本）； 或者是一，这种情况下声明格式代码应用于所有结果字段（如果有的话）；或者它可以等于查询的结果字段的实际数目。\r\n//\t\t\r\n//\t\tInt16[R]\r\n//\t\t结果字段格式代码。目前每个必须是零（文本）或者一（二进制）。\r\n\r\n\r\npublic class Bind extends PostgreSQLPacket {\r\n\r\n\tpublic static class DataParameter {\r\n\t\t/**\r\n\t\t * 字段值的长度，以字节记（这个长度不包括它自己）。 可以为零。一个特殊的情况是，-1 表示一个 NULL 的字段值。 在 NULL\r\n\t\t * 的情况下就没有跟着数据字段。\r\n\t\t */\r\n\t\tprivate int length;\r\n\t\tprivate byte[] data;\r\n\r\n\t\tprivate boolean isNull;\r\n\r\n\t\t/**\r\n\t\t * @return the length\r\n\t\t */\r\n\t\tpublic int getLength() {\r\n\t\t\treturn length;\r\n\t\t}\r\n\r\n\t\t/**\r\n\t\t * @param length the length to set\r\n\t\t */\r\n\t\tpublic void setLength(int length) {\r\n\t\t\tthis.length = length;\r\n\t\t}\r\n\r\n\t\t/**\r\n\t\t * @return the data\r\n\t\t */\r\n\t\tpublic byte[] getData() {\r\n\t\t\treturn data;\r\n\t\t}\r\n\r\n\t\t/**\r\n\t\t * @param data the data to set\r\n\t\t */\r\n\t\tpublic void setData(byte[] data) {\r\n\t\t\tthis.data = data;\r\n\t\t}\r\n\r\n\t\t/**\r\n\t\t * @return the isNull\r\n\t\t */\r\n\t\tpublic boolean isNull() {\r\n\t\t\treturn isNull;\r\n\t\t}\r\n\r\n\t\t/**\r\n\t\t * @param isNull the isNull to set\r\n\t\t */\r\n\t\tpublic void setNull(boolean isNull) {\r\n\t\t\tthis.isNull = isNull;\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tprivate char marker;\r\n\tprivate int length;\r\n\tprivate String name;\r\n\tprivate String sql;\r\n\tprivate short parameterProtocolNumber;\r\n\tprivate DataProtocol[] parameterProtocol;\r\n\tprivate short parameterNumber;\r\n\tprivate DataParameter[] parameter;\t\r\n\tprivate short resultNumber;\t\r\n\tprivate DataProtocol[] resultProtocol;\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn marker;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the name\r\n\t */\r\n\tpublic String getName() {\r\n\t\treturn name;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the sql\r\n\t */\r\n\tpublic String getSql() {\r\n\t\treturn sql;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the parameterProtocolNumber\r\n\t */\r\n\tpublic short getParameterProtocolNumber() {\r\n\t\treturn parameterProtocolNumber;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the parameterProtocol\r\n\t */\r\n\tpublic DataProtocol[] getParameterProtocol() {\r\n\t\treturn parameterProtocol;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the parameterNumber\r\n\t */\r\n\tpublic short getParameterNumber() {\r\n\t\treturn parameterNumber;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the parameter\r\n\t */\r\n\tpublic DataParameter[] getParameter() {\r\n\t\treturn parameter;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the resultNumber\r\n\t */\r\n\tpublic short getResultNumber() {\r\n\t\treturn resultNumber;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the resultProtocol\r\n\t */\r\n\tpublic DataProtocol[] getResultProtocol() {\r\n\t\treturn resultProtocol;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/BindComplete.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\nimport io.mycat.backend.postgresql.utils.PIOUtils;\r\n\r\n//\t\tBindComplete (B)\r\n//\t\tByte1('2')\r\n//\t\t标识消息为一个绑定结束标识符。\r\n//\t\t\r\n//\t\tInt32(4)\r\n//\t\t以字节记的消息长度，包括长度本身。\r\n\r\n\r\n/***\r\n * 绑定预编译sql成功\r\n * @author Coollf\r\n *\r\n */\r\npublic class BindComplete extends PostgreSQLPacket {\r\n\tprivate char marker = PacketMarker.B_BindComplete.getValue();\r\n\tprivate int length;\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn marker;\r\n\t}\r\n\r\n\tpublic static BindComplete parse(ByteBuffer buffer, int offset) {\r\n\t\tif ((char) buffer.get(offset) != PacketMarker.B_BindComplete.getValue()) {\r\n\t\t\tthrow new IllegalArgumentException(\r\n\t\t\t\t\t\"this packet not is BindComplete\");\r\n\t\t}\r\n\t\tBindComplete parse = new BindComplete();\r\n\t\tparse.length = PIOUtils.redInteger4(buffer, offset + 1);\r\n\t\treturn parse;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/CancelRequest.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\nimport io.mycat.backend.postgresql.utils.PIOUtils;\r\n\r\n//\t\tCancelRequest (F)\r\n//\t\tInt32(16)\r\n//\t\t以字节计的消息长度。包括长度本身。\r\n//\t\t\r\n//\t\tInt32(80877102)\r\n//\t\t取消请求代码。选这个值是为了在高16位包含 1234， 低16位包含 5678。（为避免混乱，这个代码必须与协议版本号不同．）\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t目标后端的进程号（PID）。\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t目标后端的密钥（secret key）。\r\n\r\n/***\r\n * 取消请求\r\n * \r\n * @author Coollf\r\n *\r\n */\r\npublic class CancelRequest extends PostgreSQLPacket {\r\n\tprivate int length = 16;\r\n\tprivate int cancelCode = 80877102;\r\n\tprivate int pid;\r\n\tprivate int secretKey;\r\n\r\n\tpublic CancelRequest(int pid, int secretKey) {\r\n\t\tthis.pid = pid;\r\n\t\tthis.secretKey = secretKey;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tpublic void write(ByteBuffer buffer) {\r\n\t\tPIOUtils.SendInteger4(length, buffer);\r\n\t\tPIOUtils.SendInteger4(cancelCode, buffer);\r\n\t\tPIOUtils.SendInteger4(pid, buffer);\r\n\t\tPIOUtils.SendInteger4(secretKey, buffer);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/CommandComplete.java",
    "content": "package io.mycat.backend.postgresql.packet;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.postgresql.utils.PIOUtils;\n\n//\t\tCommandComplete (B)\n//\t\tByte1('C')\n//\t\t标识此消息是一个命令结束响应。\n//\t\t\n//\t\tInt32\n//\t\t以字节记的消息内容的长度，包括长度本身。\n//\t\t\n//\t\tString\n//\t\t命令标记。它通常是一个单字，标识那个命令完成。\n//\t\t\n//\t\t对于INSERT命令，标记是INSERT oid rows， 这里的rows是插入的行数。oid 在row为 1 并且目标表有 OID 的时候是插入行的对象 ID； 否则oid就是 0。\n//\t\t\n//\t\t对于DELETE 命令，标记是 DELETE rows， 这里的 rows 是删除的行数。\n//\t\t\n//\t\t对于 UPDATE 命令，标记是 UPDATE rows 这里的 rows 是更新的行数。\n//\t\t\n//\t\t对于 MOVE 命令，标记是 MOVE rows，这里的 rows 是游标未知改变的行数。\n//\t\t\n//\t\t对于 FETCH 命令，标记是 FETCH rows，这里的 rows 是从游标中检索出来的行数。\npublic class CommandComplete extends PostgreSQLPacket {\n\n\tprivate int length;\n\n\t/**\n\t * 命令\n\t */\n\tprivate String commandResponse;\n\n\t// 存储状态。\n\n\tpublic int getAffectedRows() {\n\t\treturn affectedRows;\n\t}\n\n\tpublic void setAffectedRows(int affectedRows) {\n\t\tthis.affectedRows = affectedRows;\n\t}\n\n\tpublic int getInsertId() {\n\t\treturn insertId;\n\t}\n\n\tpublic void setInsertId(int insertId) {\n\t\tthis.insertId = insertId;\n\t}\n\n\t// 修改影响条数\n\tprivate int affectedRows = 0;\n\n\t// 插入ID\n\tprivate int insertId = 0;\n\n\t@Override\n\tpublic int getLength() {\n\t\treturn length;\n\t}\n\n\tpublic boolean isDDLComplete() {\n\t\treturn commandResponse != null && (commandResponse.startsWith(\"INSERT\") || commandResponse.startsWith(\"DELETE\")\n\t\t\t\t|| commandResponse.startsWith(\"UPDATE\"));\n\t}\n\n\tpublic boolean isTranComplete() {\n\t\treturn commandResponse != null\n\t\t\t\t&& (commandResponse.startsWith(\"ROLLBACK\") || commandResponse.startsWith(\"COMMIT\"));\n\t}\n\n\tpublic boolean isSelectComplete() {\n\t\treturn commandResponse != null && (commandResponse.startsWith(\"SELECT\"));\n\t}\n\n\tpublic int getRows() {\n\t\tif (!isDDLComplete()) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (commandResponse != null) {\n\t\t\tString[] s = commandResponse.split(\" +\");\n\t\t\tif (s.length == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn Integer.valueOf(s[s.length - 1].trim());\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic char getMarker() {\n\t\treturn PacketMarker.B_CommandComplete.getValue();\n\t}\n\n\tpublic static CommandComplete parse(ByteBuffer buffer, int offset) {\n\t\tif (buffer.get(offset) != PacketMarker.B_CommandComplete.getValue()) {\n\t\t\tthrow new IllegalArgumentException(\"this packetData not is CommandComplete\");\n\t\t}\n\t\tCommandComplete packet = new CommandComplete();\n\t\tpacket.length = PIOUtils.redInteger4(buffer, offset + 1);\n\t\tpacket.commandResponse = new String(PIOUtils.redByteArray(buffer, offset + 1 + 4, packet.length - 4), UTF8)\n\t\t\t\t.trim();\n\t\tif (packet.commandResponse.startsWith(\"INSERT\")) {\n\t\t\tString vs[] = packet.commandResponse.replace(\"INSERT\", \"\").trim().split(\" +\");\n\t\t\tpacket.insertId = parseInt(vs[0]);\n\t\t\t\n\t\t\tpacket.affectedRows =parseInt(vs[1]);\n\t\t} else if (packet.commandResponse.startsWith(\"UPDATE\")) {\n\t\t\tpacket.affectedRows = parseInt(packet.commandResponse.replace(\"UPDATE\", \"\").trim());\n\t\t}else if(packet.commandResponse.startsWith(\"DELETE\")){\n\t\t\tpacket.affectedRows = parseInt(packet.commandResponse.replace(\"DELETE\", \"\").trim());\n\t\t}\n\t\treturn packet;\n\n\t}\n\t\n\t\n\n\tprivate static int parseInt(String value) {\n\t\ttry{\n\t\t\treturn Integer.parseInt(value);\n\t\t}catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn 0;\n\t}\n\n\tpublic String getCommandResponse() {\n\t\treturn commandResponse;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/CopyInResponse.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n//\t\tCopyInResponse (B)\r\n//\t\tByte1('G')\r\n//\t\t标识这条消息是一条 Start Copy In （开始拷贝进入）响应消息。 前端现在必须发送一条拷贝入数据。（如果还没准备好做这些事情， 那么发送一条 CopyFail 消息）。\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t以字节记的消息内容的长度，包括长度本身。\r\n//\t\t\r\n//\t\tInt8\r\n//\t\t0 表示全部的 COPY 格式都是文本的（数据行由换行符分隔，字段由分隔字符分隔等等）。 1 表示全部 COPY 格式都是二进制的（类似 DataRow 格式）。 参阅 COPY\t获取更多信息。\r\n//\t\t\r\n//\t\tInt16\r\n//\t\t数据中要拷贝的字段数（由下面的 N 解释）。\r\n//\t\t\r\n//\t\tInt16[N]\r\n//\t\t每个字段将要用的格式代码，目前每个都必须是零（文本）或者一（二进制）。 如果全部拷贝格式都是文本的，那么所有的都必须是零。\r\n\r\nimport io.mycat.backend.postgresql.utils.PIOUtils;\r\n\r\n/***\r\n * 拷贝数据开始\r\n * \r\n * @author Coollf\r\n *\r\n */\r\npublic class CopyInResponse extends PostgreSQLPacket {\r\n\t/**\r\n\t * 标示\r\n\t */\r\n\tprivate char marker = PacketMarker.B_CopyInResponse.getValue();\r\n\r\n\t/**\r\n\t * 长度\r\n\t */\r\n\tprivate int length;\r\n\r\n\t/**\r\n\t * 拷贝协议, 0 文本, 1 二进制\r\n\t */\r\n\tprivate DataProtocol protocol;\r\n\r\n\t/***\r\n\t * 拷贝的数据字段数\r\n\t */\r\n\tprivate short dataLength;\r\n\r\n\t/**\r\n\t * @return the protocol\r\n\t */\r\n\tpublic DataProtocol getProtocol() {\r\n\t\treturn protocol;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the dataLength\r\n\t */\r\n\tpublic short getDataLength() {\r\n\t\treturn dataLength;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the columnType\r\n\t */\r\n\tpublic DataProtocol[] getColumnType() {\r\n\t\treturn columnType;\r\n\t}\r\n\r\n\t/**\r\n\t * 要拷贝数据列的类型  Int16[N]\r\n\t */\r\n\tprivate DataProtocol[] columnType;\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn marker;\r\n\t}\r\n\r\n\tpublic static CopyInResponse parse(ByteBuffer buffer, int offset) {\r\n\r\n\t\tif (buffer.get(offset) != PacketMarker.B_CopyInResponse.getValue()) {\r\n\t\t\tthrow new IllegalArgumentException(\r\n\t\t\t\t\t\"this packetData not is CopyInResponse\");\r\n\t\t}\r\n\t\tint _offset = offset + 1;\r\n\t\tCopyInResponse pack = new CopyInResponse();\r\n\t\tpack.length = PIOUtils.redInteger4(buffer, _offset);\r\n\t\t_offset += 4;\r\n\t\tpack.protocol = DataProtocol.valueOf(PIOUtils.redInteger1(buffer,\r\n\t\t\t\t_offset));\r\n\t\t_offset += 1;\r\n\t\tpack.dataLength = PIOUtils.redInteger2(buffer, _offset);\r\n\t\t_offset += 2;\r\n\t\tpack.columnType = new DataProtocol[pack.dataLength];\r\n\t\tfor (int i = 0; i < pack.columnType.length; i++) {\r\n\t\t\tpack.columnType[i] = DataProtocol.valueOf(PIOUtils.redInteger2(\r\n\t\t\t\t\tbuffer, _offset));\r\n\t\t\t_offset += 2;\r\n\t\t}\r\n\t\treturn pack;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/CopyOutResponse.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\nimport io.mycat.backend.postgresql.utils.PIOUtils;\r\n\r\n//\t\tCopyOutResponse (B)\r\n//\t\tByte1('H')\r\n//\t\t标识这条消息是一条 Start Copy Out （开始拷贝进出）响应消息。 这条消息后面将跟着一条拷贝出数据消息。\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t以字节记的消息内容的长度，包括它自己。\r\n//\t\t\r\n//\t\tInt8\r\n//\t\t0 表示全部拷贝格式都是文本（数据行由换行符分隔， 字段由分隔字符分隔等等）。1 表示所有拷贝格式都是二进制的（类似于 DataRow 格式）。参阅 COPY 获取更多信息。\r\n//\t\t\r\n//\t\tInt16\r\n//\t\t要拷贝的数据的字段的数目（在下面的 N 说明）。\r\n//\t\t\r\n//\t\tInt16[N]\r\n//\t\t每个字段要试用的格式代码。目前每个都必须是零（文本）或者一（二进制）。 如果全部的拷贝格式都是文本，那么所有的都必须是零。\r\npublic class CopyOutResponse extends PostgreSQLPacket {\r\n\t/**\r\n\t * 标示\r\n\t */\r\n\tprivate char marker = PacketMarker.B_CopyOutResponse.getValue();\r\n\r\n\t/**\r\n\t * 长度\r\n\t */\r\n\tprivate int length;\r\n\r\n\t/**\r\n\t * @return the marker\r\n\t */\r\n\tpublic char getMarker() {\r\n\t\treturn marker;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the length\r\n\t */\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the protocol\r\n\t */\r\n\tpublic DataProtocol getProtocol() {\r\n\t\treturn protocol;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the dataLength\r\n\t */\r\n\tpublic short getDataLength() {\r\n\t\treturn dataLength;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the columnType\r\n\t */\r\n\tpublic DataProtocol[] getColumnType() {\r\n\t\treturn columnType;\r\n\t}\r\n\r\n\t/**\r\n\t * 拷贝协议, 0 文本, 1 二进制\r\n\t */\r\n\tprivate DataProtocol protocol;\r\n\r\n\t/***\r\n\t * 拷贝的数据字段数\r\n\t */\r\n\tprivate short dataLength;\r\n\r\n\t/**\r\n\t * 要拷贝数据列的类型 Int16[N]\r\n\t */\r\n\tprivate DataProtocol[] columnType;\r\n\r\n\tpublic static CopyOutResponse parse(ByteBuffer buffer, int offset) {\r\n\r\n\t\tif (buffer.get(offset) != PacketMarker.B_CopyOutResponse.getValue()) {\r\n\t\t\tthrow new IllegalArgumentException(\r\n\t\t\t\t\t\"this packetData not is CopyInResponse\");\r\n\t\t}\r\n\t\tint _offset = offset + 1;\r\n\t\tCopyOutResponse pack = new CopyOutResponse();\r\n\t\tpack.length = PIOUtils.redInteger4(buffer, _offset);\r\n\t\t_offset += 4;\r\n\t\tpack.protocol = DataProtocol.valueOf(PIOUtils.redInteger1(buffer,\r\n\t\t\t\t_offset));\r\n\t\t_offset += 1;\r\n\t\tpack.dataLength = PIOUtils.redInteger2(buffer, _offset);\r\n\t\t_offset += 2;\r\n\t\tpack.columnType = new DataProtocol[pack.dataLength];\r\n\t\tfor (int i = 0; i < pack.columnType.length; i++) {\r\n\t\t\tpack.columnType[i] = DataProtocol.valueOf(PIOUtils.redInteger2(\r\n\t\t\t\t\tbuffer, _offset));\r\n\t\t\t_offset += 2;\r\n\t\t}\r\n\t\treturn pack;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/DataRow.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\nimport io.mycat.backend.postgresql.utils.PIOUtils;\r\n\r\n//\t\tDataRow (B)\r\n//\t\tByte1('D')\r\n//\t\t标识这个消息是一个数据行。\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t以字节记的消息内容的长度，包括长度自身。\r\n//\t\t\r\n//\t\tInt16\r\n//\t\t后面跟着的字段值的个数（可能是零）。\r\n//\t\t\r\n//\t\t然后，每个字段都会出现下面的数据域对：\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t字段值的长度，以字节记（这个长度不包括它自己）。 可以为零。一个特殊的情况是，-1 表示一个 NULL 的字段值。 在 NULL 的情况下就没有跟着数据字段。\r\n//\t\t\r\n//\t\tByten\r\n//\t\t一个字段的数值，以相关的格式代码表示的格式展现。 n 是上面的长度。\r\npublic class DataRow extends PostgreSQLPacket {\r\n\tpublic static class DataColumn {\r\n\t\t/**\r\n\t\t * 字段值的长度，以字节记（这个长度不包括它自己）。 可以为零。一个特殊的情况是，-1 表示一个 NULL 的字段值。 在 NULL\r\n\t\t * 的情况下就没有跟着数据字段。\r\n\t\t */\r\n\t\tprivate int length;\r\n\t\tprivate byte[] data;\r\n\r\n\t\tprivate boolean isNull;\r\n\r\n\t\t/**\r\n\t\t * @return the isNull\r\n\t\t */\r\n\t\tpublic boolean isNull() {\r\n\t\t\treturn isNull;\r\n\t\t}\r\n\r\n\t\t/**\r\n\t\t * @return the length\r\n\t\t */\r\n\t\tpublic int getLength() {\r\n\t\t\treturn length;\r\n\t\t}\r\n\r\n\t\t/**\r\n\t\t * @param length\r\n\t\t *            the length to set\r\n\t\t */\r\n\t\tpublic void setLength(int length) {\r\n\t\t\tthis.length = length;\r\n\t\t}\r\n\r\n\t\t/**\r\n\t\t * @return the data\r\n\t\t */\r\n\t\tpublic byte[] getData() {\r\n\t\t\treturn data;\r\n\t\t}\r\n\r\n\t\t/**\r\n\t\t * @param data\r\n\t\t *            the data to set\r\n\t\t */\r\n\t\tpublic void setData(byte[] data) {\r\n\t\t\tthis.data = data;\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * 标准\r\n\t */\r\n\tprivate char marker = PacketMarker.B_DataRow.getValue();\r\n\r\n\t/**\r\n\t * 长度\r\n\t */\r\n\tprivate int length;\r\n\r\n\t/**\r\n\t * 列数\r\n\t */\r\n\tprivate short columnNumber;\r\n\r\n\t/**\r\n\t * @return the columnNumber\r\n\t */\r\n\tpublic short getColumnNumber() {\r\n\t\treturn columnNumber;\r\n\t}\r\n\r\n\t/**\r\n\t * @return the columns\r\n\t */\r\n\tpublic DataColumn[] getColumns() {\r\n\t\treturn columns;\r\n\t}\r\n\r\n\t/**\r\n\t * 数据列\r\n\t */\r\n\tprivate DataColumn[] columns;\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn marker;\r\n\t}\r\n\r\n\tpublic static DataRow parse(ByteBuffer buffer, int offset) {\r\n\r\n\t\tif (buffer.get(offset) != PacketMarker.B_DataRow.getValue()) {\r\n\t\t\tthrow new IllegalArgumentException(\"this packetData not is DataRow\");\r\n\t\t}\r\n\t\tint _offset = offset + 1;\r\n\t\tDataRow pack = new DataRow();\r\n\t\tpack.length = PIOUtils.redInteger4(buffer, _offset);\r\n\t\t_offset += 4;\r\n\t\tpack.columnNumber = PIOUtils.redInteger2(buffer, _offset);\r\n\t\t_offset += 2;\r\n\t\tpack.columns = new DataColumn[pack.columnNumber];\r\n\t\tfor (int i = 0; i < pack.columns.length; i++) {\r\n\t\t\tDataColumn col = new DataColumn();\r\n\t\t\tcol.length = PIOUtils.redInteger4(buffer, _offset);\r\n\t\t\t_offset += 4;\r\n\t\t\tif (col.length == -1) {\r\n\t\t\t\t// 数据为空\r\n\t\t\t\tcol.isNull = true;\r\n\t\t\t} else {\r\n\t\t\t\tcol.data = PIOUtils.redByteArray(buffer, _offset, col.length);\r\n\t\t\t\t_offset += col.length;\r\n\t\t\t}\r\n\t\t\tpack.columns[i] = col;\r\n\t\t}\r\n\t\treturn pack;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/EmptyQueryResponse.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\nimport io.mycat.backend.postgresql.utils.PIOUtils;\r\n\r\n\r\n//\t\tEmptyQueryResponse (B)\r\n//\t\tByte1('I')\r\n//\t\t标识这条消息是对一个空查询字串的响应。 （这个消息替换了 CommandComplete。）\r\n//\t\t\r\n//\t\tInt32(4)\r\n//\t\t以字节记的消息内容长度，包括它自己。\r\n\r\n/*******\r\n * 空查询响应\r\n * @author Coollf\r\n *\r\n */\r\npublic class EmptyQueryResponse extends PostgreSQLPacket {\r\n\r\n\tprivate char marker = PacketMarker.B_EmptyQueryResponse.getValue();\r\n\tprivate int length;\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn marker;\r\n\t}\r\n\r\n\tpublic static EmptyQueryResponse parse(ByteBuffer buffer, int offset) {\r\n\t\tif (buffer.get(offset) != PacketMarker.B_EmptyQueryResponse.getValue()) {\r\n\t\t\tthrow new IllegalArgumentException(\r\n\t\t\t\t\t\"this packetData not is EmptyQueryResponse\");\r\n\t\t}\r\n\t\tint _offset = offset + 1;\r\n\t\tEmptyQueryResponse pack = new EmptyQueryResponse();\r\n\t\tpack.length = PIOUtils.redInteger4(buffer, _offset);\r\n\t\treturn pack;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/ErrorResponse.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.io.UnsupportedEncodingException;\r\nimport java.nio.ByteBuffer;\r\n\r\n//ErrorResponse (B)\r\n//Byte1('E')\r\n//标识消息是一条错误。\r\n//\r\n//Int32\r\n//以字节记的消息内容的长度，包括长度本身。\r\n//\r\n//消息体由一个或多个标识出来的字段组成，后面跟着一个字节零作为终止符。 字段可以以任何顺序出现。对于每个字段都有下面的东西：\r\n//\r\n//Byte1\r\n//一个标识字段类型的代码；如果为零，这就是消息终止符并且不会跟着有字串。 目前定义的字段类型在 Section 43.5 列出。 因为将来可能增加更多的字段类型，所以前端应该不声不响地忽略不认识类型的字段。\r\n//\r\n//String\r\n//字段值。\r\n\r\npublic class ErrorResponse extends PostgreSQLPacket {\r\n\t/*********\r\n\t * 解析错误包\r\n\t * \r\n\t * @param buffer\r\n\t * @param offset\r\n\t * @return\r\n\t * @throws UnsupportedEncodingException\r\n\t * @throws IllegalAccessException\r\n\t */\r\n\tpublic static ErrorResponse parse(ByteBuffer buffer, int offset)\r\n\t\t\tthrows IllegalArgumentException  {\r\n\t\tif ((char) buffer.get(offset) != PacketMarker.B_Error.getValue()) {\r\n\t\t\tthrow new IllegalArgumentException(\"this packet not is ErrorResponse\");\r\n\t\t}\r\n\t\tErrorResponse err = new ErrorResponse();\r\n\t\terr.length = buffer.getInt(offset + 1);\r\n\t\terr.mark = buffer.get(offset + 1 + 4);\r\n\t\tif (err.mark != 0) {\r\n\t\t\tbyte[] str = new byte[err.length - (4+4)];\r\n\t\t\tfor(int i =0;i<str.length;i++){\r\n\t\t\t\tstr[i] = buffer.get(offset + 1 + 4 + 4 +i);\r\n\t\t\t}\r\n\t\t\terr.errMsg = new String(str,UTF8);\r\n\t\t}\r\n\t\treturn err;\r\n\t}\r\n\r\n\tprivate int length;\r\n\r\n\tprivate byte mark;\r\n\r\n\tprivate String errMsg;\r\n\r\n\tpublic String getErrMsg() {\r\n\t\treturn errMsg;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn PostgreSQLPacket.PacketMarker.B_Error.getValue();\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/NoticeResponse.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\npublic class NoticeResponse extends PostgreSQLPacket {\r\n\t/**\r\n\t * 长度\r\n\t */\r\n\tint length;\r\n\r\n\t/**\r\n\t * 标记\r\n\t */\r\n\tbyte mark;\r\n\r\n\tprivate String msg;\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn PacketMarker.B_NoticeResponse.getValue();\r\n\t}\r\n\r\n\tpublic static NoticeResponse parse(ByteBuffer buffer, int offset) {\r\n\t\tif (PacketMarker.B_NoticeResponse.getValue() != (char) buffer.get(offset)) {\r\n\t\t\tthrow new IllegalArgumentException(\"this packet not is NoticeResponse\");\r\n\t\t}\r\n\t\tNoticeResponse noticeResponse = new NoticeResponse();\r\n\t\tnoticeResponse.length = buffer.getInt(offset + 1);\r\n\t\tnoticeResponse.mark = buffer.get(offset + 1 + 4);\r\n\t\tif (noticeResponse.mark != 0) {\r\n\t\t\tbyte[] str = new byte[noticeResponse.length - 4 - 1];\r\n\t\t\tfor (int i = 0; i < str.length; i++) {\r\n\t\t\t\tstr[i] = buffer.get(offset + 1 + 4 + 1 + i);\r\n\t\t\t}\r\n\t\t\tnoticeResponse.msg = new String(str,UTF8);\r\n\t\t}\r\n\t\treturn noticeResponse;\r\n\t}\r\n\r\n\tpublic String getMsg() {\r\n\t\treturn msg;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/NotificationResponse.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n//\t\tNotificationResponse (B)\r\n//\t\tByte1('A')\r\n//\t\t标识这条消息是一个通知响应。\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t以字节记地消息内容地长度，包括长度本身。\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t通知后端进程地进程 ID。\r\n//\t\t\r\n//\t\tString\r\n//\t\t触发通知的条件的名字。\r\n//\t\t\r\n//\t\tString\r\n//\t\t从通知进程传递过来的额外的信息。（目前，这个特性还未实现，因此这个字段总是一个空字串。）\r\npublic class NotificationResponse extends PostgreSQLPacket{\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\t// TODO Auto-generated method stub\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\t// TODO Auto-generated method stub\r\n\t\treturn 0;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/ParameterDescription.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n//\t\tParameterDescription (B)\r\n//\t\tByte1('t')\r\n//\t\t标识消息是一个参数描述。\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t以字节记的消息内容长度，包括长度本身。\r\n//\t\t\r\n//\t\tInt16\r\n//\t\t语句所使用的参数的个数（可以为零）。\r\n//\t\t\r\n//\t\t然后，对每个参数，有下面的东西。\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t声明参数数据类型的对象 ID。\r\npublic class ParameterDescription {\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/ParameterStatus.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\npublic class ParameterStatus extends PostgreSQLPacket {\r\n\t/**\r\n\t * 数据包长度\r\n\t */\r\n\tprivate int length;\r\n\r\n\tprivate String key;\r\n\r\n\tprivate String value;\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn PacketMarker.B_ParameterStatus.getValue();\r\n\t}\r\n\r\n\tpublic static ParameterStatus parse(ByteBuffer buffer, int offset) {\r\n\t\tif ((char) buffer.get(offset) != PacketMarker.B_ParameterStatus.getValue()) {\r\n\t\t\tthrow new IllegalArgumentException(\"this packet not is ParameterStatus\");\r\n\t\t}\r\n\r\n\t\tParameterStatus ps = new ParameterStatus();\r\n\t\tps.length = buffer.getInt(offset + 1);\r\n\t\tbyte[] bs = new byte[ps.length - 4];\r\n\t\tfor (int i = 0; i < bs.length; i++) {\r\n\t\t\tbs[i] = buffer.get(offset + 1 + 4 + i);\r\n\t\t}\r\n\t\tString _val = new String(bs, UTF8);\r\n\t\tString[] vs = _val.split(\" \");\r\n\t\tps.key = vs[0];\r\n\t\tps.value = _val.substring(ps.key.length());\r\n\t\treturn ps;\r\n\t}\r\n\r\n\tpublic String getKey() {\r\n\t\treturn key;\r\n\t}\r\n\r\n\tpublic String getValue() {\r\n\t\treturn value;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/Parse.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.io.IOException;\r\nimport java.nio.ByteBuffer;\r\n\r\nimport io.mycat.backend.postgresql.utils.PIOUtils;\r\n\r\n//\t\tParse (F)\r\n//\t\tByte1('P')\r\n//\t\t标识消息是一条 Parse 命令。\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t以字节记的消息内容的长度，包括长度自身。\r\n//\t\t\r\n//\t\tString\r\n//\t\t目的准备好语句的名字（空字串表示选取了未命名的准备好语句）。\r\n//\t\t\r\n//\t\tString\r\n//\t\t要分析的查询字串。\r\n//\t\t\r\n//\t\tInt16\r\n//\t\t\r\n//\t\t\t声明的参数数据类型的数目(可以为零)。请注意这个参数并不意味着可能在查询字串里出现的参数个数的意思， 只是前端希望预先声明的类型的数目。\r\n//\t\t\r\n//\t\t然后，对每个参数，有下面的东西：\r\n//\t\t\r\n//\t\tInt32\r\n//\t\t声明参数数据类型的对象 ID。在这里放一个零等效于不声明该类型。\r\n\r\n/**\r\n * 解析sql语句\r\n * \r\n * @author Coollf\r\n *\r\n */\r\npublic class Parse extends PostgreSQLPacket {\r\n\r\n\tprivate char marker = PacketMarker.F_Parse.getValue();\r\n\t// private int length;\r\n\tprivate String name;\r\n\tprivate String sql;\r\n\tprivate short parameterNumber;\r\n\tprivate DateType[] parameterTypes;\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn 4 + name.getBytes(UTF8).length + sql.getBytes(UTF8).length + 2 + 4\r\n\t\t\t\t* parameterNumber; //参数为空时仍然需要多个类型为0的int\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn marker;\r\n\t}\r\n\r\n\tpublic Parse(String name, String sql, DateType... parameterTypes) {\r\n\t\tthis.name = (name == null) ? \"\\0\" : (name.trim() + \"\\0\");\r\n\t\tthis.sql = (sql == null) ? \"\\0\" : (sql.trim() + \"\\0\");\r\n\t\tthis.parameterNumber = (short) parameterTypes.length;\r\n\t\tthis.parameterTypes = parameterTypes;\r\n\r\n\t}\r\n\r\n\tpublic void write(ByteBuffer buffer) throws IOException {\r\n\t\tPIOUtils.SendChar(marker, buffer);\r\n\t\tPIOUtils.SendInteger4(getLength(), buffer);\r\n\t\tPIOUtils.SendString(name, buffer);\r\n\t\tPIOUtils.SendString(sql, buffer);\r\n\t\tPIOUtils.SendInteger2(parameterNumber, buffer);\r\n\t\tfor (DateType tp : parameterTypes) {\r\n\t\t\tPIOUtils.SendInteger4(tp.getValue(), buffer);\r\n\t\t}\r\n\t\t\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/ParseComplete.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\nimport io.mycat.backend.postgresql.utils.PIOUtils;\r\n\r\npublic class ParseComplete extends PostgreSQLPacket {\r\n\tprivate char marker = PacketMarker.B_ParseComplete.getValue();\r\n\tprivate int length ;\r\n\t\r\n\t\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn marker;\r\n\t}\r\n\r\n\tpublic static ParseComplete parse(ByteBuffer buffer, int offset) {\r\n\t\tif ((char) buffer.get(offset) != PacketMarker.B_ParseComplete.getValue()) {\r\n\t\t\tthrow new IllegalArgumentException(\"this packet not is ParseComplete\");\r\n\t\t}\r\n\t\tParseComplete parse = new ParseComplete();\r\n\t\tparse.length = PIOUtils.redInteger4(buffer, offset+1);\r\n\t\treturn parse;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/PasswordMessage.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.io.UnsupportedEncodingException;\r\nimport java.nio.ByteBuffer;\r\n\r\nimport io.mycat.backend.postgresql.packet.AuthenticationPacket.AuthType;\r\nimport io.mycat.backend.postgresql.utils.MD5Digest;\r\nimport io.mycat.backend.postgresql.utils.PIOUtils;\r\n\r\n//PasswordMessage (F)\r\n//Byte1('p')\r\n//标识这条消息是一个口令响应。\r\n//\r\n//Int32\r\n//以字节记的消息内容的长度，包括长度本身。\r\n//\r\n//String\r\n//口令（如果要求了，就是加密后的）。\r\npublic class PasswordMessage extends PostgreSQLPacket {\r\n\r\n\tpublic PasswordMessage(String user, String password, AuthType aut, byte[] salt)\r\n\t\t\tthrows UnsupportedEncodingException {\r\n\t\tif (aut == AuthType.MD5Password) {\r\n\t\t\tthis.password = MD5Digest.encode(user.getBytes(UTF8), password.getBytes(UTF8), salt);\r\n\t\t}\r\n\t}\r\n\r\n\tprivate char marker = PacketMarker.F_PwdMess.getValue();\r\n\r\n\tprivate byte[] password;\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn  4 + password.length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn marker;\r\n\t}\r\n\r\n\tpublic void write(ByteBuffer buffer) {\r\n\t\tPIOUtils.SendChar(getMarker(), buffer);\r\n\t\tPIOUtils.SendInteger4(getLength(), buffer);\r\n\t\tPIOUtils.Send(password, buffer);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/PostgreSQLPacket.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.charset.Charset;\r\n\r\npublic abstract class PostgreSQLPacket {\r\n\r\n\tpublic final static Charset UTF8 = Charset.forName(\"utf-8\");\r\n\r\n\t/***\r\n\t * 获取包长度\r\n\t * \r\n\t * @return\r\n\t */\r\n\tpublic abstract int getLength();\r\n\r\n\t/***\r\n\t * 获取包标记\r\n\t * \r\n\t * @return\r\n\t */\r\n\tpublic abstract char getMarker();\r\n\t\r\n\t\r\n\tpublic int getPacketSize(){\r\n\t\treturn getLength() + 1;\r\n\t}\r\n\t\r\n\tpublic String getType(){\r\n\t\treturn this.getClass().getSimpleName();\r\n\t}\r\n\t\r\n\t/***\r\n\t * 数据类型\r\n\t * \r\n\t * @author Coollf\r\n\t *\r\n\t */\r\n\tpublic static enum DateType {\r\n\t\tbit_(1560), boo_(16), box_(603), bytea_(17), char_(1042), cidr_(650), circle_(\r\n\t\t\t\t718), date_(1082), decimal_(1700), float4_(700), float8_(701), inet_(\r\n\t\t\t\t869), int2_(21), int4_(23), int8_(20), interval_(1186), json_(\r\n\t\t\t\t114), jsonb_(3802), line_(628), lseg_(601), macaddr_(829), money_(\r\n\t\t\t\t790), path_(602), point_(600), polygon_(604), serial2_(21), serial4_(\r\n\t\t\t\t23), serial8_(20), text_(25), time_(1083), timetz_(1266), timestamp_(\r\n\t\t\t\t1114), timestamptz_(1184), tsquery_(3615), tsvector_(3614), txid_snapshot_(\r\n\t\t\t\t2970), uuid_(2950), varbit_(1562), varchar_(1043), xml_(142), UNKNOWN;\r\n\r\n\t\tprivate int value = 0;\r\n\r\n\t\tpublic static DateType valueOf(int val) {\r\n\t\t\tswitch (val) {\r\n\t\t\tcase 1560:\r\n\t\t\t\treturn bit_;\r\n\t\t\tcase 16:\r\n\t\t\t\treturn boo_;\r\n\t\t\tcase 603:\r\n\t\t\t\treturn box_;\r\n\t\t\tcase 17:\r\n\t\t\t\treturn bytea_;\r\n\t\t\tcase 1042:\r\n\t\t\t\treturn char_;\r\n\t\t\tcase 650:\r\n\t\t\t\treturn cidr_;\r\n\t\t\tcase 718:\r\n\t\t\t\treturn circle_;\r\n\t\t\tcase 1082:\r\n\t\t\t\treturn date_;\r\n\t\t\tcase 1700:\r\n\t\t\t\treturn decimal_;\r\n\t\t\tcase 700:\r\n\t\t\t\treturn float4_;\r\n\t\t\tcase 701:\r\n\t\t\t\treturn float8_;\r\n\t\t\tcase 869:\r\n\t\t\t\treturn inet_;\r\n\t\t\tcase 21:\r\n\t\t\t\treturn int2_;\r\n\t\t\tcase 23:\r\n\t\t\t\treturn int4_;\r\n\t\t\tcase 20:\r\n\t\t\t\treturn int8_;\r\n\t\t\tcase 1186:\r\n\t\t\t\treturn interval_;\r\n\t\t\tcase 114:\r\n\t\t\t\treturn json_;\r\n\t\t\tcase 3802:\r\n\t\t\t\treturn jsonb_;\r\n\t\t\tcase 628:\r\n\t\t\t\treturn line_;\r\n\t\t\tcase 601:\r\n\t\t\t\treturn lseg_;\r\n\t\t\tcase 829:\r\n\t\t\t\treturn macaddr_;\r\n\t\t\tcase 790:\r\n\t\t\t\treturn money_;\r\n\t\t\tcase 602:\r\n\t\t\t\treturn path_;\r\n\t\t\tcase 600:\r\n\t\t\t\treturn point_;\r\n\t\t\tcase 604:\r\n\t\t\t\treturn polygon_;\r\n//\t\t\tcase 21:\r\n//\t\t\t\treturn serial2_;\r\n//\t\t\tcase 23:\r\n//\t\t\t\treturn serial4_;\r\n//\t\t\tcase 20:\r\n//\t\t\t\treturn serial8_;\r\n\t\t\tcase 25:\r\n\t\t\t\treturn text_;\r\n\t\t\tcase 1083:\r\n\t\t\t\treturn time_;\r\n\t\t\tcase 1266:\r\n\t\t\t\treturn timetz_;\r\n\t\t\tcase 1114:\r\n\t\t\t\treturn timestamp_;\r\n\t\t\tcase 1184:\r\n\t\t\t\treturn timestamptz_;\r\n\t\t\tcase 3615:\r\n\t\t\t\treturn tsquery_;\r\n\t\t\tcase 3614:\r\n\t\t\t\treturn tsvector_;\r\n\t\t\tcase 2970:\r\n\t\t\t\treturn txid_snapshot_;\r\n\t\t\tcase 2950:\r\n\t\t\t\treturn uuid_;\r\n\t\t\tcase 1562:\r\n\t\t\t\treturn varbit_;\r\n\t\t\tcase 1043:\r\n\t\t\t\treturn varchar_;\r\n\t\t\tcase 142:\r\n\t\t\t\treturn xml_;\r\n\t\t\t}\r\n\t\t\treturn UNKNOWN;\r\n\t\t}\r\n\r\n\t\tprivate DateType() {\r\n\t\t}\r\n\r\n\t\tprivate DateType(int value) {\r\n\t\t\tthis.value = value;\r\n\t\t}\r\n\r\n\t\tpublic int getValue() {\r\n\t\t\treturn value;\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/****\r\n\t * 数据协议\r\n\t * \r\n\t * @author Coollf\r\n\t *\r\n\t */\r\n\tpublic static enum DataProtocol {\r\n\t\tTEXT, BINARY, UNKNOWN;\r\n\r\n\t\tpublic static DataProtocol valueOf(short val) {\r\n\t\t\tif (val == 0) {\r\n\t\t\t\treturn TEXT;\r\n\t\t\t}\r\n\t\t\tif (val == 1) {\r\n\t\t\t\treturn BINARY;\r\n\t\t\t}\r\n\t\t\treturn UNKNOWN;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static enum PacketMarker {\r\n\t\t/**\r\n\t\t * 认证包\r\n\t\t */\r\n\t\tB_Auth('R'),\r\n\r\n\t\t/***\r\n\t\t * 密码请求包\r\n\t\t */\r\n\t\tF_PwdMess('p'),\r\n\r\n\t\t/**\r\n\t\t * 错误包响应\r\n\t\t */\r\n\t\tB_Error('E'),\r\n\r\n\t\t/***\r\n\t\t * 后台传回的秘钥\r\n\t\t */\r\n\t\tB_BackendKey('K'),\r\n\r\n\t\t/***\r\n\t\t * paramter 状态信息\r\n\t\t */\r\n\t\tB_ParameterStatus('S'),\r\n\r\n\t\t/**\r\n\t\t * 等待查询\r\n\t\t */\r\n\t\tB_ReadyForQuery('Z'),\r\n\r\n\t\t/**\r\n\t\t * 警告响应\r\n\t\t */\r\n\t\tB_NoticeResponse('N'),\r\n\r\n\t\t/***\r\n\t\t * 简单查询\r\n\t\t */\r\n\t\tF_Query('Q'),\r\n\r\n\t\t/******\r\n\t\t * SQL 命令正常结束\r\n\t\t */\r\n\t\tB_CommandComplete('C'),\r\n\r\n\t\t/***\r\n\t\t * 数据行描述\r\n\t\t */\r\n\t\tB_RowDescription('T'),\r\n\r\n\t\t/***\r\n\t\t * 数据行数据\r\n\t\t */\r\n\t\tB_DataRow('D'),\r\n\r\n\t\t/***\r\n\t\t * 空查询\r\n\t\t */\r\n\t\tB_EmptyQueryResponse('I'),\r\n\r\n\t\t/*************\r\n\t\t * 拷贝数据进PGsql\r\n\t\t */\r\n\t\tB_CopyInResponse('G'),\r\n\r\n\t\t/***\r\n\t\t * 从PGsql 中拷贝数据出来\r\n\t\t */\r\n\t\tB_CopyOutResponse('H'),\r\n\r\n\t\t/**\r\n\t\t * 连接启动信息\r\n\t\t */\r\n\t\tF_StartupMessage('\\0'),\r\n\r\n\t\t/***\r\n\t\t * 终止请求\r\n\t\t */\r\n\t\tF_Terminate('X'),\r\n\r\n\t\t/***\r\n\t\t * 解析sql语句请求\r\n\t\t */\r\n\t\tF_Parse('P'),\r\n\r\n\t\t/**\r\n\t\t * sql 解析成功\r\n\t\t */\r\n\t\tB_ParseComplete('1'),\r\n\r\n\t\t/***\r\n\t\t * 绑定参数成功\r\n\t\t */\r\n\t\tB_BindComplete('2');\r\n\r\n\t\tprivate char value;\r\n\r\n\t\tprivate PacketMarker(char marker) {\r\n\t\t\tthis.value = marker;\r\n\t\t}\r\n\r\n\t\tpublic char getValue() {\r\n\t\t\treturn value;\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/Query.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\nimport io.mycat.backend.postgresql.utils.PIOUtils;\r\n\r\n/**********\r\n * 查询数据包\r\n * \r\n * @author Coollf\r\n */\r\n// Query (F)\r\n// Byte1('Q')\r\n// 标识消息是一个简单查询。\r\n//\r\n// Int32\r\n// 以字节记的消息内容的长度，包括长度自身。\r\n//\r\n// String\r\n// 查询字串自身。\r\npublic class Query extends PostgreSQLPacket {\r\n\r\n\tprivate String sql;\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn 4 + (sql == null ? 0 : (sql.getBytes(UTF8).length)); // length + string\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// length\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn PacketMarker.F_Query.getValue();\r\n\t}\r\n\r\n\tpublic Query(String sql) {\r\n\t\tthis.sql = sql.trim() + \"\\0\";\r\n\t}\r\n\r\n\tpublic void write(ByteBuffer buffer) {\r\n\t\tPIOUtils.SendChar(getMarker(), buffer);\r\n\t\tPIOUtils.SendInteger4(getLength(), buffer);\r\n\t\tPIOUtils.SendString(sql, buffer);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/ReadyForQuery.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\n/***\r\n * 等待查询包\r\n * \r\n * @author Coollf\r\n *\r\n */\r\n\r\n// ReadyForQuery (B)\r\n// Byte1('Z')\r\n// 标识消息类型。在后端为新的查询循环准备好的时候， 总会发送 ReadyForQuery。\r\n//\r\n// Int32(5)\r\n// 以字节记的消息内容的长度，包括长度本身。\r\n//\r\n// Byte1\r\n// 当前后端事务状态指示器。可能的值是空闲状况下的 'I'（不在事务块里）；在事务块里是 'T'； 或者在一个失败的事务块里是\r\n// 'E'（在事务块结束之前，任何查询都将被拒绝）。\r\npublic class ReadyForQuery extends PostgreSQLPacket {\r\n\r\n\t/*****\r\n\t * 消息长度\r\n\t */\r\n\tprivate int length;\r\n\r\n\t/***\r\n\t * 状态\r\n\t */\r\n\tTransactionState state;\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn PacketMarker.B_ReadyForQuery.getValue();\r\n\t}\r\n\r\n\tpublic static ReadyForQuery parse(ByteBuffer buffer, int offset) {\r\n\t\tif (buffer.get(offset) != PacketMarker.B_ReadyForQuery.getValue()) {\r\n\t\t\tthrow new IllegalArgumentException(\"this packet not is ReadyForQuery\");\r\n\t\t}\r\n\t\tReadyForQuery readyForQuery = new ReadyForQuery();\r\n\t\treadyForQuery.length = buffer.getInt(offset + 1);\r\n\t\treadyForQuery.state = TransactionState.valueOf((char)buffer.get(offset+1+4));\r\n\t\treturn readyForQuery;\r\n\t}\r\n\r\n\t/***\r\n\t * 后端事物状态\r\n\t * \r\n\t * @author Coollf\r\n\t *\r\n\t */\r\n\tpublic static enum TransactionState {\r\n\t\t/***\r\n\t\t * 不在事物中\r\n\t\t */\r\n\t\tNOT_IN('I'),\r\n\r\n\t\t/**\r\n\t\t * 在事物中\r\n\t\t */\r\n\t\tIN('T'),\r\n\r\n\t\t/***\r\n\t\t * 错误\r\n\t\t */\r\n\t\tERR('E');\r\n\r\n\t\tprivate char vlaue;\r\n\r\n\t\tpublic char getVlaue() {\r\n\t\t\treturn vlaue;\r\n\t\t}\r\n\r\n\t\tTransactionState(char value) {\r\n\t\t\tthis.vlaue = value;\r\n\t\t}\r\n\r\n\t\tpublic static TransactionState valueOf(char v) {\r\n\t\t\tif (v == NOT_IN.getVlaue()) {\r\n\t\t\t\treturn NOT_IN;\r\n\t\t\t}\r\n\t\t\tif (v == IN.getVlaue()) {\r\n\t\t\t\treturn IN;\r\n\t\t\t}\r\n\t\t\tif (v == ERR.getVlaue()) {\r\n\t\t\t\treturn TransactionState.ERR;\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * @return the state\r\n\t */\r\n\tpublic TransactionState getState() {\r\n\t\treturn state;\r\n\t}\r\n\r\n\t/**\r\n\t * @param state the state to set\r\n\t */\r\n\tpublic void setState(TransactionState state) {\r\n\t\tthis.state = state;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/RowDescription.java",
    "content": "package io.mycat.backend.postgresql.packet;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.postgresql.utils.PIOUtils;\n\n//\t\tRowDescription (B)\n//\t\tByte1('T')\n//\t\t标识消息是一个行描述。\n//\t\t\n//\t\tInt32\n//\t\t以字节记的消息内容的长度，包括长度自身。\n//\t\t\n//\t\tInt16\n//\t\t声明在一个行里面的字段数目（可以为零）。\n//\t\t\n//\t\t然后对于每个字段，有下面的东西：\n//\t\t\n//\t\tString\n//\t\t字段名字。\n//\t\t\n//\t\tInt32\n//\t\t如果字段可以标识为一个特定表的字段，那么就是表的对象 ID； 否则就是零。\n//\t\t\n//\t\tInt16\n//\t\t\n//\t\t\t如果该字段可以标识为一个特定表的字段，那么就是该表字段的属性号；否则就是零。\n//\t\t\n//\t\tInt32\n//\t\t字段数据类型的对象 ID。\n//\t\t\n//\t\tInt16\n//\t\t数据类型尺寸（参阅pg_type.typlen）。 请注意负数表示变宽类型。\n//\t\t\n//\t\tInt32\n//\t\t类型修饰词(参阅pg_attribut.atttypmod)。 修饰词的含义是类型相关的。\n//\t\t\n//\t\tInt16\n//\t\t\n//\t\t\t用于该字段的格式码。目前会是零（文本）或者一（二进制）。 从语句变种 Describe 返回的 RowDescription 里，格式码还是未知的，因此总是零。\npublic class RowDescription extends PostgreSQLPacket {\n\n\tprivate int length;\n\n\t/**\n\t * 列数\n\t */\n\tprivate short columnNumber;\n\n\t/***\n\t * 列信息\n\t */\n\tprivate ColumnDescription[] columns;\n\n\t@Override\n\tpublic int getLength() {\n\t\treturn length;\n\t}\n\n\t@Override\n\tpublic char getMarker() {\n\t\treturn PacketMarker.B_RowDescription.getValue();\n\t}\n\n\tpublic static RowDescription parse(ByteBuffer buffer, int offset)\n\t\t\tthrows  IOException {\n\t\tif (buffer.get(offset) != PacketMarker.B_RowDescription.getValue()) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"this packetData not is RowDescription\");\n\t\t}\n\t\tRowDescription pack = new RowDescription();\n\t\tpack.length = PIOUtils.redInteger4(buffer, offset + 1);\n\t\tpack.columnNumber = PIOUtils.redInteger2(buffer, offset + 1 + 4);\n\n\t\tpack.columns = new ColumnDescription[pack.columnNumber];\n\t\tint _offset = offset + 1 + 4 + 2;\n\t\tfor (int i = 0; i < pack.columns.length; i++) {\n\t\t\tColumnDescription col = new ColumnDescription();\n\t\t\tcol.columnName = PIOUtils.redString(buffer, _offset, UTF8);\n\t\t\t_offset = _offset + col.columnName.getBytes(UTF8).length + 1;\n\n\t\t\tcol.oid = PIOUtils.redInteger4(buffer, _offset);\n\t\t\t_offset += 4;\n\t\t\tcol.coid = PIOUtils.redInteger2(buffer, _offset);\n\t\t\t_offset += 2;\n\t\t\tcol.columnType = DateType.valueOf(PIOUtils.redInteger4(buffer,\n\t\t\t\t\t_offset));\t\t\t\n\t\t\t_offset += 4;\n\t\t\tcol.typlen = PIOUtils.redInteger2(buffer, _offset);\n\t\t\t_offset += 2;\n\t\t\tcol.atttypmod = PIOUtils.redInteger4(buffer, _offset);\n\t\t\t_offset += 4;\n\t\t\tcol.protocol = DataProtocol.valueOf(PIOUtils.redInteger2(buffer,\n\t\t\t\t\t_offset));\n\t\t\t_offset += 2;\n\t\t\tpack.columns[i] = col;\n\t\t}\n\n\t\treturn pack;\n\t}\n\n\t/**\n\t * @return the columns\n\t */\n\tpublic ColumnDescription[] getColumns() {\n\t\treturn columns;\n\t}\n\n\t/**\n\t * @return the columnNumber\n\t */\n\tpublic short getColumnNumber() {\n\t\treturn columnNumber;\n\t}\n\n\tpublic static class ColumnDescription {\n\t\t/***\n\t\t * 列名称\n\t\t */\n\t\tprivate String columnName;\n\n\t\t/**\n\t\t * 表的对象id\n\t\t */\n\t\tprivate int oid;\n\n\t\t/***\n\t\t * 那么就是该表字段的属性号\n\t\t */\n\t\tprivate short coid;\n\n\t\t/***\n\t\t * 字段类型\n\t\t */\n\t\tprivate DateType columnType;\n\n\t\t/**\n\t\t * 数据类型尺寸\n\t\t */\n\t\tprivate short typlen;\n\n\t\t/**\n\t\t * 数尺寸,负数标示宽度类型\n\t\t */\n\t\tprivate int atttypmod;\n\n\t\t/***\n\t\t * 数据协议 int16\n\t\t */\n\t\tprivate DataProtocol protocol;\n\n\t\t/**\n\t\t * @return the columnName\n\t\t */\n\t\tpublic String getColumnName() {\n\t\t\treturn columnName;\n\t\t}\n\n\t\t/**\n\t\t * @param columnName\n\t\t *            the columnName to set\n\t\t */\n\t\tpublic void setColumnName(String columnName) {\n\t\t\tthis.columnName = columnName;\n\t\t}\n\n\t\t/**\n\t\t * @return the oid\n\t\t */\n\t\tpublic int getOid() {\n\t\t\treturn oid;\n\t\t}\n\n\t\t/**\n\t\t * @param oid\n\t\t *            the oid to set\n\t\t */\n\t\tpublic void setOid(int oid) {\n\t\t\tthis.oid = oid;\n\t\t}\n\n\t\t/**\n\t\t * @return the coid\n\t\t */\n\t\tpublic short getCoid() {\n\t\t\treturn coid;\n\t\t}\n\n\t\t/**\n\t\t * @param coid\n\t\t *            the coid to set\n\t\t */\n\t\tpublic void setCoid(short coid) {\n\t\t\tthis.coid = coid;\n\t\t}\n\n\t\t/**\n\t\t * @return the columnType\n\t\t */\n\t\tpublic DateType getColumnType() {\n\t\t\treturn columnType;\n\t\t}\n\n\t\t/**\n\t\t * @param columnType\n\t\t *            the columnType to set\n\t\t */\n\t\tpublic void setColumnType(DateType columnType) {\n\t\t\tthis.columnType = columnType;\n\t\t}\n\n\t\t/**\n\t\t * @return the typlen\n\t\t */\n\t\tpublic short getTyplen() {\n\t\t\treturn typlen;\n\t\t}\n\n\t\t/**\n\t\t * @param typlen\n\t\t *            the typlen to set\n\t\t */\n\t\tpublic void setTyplen(short typlen) {\n\t\t\tthis.typlen = typlen;\n\t\t}\n\n\t\t/**\n\t\t * @return the atttypmod\n\t\t */\n\t\tpublic int getAtttypmod() {\n\t\t\treturn atttypmod;\n\t\t}\n\n\t\t/**\n\t\t * @param atttypmod\n\t\t *            the atttypmod to set\n\t\t */\n\t\tpublic void setAtttypmod(int atttypmod) {\n\t\t\tthis.atttypmod = atttypmod;\n\t\t}\n\n\t\t/**\n\t\t * @return the protocol\n\t\t */\n\t\tpublic DataProtocol getProtocol() {\n\t\t\treturn protocol;\n\t\t}\n\n\t\t/**\n\t\t * @param protocol\n\t\t *            the protocol to set\n\t\t */\n\t\tpublic void setProtocol(DataProtocol protocol) {\n\t\t\tthis.protocol = protocol;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/SSLRequest.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\n//\t\tSSLRequest (F)\r\n//\t\tInt32(8)\r\n//\t\t以字节记的消息内容的长度，包括长度本身。\r\n//\t\t\r\n//\t\tInt32(80877103)\r\n//\t\tSSL 请求码。选取的数值在高16位里包含 1234，在低16位里包含 5679。 （为了避免混淆，这个编码必须和任何协议版本号不同。）\r\npublic class SSLRequest {\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/StartupMessage.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.util.List;\r\n\r\npublic class StartupMessage extends PostgreSQLPacket {\r\n\tprivate char marker = PacketMarker.F_StartupMessage.getValue(); //标准\r\n\tpublic int major; // 协议版本\r\n\r\n\tpublic List<String[]> params; // 协议参数\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t@Override\r\n\t@Deprecated\r\n\tpublic char getMarker() {\r\n\t\treturn marker;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/packet/Terminate.java",
    "content": "package io.mycat.backend.postgresql.packet;\r\n\r\nimport java.nio.ByteBuffer;\r\n//\t\r\n//\tTerminate (F)\r\n//\tByte1('X')\r\n//\t标识消息是一个终止消息。\r\n//\t\r\n//\tInt32(4)\r\n//\t以字节记的消息内容的长度，包括长度自身。\r\n\r\nimport io.mycat.backend.postgresql.utils.PIOUtils;\r\n\r\n/***\r\n * 终止命令\r\n * \r\n * @author Coollf\r\n *\r\n */\r\npublic class Terminate extends PostgreSQLPacket {\r\n\r\n\tprivate int length = 4;\r\n\r\n\t@Override\r\n\tpublic int getLength() {\r\n\r\n\t\treturn length;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic char getMarker() {\r\n\t\treturn PacketMarker.F_Terminate.getValue();\r\n\t}\r\n\r\n\tpublic void write(ByteBuffer buffer) {\r\n\t\tPIOUtils.SendChar(getMarker(), buffer);\r\n\t\tPIOUtils.SendInteger4(getLength(), buffer);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/utils/MD5Digest.java",
    "content": "/*-------------------------------------------------------------------------\n*\n* Copyright (c) 2003-2014, PostgreSQL Global Development Group\n*\n*\n*-------------------------------------------------------------------------\n*/\npackage io.mycat.backend.postgresql.utils;\n\n/**\n * MD5-based utility function to obfuscate passwords before network \n * transmission.\n *\n * @author Jeremy Wohl\n */\n\nimport java.security.MessageDigest;\n\npublic class MD5Digest\n{\n    private MD5Digest()\n    {\n    }\n\n    /*\n     * Encodes user/password/salt information in the following way:\n     *  MD5(MD5(password + user) + salt)\n     *\n     * @param user  The connecting user.\n     * @param password The connecting user's password.\n     * @param salt  A four-salt sent by the server.\n     *\n     * @return A 35-byte array, comprising the string \"md5\" and an MD5 digest.\n     */\n    public static byte[] encode(byte user[], byte password[], byte salt[])\n    {\n        MessageDigest md;\n        byte[] temp_digest, pass_digest;\n        byte[] hex_digest = new byte[35];\n\n        try\n        {\n            md = MessageDigest.getInstance(\"MD5\");\n\n            md.update(password);\n            md.update(user);\n            temp_digest = md.digest();\n\n            bytesToHex(temp_digest, hex_digest, 0);\n            md.update(hex_digest, 0, 32);\n            md.update(salt);\n            pass_digest = md.digest();\n\n            bytesToHex(pass_digest, hex_digest, 3);\n            hex_digest[0] = (byte) 'm';\n            hex_digest[1] = (byte) 'd';\n            hex_digest[2] = (byte) '5';\n        }\n        catch (Exception e)\n        {\n            ; // \"MessageDigest failure; \" + e\n        }\n\n        return hex_digest;\n    }\n\n    /*\n     * Turn 16-byte stream into a human-readable 32-byte hex string\n     */\n    private static void bytesToHex(byte[] bytes, byte[] hex, int offset)\n    {\n        final char lookup[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n                                'a', 'b', 'c', 'd', 'e', 'f' };\n\n        int i, c, j, pos = offset;\n\n        for (i = 0; i < 16; i++)\n        {\n            c = bytes[i] & 0xFF;\n            j = c >> 4;\n            hex[pos++] = (byte) lookup[j];\n            j = (c & 0xF);\n            hex[pos++] = (byte) lookup[j];\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/utils/PIOUtils.java",
    "content": "package io.mycat.backend.postgresql.utils;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\n\n/*****\n * PostgreSQL io工具类\n * \n * @author Coollf\n *\n */\npublic class PIOUtils {\n\n\tpublic final static Charset UTF8 = Charset.forName(\"utf-8\");\n\n\t/**\n\t * Sends a 4-byte integer to the back end\n\t *\n\t * @param val\n\t *            the integer to be sent\n\t * @exception IOException\n\t *                if an I/O error occurs\n\t */\n\tpublic static void SendInteger4(int val, ByteBuffer buffer) {\n\t\tbyte[] _int4buf = new byte[4];\n\t\t_int4buf[0] = (byte) (val >>> 24);\n\t\t_int4buf[1] = (byte) (val >>> 16);\n\t\t_int4buf[2] = (byte) (val >>> 8);\n\t\t_int4buf[3] = (byte) (val);\n\t\tbuffer.put(_int4buf);\n\t}\n\n\tpublic static int redInteger4(ByteBuffer buffer, int offset) {\n\t\treturn buffer.getInt(offset);\n\t}\n\t\n\t/***\n\t * 读取数据\n\t * @param buffer\n\t * @param offset\n\t * @return\n\t */\n\tpublic static short redInteger2(ByteBuffer buffer, int offset) {\n\t\treturn buffer.getShort(offset);\n\t}\n\n\t/**\n\t * Sends a 2-byte integer (short) to the back end\n\t *\n\t * @param val\n\t *            the integer to be sent\n\t * @exception IOException\n\t *                if an I/O error occurs or <code>val</code> cannot be\n\t *                encoded in 2 bytes\n\t */\n\tpublic static void SendInteger2(int val, ByteBuffer buffer)\n\t\t\tthrows IOException {\n\t\tif (val < Short.MIN_VALUE || val > Short.MAX_VALUE) {\n\t\t\tthrow new IOException(\n\t\t\t\t\t\"Tried to send an out-of-range integer as a 2-byte value: \"\n\t\t\t\t\t\t\t+ val);\n\t\t}\n\n\t\tbyte[] _int2buf = new byte[2];\n\t\t_int2buf[0] = (byte) (val >>> 8);\n\t\t_int2buf[1] = (byte) val;\n\t\tbuffer.put(_int2buf);\n\t}\n\n\tpublic static void Send(byte[] encodedParam, ByteBuffer buffer) {\n\t\tbuffer.put(encodedParam);\n\t}\n\n\tpublic static void SendChar(int i, ByteBuffer buffer) {\n\t\tbuffer.put((byte) i);\n\t}\n\n\t/***\n\t * 读取数组信息\n\t * \n\t * @param buffer\n\t * @param offset\n\t * @param length\n\t * @return\n\t */\n\tpublic static byte[] redByteArray(ByteBuffer buffer, int offset, int length) {\n\t\tbyte[] dst = new byte[length];\n\t\tfor (int i = 0; i < length; i++) {\n\t\t\tdst[i] = buffer.get(offset + i);\n\t\t}\n\t\treturn dst;\n\t}\n\n\tpublic static void SendString(String string, ByteBuffer buffer) {\n\t\tbuffer.put(string.getBytes(UTF8));\n\t}\n\n\tpublic static String redString(ByteBuffer buffer, int offset, Charset charset) throws IOException {\n\t\tByteArrayOutputStream out  =new ByteArrayOutputStream();\n\t\tfor(int i=offset ;i< buffer.limit();i++){\n\t\t\tif(((char)buffer.get(i)) == '\\0'){\n\t\t\t\tbreak;\n\t\t\t}\t\t\t\n\t\t\tout.write(new byte[]{buffer.get(i)});\n\t\t}\n\t\treturn new String(out.toByteArray(),charset);\n\t}\n\n\t/**\n\t * 读取1byte数据\n\t * @param buffer\n\t * @param _offset\n\t * @return\n\t */\n\tpublic static byte redInteger1(ByteBuffer buffer, int _offset) {\n\t\treturn buffer.get(_offset);\n\t}\n\n\tpublic static void SendByte(byte b, ByteBuffer buffer) {\n\t\tbuffer.put(b);\n\t}\n\t\n\t\n\n\t\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/utils/PacketUtils.java",
    "content": "package io.mycat.backend.postgresql.utils;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.TimeZone;\n\nimport io.mycat.backend.postgresql.packet.AuthenticationPacket;\nimport io.mycat.backend.postgresql.packet.BackendKeyData;\nimport io.mycat.backend.postgresql.packet.CommandComplete;\nimport io.mycat.backend.postgresql.packet.CopyInResponse;\nimport io.mycat.backend.postgresql.packet.CopyOutResponse;\nimport io.mycat.backend.postgresql.packet.DataRow;\nimport io.mycat.backend.postgresql.packet.EmptyQueryResponse;\nimport io.mycat.backend.postgresql.packet.ErrorResponse;\nimport io.mycat.backend.postgresql.packet.NoticeResponse;\nimport io.mycat.backend.postgresql.packet.ParameterStatus;\nimport io.mycat.backend.postgresql.packet.ParseComplete;\nimport io.mycat.backend.postgresql.packet.PostgreSQLPacket;\nimport io.mycat.backend.postgresql.packet.ReadyForQuery;\nimport io.mycat.backend.postgresql.packet.RowDescription;\n\npublic class PacketUtils {\n\n\tpublic static List<PostgreSQLPacket> parsePacket(ByteBuffer buffer,int offset,int readLength) throws  IOException{\n\t\tfinal ByteBuffer bytes = buffer;\n\t\tList<PostgreSQLPacket> pgs = new ArrayList<>();\n\t\twhile(offset < readLength){\n\t\t\tchar MAKE = (char)bytes.get(offset);\n\t\t\tPostgreSQLPacket pg = null;\n\t\t\tswitch (MAKE) {\n\t\t\tcase 'R':\n\t\t\t\tpg = AuthenticationPacket.parse(bytes, offset);\n\t\t\t\tbreak;\n\t\t\tcase 'E':\n\t\t\t\tpg = ErrorResponse.parse(bytes, offset);\n\t\t\t\tbreak;\n\t\t\tcase 'K':\n\t\t\t\tpg = BackendKeyData.parse(bytes, offset);\n\t\t\t\tbreak;\n\t\t\tcase 'S':\n\t\t\t\tpg = ParameterStatus.parse(bytes, offset);\n\t\t\t\tbreak;\n\t\t\tcase 'Z':\n\t\t\t\tpg = ReadyForQuery.parse(bytes, offset);\n\t\t\t\tbreak;\n\t\t\tcase 'N':\n\t\t\t\tpg = NoticeResponse.parse(bytes, offset);\n\t\t\t\tbreak;\n\t\t\tcase 'C':\n\t\t\t\tpg = CommandComplete.parse(bytes, offset);\n\t\t\t\tbreak;\n\t\t\tcase 'T':\n\t\t\t\tpg = RowDescription.parse(bytes, offset);\n\t\t\t\tbreak;\n\t\t\tcase 'D':\n\t\t\t\tpg = DataRow.parse(bytes, offset);\n\t\t\t\tbreak;\n\n\t\t\tcase 'I':\n\t\t\t\tpg = EmptyQueryResponse.parse(bytes, offset);\n\t\t\t\tbreak;\n\n\t\t\tcase 'G':\n\t\t\t\tpg = CopyInResponse.parse(bytes, offset);\n\t\t\t\tbreak;\n\t\t\tcase 'H':\n\t\t\t\tpg = CopyOutResponse.parse(bytes, offset);\n\t\t\t\tbreak;\n\t\t\tcase '1':\n\t\t\t\tpg = ParseComplete.parse(bytes, offset);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new RuntimeException(\"Unknown packet\");\n\t\t\t}\n\t\t\tif (pg != null) {\n\t\t\t\toffset = offset + pg.getLength() + 1;\n\t\t\t\tpgs.add(pg);\n\t\t\t}\n\t\t\t\n\t\t}\n\t\treturn pgs;\n\t}\n\t\n\t@Deprecated\n\tprivate static List<PostgreSQLPacket> parsePacket(byte[] bytes, int offset,\n\t\t\tint readLength) throws IOException {\n\t\tList<PostgreSQLPacket> pgs = new ArrayList<>();\n\t\twhile (offset < readLength) {\n\t\t\tchar MAKE = (char) bytes[offset];\n\t\t\tPostgreSQLPacket pg = null;\n\t\t\tswitch (MAKE) {\n\t\t\tcase 'R':\n\t\t\t\tpg = AuthenticationPacket.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\t\t\tcase 'E':\n\t\t\t\tpg = ErrorResponse.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\t\t\tcase 'K':\n\t\t\t\tpg = BackendKeyData.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\t\t\tcase 'S':\n\t\t\t\tpg = ParameterStatus.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\t\t\tcase 'Z':\n\t\t\t\tpg = ReadyForQuery.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\t\t\tcase 'N':\n\t\t\t\tpg = NoticeResponse.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\t\t\tcase 'C':\n\t\t\t\tpg = CommandComplete.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\t\t\tcase 'T':\n\t\t\t\tpg = RowDescription.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\t\t\tcase 'D':\n\t\t\t\tpg = DataRow.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\n\t\t\tcase 'I':\n\t\t\t\tpg = EmptyQueryResponse.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\n\t\t\tcase 'G':\n\t\t\t\tpg = CopyInResponse.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\t\t\tcase 'H':\n\t\t\t\tpg = CopyOutResponse.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\t\t\tcase '1':\n\t\t\t\tpg = ParseComplete.parse(ByteBuffer.wrap(bytes), offset);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new RuntimeException(\"Unknown packet\");\n\t\t\t}\n\t\t\tif (pg != null) {\n\t\t\t\toffset = offset + pg.getLength() + 1;\n\t\t\t\tpgs.add(pg);\n\t\t\t}\n\t\t}\n\n\t\treturn pgs;\n\t}\n\n\t/**\n\t * Convert Java time zone to postgres time zone. All others stay the same\n\t * except that GMT+nn changes to GMT-nn and vise versa.\n\t * \n\t * @return The current JVM time zone in postgresql format.\n\t */\n\tpublic static String createPostgresTimeZone() {\n\t\tString tz = TimeZone.getDefault().getID();\n\t\tif (tz.length() <= 3 || !tz.startsWith(\"GMT\")) {\n\t\t\treturn tz;\n\t\t}\n\t\tchar sign = tz.charAt(3);\n\t\tString start;\n\t\tif (sign == '+') {\n\t\t\tstart = \"GMT-\";\n\t\t} else if (sign == '-') {\n\t\t\tstart = \"GMT+\";\n\t\t} else {\n\t\t\t// unknown type\n\t\t\treturn tz;\n\t\t}\n\t\treturn start + tz.substring(4);\n\t}\n\n\tpublic static ByteBuffer makeStartUpPacket(String user, String database)\n\t\t\tthrows IOException {\n\t\tList<String[]> paramList = new ArrayList<String[]>();\n\t\tString appName = \"MyCat-Server\";\n\t\tparamList.add(new String[] { \"user\", user });\n\t\tparamList.add(new String[] { \"database\", database });\n\t\tparamList.add(new String[] { \"client_encoding\", \"UTF8\" });\n\t\tparamList.add(new String[] { \"DateStyle\", \"ISO\" });\n\t\tparamList.add(new String[] { \"TimeZone\", createPostgresTimeZone() });\n\t\tparamList.add(new String[] { \"extra_float_digits\", \"3\" });\n\t\tparamList.add(new String[] { \"application_name\", appName });\n\t\tString[][] params = paramList.toArray(new String[0][]);\n\t\tStringBuilder details = new StringBuilder();\n\t\tfor (int i = 0; i < params.length; ++i) {\n\t\t\tif (i != 0) {\n\t\t\t\tdetails.append(\", \");\n\t\t\t}\n\t\t\tdetails.append(params[i][0]);\n\t\t\tdetails.append(\"=\");\n\t\t\tdetails.append(params[i][1]);\n\t\t}\n\n\t\t/*\n\t\t * Precalculate message length and encode params.\n\t\t */\n\t\tint length = 4 + 4;\n\t\tbyte[][] encodedParams = new byte[params.length * 2][];\n\t\tfor (int i = 0; i < params.length; ++i) {\n\t\t\tencodedParams[i * 2] = params[i][0].getBytes(\"UTF-8\");\n\t\t\tencodedParams[i * 2 + 1] = params[i][1].getBytes(\"UTF-8\");\n\t\t\tlength += encodedParams[i * 2].length + 1\n\t\t\t\t\t+ encodedParams[i * 2 + 1].length + 1;\n\t\t}\n\n\t\tlength += 1; // Terminating \\0\n\n\t\tByteBuffer buffer = ByteBuffer.allocate(length);\n\n\t\t/*\n\t\t * Send the startup message.\n\t\t */\n\t\tPIOUtils.SendInteger4(length, buffer);\n\t\tPIOUtils.SendInteger2(3, buffer); // protocol major\n\t\tPIOUtils.SendInteger2(0, buffer); // protocol minor\n\t\tfor (byte[] encodedParam : encodedParams) {\n\t\t\tPIOUtils.Send(encodedParam, buffer);\n\t\t\tPIOUtils.SendChar(0, buffer);\n\t\t}\n\t\tPIOUtils.Send(new byte[] { 0 }, buffer);\t\t\n\t\treturn buffer;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/utils/PgPacketApaterUtils.java",
    "content": "package io.mycat.backend.postgresql.utils;\n\nimport io.mycat.backend.postgresql.packet.DataRow;\nimport io.mycat.backend.postgresql.packet.DataRow.DataColumn;\nimport io.mycat.backend.postgresql.packet.PostgreSQLPacket.DateType;\nimport io.mycat.backend.postgresql.packet.RowDescription;\nimport io.mycat.backend.postgresql.packet.RowDescription.ColumnDescription;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.RowDataPacket;\n\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.List;\n\n\n/*********\n * 数据包适配\n * @author Coollf\n *\n */\npublic class PgPacketApaterUtils {\n\tprivate static final Charset UTF8 = Charset.forName(\"utf-8\");\n\n\t/**\n\t * 列标示转换成Mysql的数据\n\t * @param description\n\t * @return\n\t */\n\tpublic static List<FieldPacket> rowDescConvertFieldPacket(RowDescription description){\n\t\tList<FieldPacket>  fieldPks = new ArrayList<FieldPacket>(description.getColumnNumber());\n\t\tfor(ColumnDescription c: description.getColumns()){\n\t\t\tFieldPacket fieldPk = new FieldPacket();\n\t\t\tfieldPk.name = c.getColumnName().trim().getBytes(UTF8);\n\t\t\tfieldPk.type = convertFieldType(c.getColumnType());\n\t\t\tfieldPks.add(fieldPk);\n\t\t}\n\t\t//TODO 等待实现\n\t\treturn fieldPks;\t\t\n\t}\n\t\n\t/***\n\t * 将pg的sql类型转换成\n\t * @param columnType\n\t * @return\n\t */\n\tprivate static int convertFieldType(DateType columnType) {\n\t\tif(columnType == DateType.timestamp_){\n\t\t\treturn Fields.FIELD_TYPE_TIMESTAMP;\n\t\t}\n\t\tif(columnType == DateType.int2_ || columnType == DateType.int4_ || columnType == DateType.int8_ ){\n\t\t\treturn Fields.FIELD_TYPE_INT24;\n\t\t}\n\t\tif(columnType == DateType.decimal_){\n\t\t\treturn Fields.FIELD_TYPE_NEW_DECIMAL;\n\t\t}\t\t\n\t\tif(columnType == DateType.UNKNOWN){\n\t\t\n\t\t}\n\t\treturn Fields.FIELD_TYPE_VARCHAR;\n\t}\n\n\n\t/***\n\t * 行数据转换成mysql的数据\n\t * @param dataRow\n\t * @return\n\t */\n\tpublic static RowDataPacket rowDataConvertRowDataPacket(DataRow dataRow){\n\t\tRowDataPacket curRow = new RowDataPacket(dataRow.getColumnNumber());\n\t\tfor(DataColumn c: dataRow.getColumns()){\n\t\t\tcurRow.add(c.getData());\n\t\t}\t\t\n\t\treturn curRow;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/backend/postgresql/utils/PgSqlApaterUtils.java",
    "content": "package io.mycat.backend.postgresql.utils;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n\n\npublic class PgSqlApaterUtils {\n\t/**\n\t * 查询表结构\n\t */\n\tprivate  static final  String SHOW_TABLE_STATUS_SQL_PREFIX =\"SHOW TABLE STATUS LIKE\";\n\n\t/*******\n\t * 展示建表语句\n\t */\n\tprivate  static  final  String SHOW_CREATE_TABLE_SQL_PREFIX  = \"SHOW CREATE TABLE\";\n\n\t/**\n\t * 表机构信息 ,包含主键\n\t */\n\tprivate  static  final  String  SHOW_COLUMNS_SQL_PREFIX =\"SHOW COLUMNS FROM\";\n\n\n\n\tpublic static String apater(String sql){\n\t\tsql =  sql.replaceAll(\"`\",\"\\\"\");\n\t\tfinal String SQL = sql.toUpperCase().replaceAll(\"`\",\"\\\"\");\n\t\tString _mapperSql  = stream.get(SQL);\n\t\tif(_mapperSql!=null){\n\t\t\treturn _mapperSql;\n\t\t}\n\t\tif (SQL.startsWith(SHOW_TABLE_STATUS_SQL_PREFIX)){\n\t\t\treturn doApaterTableStatusSql(SQL);\n\t\t}\n\n\t\tif(SQL.startsWith(SHOW_CREATE_TABLE_SQL_PREFIX)){\n\t\t\treturn doApaterCreateTabelSql(SQL);\n\t\t}\n\n\t\tif(SQL.startsWith(SHOW_COLUMNS_SQL_PREFIX)){\n\t\t\treturn doApaterColumnsSql(SQL);\n\t\t}\n\n\t\tif(SQL.indexOf(\"LIMIT\")!=-1 && SQL.indexOf(\"OFFSET\") == -1){//非pgsql 分页语句\n\t\t\treturn  doApaterPagingSql(SQL,sql);\n\t\t}\n\n\t\treturn sql;\n\t}\n\n\n\t/*******\n\t *  获取列信息SQL  语句\n\t * @param sql\n\t * @return\n     */\n\tprivate static String doApaterColumnsSql(String sql) {\n\t\treturn \"SELECT '' as \\\"Field\\\" ,'' as \\\"Type\\\" ,''as \\\"Null\\\" ,'' as \\\"Key\\\" ,'' as \\\"Default\\\" , '' as \\\"Extra\\\"  from pg_namespace where 1=2\";\n\t}\n\n\n\tprivate static String doApaterPagingSql(final String SQL, String sql) {\n\t\tint index = SQL.indexOf(\"LIMIT\");\n\t\tString pagingPart = sql.substring(index);\n\t\tString selectPart = sql.substring(0, index);\n\t\tString[] pk = pagingPart.split(\"(\\\\s+)|(,)\");\n\t\tList<String> slices = new ArrayList<String>();\n\t\tfor (String token : pk) {\n\t\t\tif (token.trim().length() > 0) {\n\t\t\t\tslices.add(token);\n\t\t\t}\n\t\t}\n\n\t\tif (slices.size() == 3) {\n\t\t\treturn selectPart\n\t\t\t\t\t+ String.format(\"%s  %s  offset %s\", slices.get(0),\n\t\t\t\t\t\t\tslices.get(2), slices.get(1));\n\t\t}\n\t\tif (slices.size() == 2) {\n\t\t\treturn selectPart\n\t\t\t\t\t+ String.format(\" %s %s offset 0 \", slices.get(0),\n\t\t\t\t\t\t\tslices.get(1));\n\t\t}\n\n\t\treturn sql;// 无法处理分页sql原样返回\n\t}\n\n\tprivate static String doApaterCreateTabelSql(String sql) {\n\t\treturn \"select '' as Table ,'' as \\\"Create Table\\\" from pg_namespace where 1=2\";\n\t}\n\n\n\t/********\n\t * 进行表结构语句适配\n\t * @param sql\n\t * @return\n     */\n\tprivate static String doApaterTableStatusSql(String sql) {\n\t\tString tableName  =sql.substring(SHOW_TABLE_STATUS_SQL_PREFIX.length());\n\t\t StringBuilder sb = new StringBuilder();\n\t\tsb.append(\"SELECT\").append(\" \");\n\t\tsb.append(\"\tattname AS NAME,\").append(\" \");\n\t\tsb.append(\"\t'InnoDB' AS Engine,\").append(\" \");\n\t\tsb.append(\"\t10 AS VERSION,\").append(\" \");\n\t\tsb.append(\"\t'Compact' AS Row_format,\").append(\" \");\n\t\tsb.append(\"\t0 AS ROWS,\").append(\" \");\n\t\tsb.append(\"\t10000 AS Avg_row_length,\").append(\" \");\n\t\tsb.append(\"\t10000 AS Data_length,\").append(\" \");\n\t\tsb.append(\"\t0 AS Max_data_length,\").append(\" \");\n\t\tsb.append(\"\t0 AS Index_length,\").append(\" \");\n\t\tsb.append(\"\t0 AS Data_free,\").append(\" \");\n\t\tsb.append(\"\tNULL AS Auto_increment,\").append(\" \");\n\t\tsb.append(\"\tNULL AS Create_time,\").append(\" \");\n\t\tsb.append(\"\tNULL AS Update_time,\").append(\" \");\n\t\tsb.append(\"\tNULL AS Check_time,\").append(\" \");\n\t\tsb.append(\"\t'utf8_general_ci' AS COLLATION,\").append(\" \");\n\t\tsb.append(\"\tNULL AS Checksum,\").append(\" \");\n\t\tsb.append(\"\t'' AS Create_options,\").append(\" \");\n\t\tsb.append(\"\t'' AS COMMENT\").append(\" \");\n\t\tsb.append(\"FROM\").append(\" \");\n\t\tsb.append(\"\tpg_attribute\").append(\" \");\n\t\tsb.append(\"INNER JOIN pg_class ON pg_attribute.attrelid = pg_class.oid\").append(\" \");\n\t\tsb.append(\"INNER JOIN pg_type ON pg_attribute.atttypid = pg_type.oid\").append(\" \");\n\t\tsb.append(\"LEFT OUTER JOIN pg_attrdef ON pg_attrdef.adrelid = pg_class.oid\").append(\" \");\n\t\tsb.append(\"AND pg_attrdef.adnum = pg_attribute.attnum\").append(\" \");\n\t\tsb.append(\"LEFT OUTER JOIN pg_description ON pg_description.objoid = pg_class.oid\").append(\" \");\n\t\tsb.append(\"AND pg_description.objsubid = pg_attribute.attnum\").append(\" \");\n\t\tsb.append(\"WHERE\").append(\" \");\n\t\tsb.append(\"\tpg_attribute.attnum > 0\").append(\" \");\n\t\tsb.append(\"AND attisdropped <> 't'\").append(\" \");\n\t\tsb.append(\"AND pg_class.relname =\").append(tableName).append(\" \");\n\t\tsb.append(\"ORDER BY\").append(\" \");\n\t\tsb.append(\"\tpg_attribute.attnum\").append(\" \");\n\t\treturn  sb.toString();\n\t}\n\n\n\tpublic static Map<String, String>  stream = new HashMap<>();\n\t\n\t\n\tstatic{\n\t\tstream.put(\"SELECT @@CHARACTER_SET_DATABASE, @@COLLATION_DATABASE\".toUpperCase(), \"SELECT 'utf8' as \\\"@@character_set_database\\\", 'utf8_general_ci' as \\\"@@collation_database\\\"\");\n\t\tstream.put(\"SHOW STATUS\", \"SELECT 'Aborted_clients' as \\\"Variable\\\" , 0 as \\\"Value\\\" where 1=2 \");\n\t\tstream.put(\"SHOW FULL TABLES WHERE Table_type != 'VIEW'\".toUpperCase(), \"select tablename as \\\"Tables_In_\\\",'BASE TABLE' as \\\"Table_Type\\\" from pg_tables where schemaname ='public'\");\n\t\tstream.put(\"SHOW ENGINES\",\"SELECT DISTINCT 'InnoDB' as Engine ,\\t'DEFAULT' as Support , \\t'Supports transactions,row-level locking and foreign keys' as \\\"Comment\\\"\\t,'YES' as \\\"Transactions\\\" ,\\t'YES' as \\\"XA\\\",'YES' as \\\"Savepoints\\\" from  pg_tablespace\\n\");\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/buffer/BufferArray.java",
    "content": "package io.mycat.buffer;\n\nimport io.mycat.util.ByteBufferUtil;\n\nimport java.nio.ByteBuffer;\nimport java.util.Collections;\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * used for large data write ,composed by buffer array, when a large MySQL\n * package write ,shoud use this object to write data\n *\n *  use DirectByteBuffer for alloc buffer\n * @author wuzhih\n * @author zagnix\n */\npublic class BufferArray {\n\tprivate final BufferPool bufferPool;\n\tprivate ByteBuffer curWritingBlock;\n\tprivate List<ByteBuffer> writedBlockLst = Collections.emptyList();\n\n\tpublic BufferArray(BufferPool bufferPool) {\n\t\tsuper();\n\t\tthis.bufferPool = bufferPool;\n\t\tcurWritingBlock = bufferPool.allocate(bufferPool.getChunkSize());\n\t}\n\n\tpublic ByteBuffer checkWriteBuffer(int capacity) {\n\t\tif (capacity > curWritingBlock.remaining()) {\n\t\t\taddtoBlock(curWritingBlock);\n\t\t\tcurWritingBlock = bufferPool.allocate(capacity);\n\t\t\treturn curWritingBlock;\n\t\t} else {\n\t\t\treturn curWritingBlock;\n\t\t}\n\t}\n\n\tpublic int getBlockCount()\n\t{\n\t\treturn writedBlockLst.size()+1;\n\t}\n\tprivate void addtoBlock(ByteBuffer buffer) {\n\t\tif (writedBlockLst.isEmpty()) {\n\t\t\twritedBlockLst = new LinkedList<ByteBuffer>();\n\t\t}\n\t\twritedBlockLst.add(buffer);\n\t}\n\n\tpublic ByteBuffer getCurWritingBlock() {\n\t\treturn curWritingBlock;\n\t}\n\n\tpublic List<ByteBuffer> getWritedBlockLst() {\n\t\treturn writedBlockLst;\n\t}\n\n\tpublic void clear() {\n\t\tcurWritingBlock = null;\n\t\twritedBlockLst.clear();\n\t\twritedBlockLst = null;\n\t}\n\n\tpublic ByteBuffer write(byte[] src) {\n\t\tint offset = 0;\n\t\tint remains = src.length;\n\t\twhile (remains > 0) {\n\t\t\tint writeable = curWritingBlock.remaining();\n\t\t\tif (writeable >= remains) {\n\t\t\t\t// can write whole srce\n\t\t\t\tcurWritingBlock.put(src, offset, remains);\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\t// can write partly\n\t\t\t\tcurWritingBlock.put(src, offset, writeable);\n\t\t\t\toffset += writeable;\n\t\t\t\tremains -= writeable;\n\t\t\t\taddtoBlock(curWritingBlock);\n\t\t\t\tcurWritingBlock = bufferPool.allocate(bufferPool.getChunkSize());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\treturn curWritingBlock;\n\t}\n\n\n    public byte[] writeToByteArrayAndRecycle() {\n        BufferArray bufferArray=this;\n        try {\n\n              int size=0;\n            List<ByteBuffer> blockes = bufferArray.getWritedBlockLst();\n            if (!bufferArray.getWritedBlockLst().isEmpty()) {\n                for (ByteBuffer curBuf : blockes) {\n                    curBuf.flip();\n                    size+=curBuf.remaining();\n                }\n            }\n            ByteBuffer curBuf = bufferArray.getCurWritingBlock();\n            curBuf.flip();\n            if(curBuf.hasRemaining())\n            {\n                size += curBuf.remaining();\n            }\n            if(size>0)\n            {\n                int offset=0;\n                byte[] all=new byte[size];\n                if (!bufferArray.getWritedBlockLst().isEmpty()) {\n                    for (ByteBuffer tBuf : blockes) {\n\n                        ByteBufferUtil.arrayCopy(tBuf,0,all,offset,tBuf.remaining());\n                        offset+=tBuf.remaining();\n\n                        bufferPool.recycle(tBuf);\n                    }\n                }\n                ByteBuffer tBuf = bufferArray.getCurWritingBlock();\n                if(tBuf.hasRemaining())\n                {\n                    ByteBufferUtil.arrayCopy(tBuf,0,all,offset,tBuf.remaining());\n                    bufferPool.recycle(tBuf);\n                   // offset += curBuf.remaining();\n                }\n                return all;\n            }\n\n        } finally {\n\n            bufferArray.clear();\n        }\n\n      return EMPTY;\n    }\n\n    private static byte[] EMPTY=new byte[0];\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/buffer/BufferPool.java",
    "content": "package io.mycat.buffer;\n\nimport java.nio.ByteBuffer;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 缓冲池\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 12:19 2016/5/23\n */\npublic interface BufferPool {\n    public ByteBuffer allocate(int size);\n    public void recycle(ByteBuffer theBuf);\n    public long capacity();\n    public long size();\n    public int getConReadBuferChunk();\n    public  int getSharedOptsCount();\n    public int getChunkSize();\n    public ConcurrentHashMap<Long,Long> getNetDirectMemoryUsage();\n    public BufferArray allocateArray();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/buffer/ByteBufferArena.java",
    "content": "package io.mycat.buffer;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.nio.ByteBuffer;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * 仿照Netty的思路，针对MyCat内存缓冲策略优化\n * ByteBufferArena维护着锁还有所有list\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 17:19 2016/5/17\n * @see @https://github.com/netty/netty\n */\npublic class ByteBufferArena implements BufferPool {\n    private static final Logger LOGGER = LoggerFactory.getLogger(ByteBufferChunkList.class);\n    private final ByteBufferChunkList q[];\n\n    private final AtomicInteger chunkCount = new AtomicInteger(0);\n    private final AtomicInteger failCount = new AtomicInteger(0);\n\n    private static final int FAIL_THRESHOLD = 1000;\n    private final int pageSize;\n    private final int chunkSize;\n\n    private final AtomicLong capacity;\n    private final AtomicLong size;\n\n    private final ConcurrentHashMap<Thread, Integer> sharedOptsCount;\n\n    /**\n     * 记录对线程ID->该线程的所使用Direct Buffer的size\n     */\n    private final ConcurrentHashMap<Long,Long> memoryUsage;\n    private final int conReadBuferChunk;\n\n    public ByteBufferArena(int chunkSize, int pageSize, int chunkCount, int conReadBuferChunk) {\n        try {\n            this.chunkSize = chunkSize;\n            this.pageSize = pageSize;\n            this.chunkCount.set(chunkCount);\n            this.conReadBuferChunk = conReadBuferChunk;\n\n            q = new ByteBufferChunkList[6];\n            q[5] = new ByteBufferChunkList(100, Integer.MAX_VALUE, chunkSize, pageSize, 0);\n            q[4] = new ByteBufferChunkList(75, 100, chunkSize, pageSize, 0);\n            q[3] = new ByteBufferChunkList(50, 100, chunkSize, pageSize, 0);\n            q[2] = new ByteBufferChunkList(25, 75, chunkSize, pageSize, 0);\n            q[1] = new ByteBufferChunkList(1, 50, chunkSize, pageSize, 0);\n            q[0] = new ByteBufferChunkList(Integer.MIN_VALUE, 25, chunkSize, pageSize, chunkCount);\n\n            q[0].nextList = q[1];\n            q[1].nextList = q[2];\n            q[2].nextList = q[3];\n            q[3].nextList = q[4];\n            q[4].nextList = q[5];\n            q[5].nextList = null;\n\n            q[5].prevList = q[4];\n            q[4].prevList = q[3];\n            q[3].prevList = q[2];\n            q[2].prevList = q[1];\n            q[1].prevList = q[0];\n            q[0].prevList = null;\n\n            capacity = new AtomicLong(6 * chunkCount * chunkSize);\n            size = new AtomicLong(6 * chunkCount * chunkSize);\n            sharedOptsCount = new ConcurrentHashMap<>();\n            memoryUsage = new ConcurrentHashMap<>();\n        } finally {\n        }\n    }\n\n    @Override\n    public ByteBuffer allocate(int reqCapacity) {\n        try {\n            ByteBuffer byteBuffer = null;\n            int i = 0, count = 0;\n            while (byteBuffer == null) {\n                if (i > 5) {\n                    i = 0;\n                    count = failCount.incrementAndGet();\n                    if (count > FAIL_THRESHOLD) {\n                        try {\n                            expand();\n                        } finally {\n                        }\n                    }\n                }\n                byteBuffer = q[i].allocate(reqCapacity);\n                i++;\n            }\n//            if (count > 0) {\n//                System.out.println(\"count: \" + count);\n//                System.out.println(failCount.get());\n//            }\n//            printList();\n            capacity.addAndGet(-reqCapacity);\n            final Thread thread =  Thread.currentThread();\n            final long threadId = thread.getId();\n\n            if (memoryUsage.containsKey(threadId)){\n                memoryUsage.put(threadId,memoryUsage.get(thread.getId())+reqCapacity);\n            }else {\n                memoryUsage.put(threadId, (long) reqCapacity);\n            }\n            if (sharedOptsCount.containsKey(thread)) {\n                int currentCount = sharedOptsCount.get(thread);\n                currentCount++;\n                sharedOptsCount.put(thread,currentCount);\n            } else{\n                sharedOptsCount.put(thread,0);\n            }\n            return byteBuffer;\n        } finally {\n        }\n    }\n\n    private void expand() {\n        LOGGER.warn(\"Current Buffer Size is not enough! Expanding Byte buffer!\");\n        ByteBufferChunk byteBufferChunk = new ByteBufferChunk(pageSize, chunkSize);\n        q[0].byteBufferChunks.add(byteBufferChunk);\n        failCount.set(0);\n    }\n\n    @Override\n    public void recycle(ByteBuffer byteBuffer) {\n        final long size = byteBuffer != null?byteBuffer.capacity():0;\n        try {\n            int i;\n            for (i = 0; i < 6; i++) {\n                if (q[i].free(byteBuffer)) {\n                    break;\n                }\n            }\n            if (i > 5) {\n                LOGGER.warn(\"This ByteBuffer is not maintained in ByteBufferArena!\");\n                return;\n            }\n            final Thread thread =  Thread.currentThread();\n            final long threadId = thread.getId();\n\n            if (memoryUsage.containsKey(threadId)){\n                memoryUsage.put(threadId,memoryUsage.get(thread.getId())-size);\n            }\n            if (sharedOptsCount.containsKey(thread)) {\n                int currentCount = sharedOptsCount.get(thread);\n                currentCount--;\n                sharedOptsCount.put(thread,currentCount);\n            } else{\n                sharedOptsCount.put(thread,0);\n            }\n            capacity.addAndGet(byteBuffer.capacity());\n            return;\n        } finally {\n        }\n    }\n\n    private void printList() {\n        for (int i = 0; i < 6; i++) {\n            System.out.println(i + \":\" + q[i].byteBufferChunks.toString());\n        }\n    }\n\n    @Override\n    public long capacity() {\n        return capacity.get();\n    }\n\n    @Override\n    public long size() {\n        return size.get();\n    }\n\n    @Override\n    public int getConReadBuferChunk() {\n        return conReadBuferChunk;\n    }\n\n    @Override\n    public int getSharedOptsCount() {\n        final Set<Integer> integers = (Set<Integer>) sharedOptsCount.values();\n        int count = 0;\n        for(int i : integers){\n            count += i;\n        }\n        return count;\n    }\n\n    /**\n     * 这里pageSize就是DirectByteBuffer的chunksize\n     * @return\n     */\n    @Override\n    public int getChunkSize() {\n        return pageSize;\n    }\n\n    @Override\n    public ConcurrentHashMap<Long, Long> getNetDirectMemoryUsage() {\n        return memoryUsage;\n    }\n\n    @Override\n    public BufferArray allocateArray() {\n        return new BufferArray(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/buffer/ByteBufferChunk.java",
    "content": "package io.mycat.buffer;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport sun.nio.ch.DirectBuffer;\n\nimport java.nio.ByteBuffer;\n\n/**\n * 仿照Netty的思路，针对MyCat内存缓冲策略优化\n * Chunk由Page组成，是一块连续内存，由memoryMap和depthMap定义成一种平衡二叉树的管理结构\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 17:19 2016/5/17\n * @see @https://github.com/netty/netty\n */\npublic class ByteBufferChunk implements Comparable{\n    private static final Logger LOGGER = LoggerFactory.getLogger(ByteBufferChunk.class);\n    private final byte[] memoryMap;\n    private final byte[] depthMap;\n    private final ByteBuffer buf;\n\n    //in bytes\n    private final int pageSize;\n    //in bytes\n    private final int chunkSize;\n    private final int chunkPageSize;\n    private final int maxOrder;\n    private final byte unusable;\n    private final int log2PageSize;\n    final long bufAddress;\n\n    private int freeBytes;\n\n    ByteBufferChunk prev;\n    ByteBufferChunk next;\n    ByteBufferChunkList parent;\n\n    public ByteBufferChunk(int pageSize, int chunkSize) {\n\n        this.pageSize = pageSize;\n        this.chunkSize = chunkSize;\n        this.chunkPageSize = chunkSize / pageSize;\n        this.maxOrder = log2(this.chunkPageSize) + 1;\n        this.unusable = (byte) this.maxOrder;\n        this.freeBytes = chunkSize;\n        this.buf = ByteBuffer.allocateDirect(chunkSize);\n        this.bufAddress = ((DirectBuffer) buf).address();\n\n        this.depthMap = new byte[(1 << this.maxOrder)];\n        this.memoryMap = new byte[this.depthMap.length];\n\n        this.log2PageSize = log2(pageSize);\n\n        int memoryMapIndex = 1;\n        for (int d = 0; d < maxOrder; ++d) { // move down the tree one level at a time\n            int depth = 1 << d;\n            for (int p = 0; p < depth; ++p) {\n                // in each level traverse left to right and set value to the depth of subtree\n                memoryMap[memoryMapIndex] = (byte) d;\n                depthMap[memoryMapIndex] = (byte) d;\n                memoryMapIndex++;\n            }\n        }\n    }\n\n    public boolean isInThisChunk(ByteBuffer byteBuffer) {\n        long address = ((DirectBuffer) byteBuffer).address();\n        return (address >= bufAddress) && (address < bufAddress + chunkSize);\n    }\n\n    public int usage() {\n        final int freeBytes = this.freeBytes;\n        if (freeBytes == 0) {\n            return 100;\n        }\n\n        int freePercentage = (int) (freeBytes * 100L / chunkSize);\n        if (freePercentage == 0) {\n            return 99;\n        }\n        return 100 - freePercentage;\n    }\n\n    public synchronized ByteBuffer allocateRun(int normCapacity) {\n        if(normCapacity > chunkSize){\n            LOGGER.warn(\"try to acquire a buffer with larger size than chunkSize!\");\n            return null;\n        }\n        int d = this.maxOrder - 2 - (log2(normCapacity) - this.log2PageSize);\n        if (d > this.maxOrder - 1) {\n            d = maxOrder - 1;\n        }\n        int id = allocateNode(d);\n        if (id < 0) {\n            return null;\n        }\n        freeBytes -= runLength(id);\n\n        int start = calculateStart(id);\n        int end = start + runLength(id);\n\n        buf.limit(end);\n        buf.position(start);\n\n//        printMemoryMap();\n\n        return buf.slice();\n    }\n\n\n    private int calculateStart(int id) {\n        int count = 0;\n        for (int i = 1; i < depthMap.length; i++) {\n            if (depthMap[i] < depthMap[id]) {\n                continue;\n            } else if (depthMap[i] == depthMap[id]) {\n                if (i == id) {\n                    break;\n                } else {\n                    count += runLength(i);\n                }\n            } else {\n                break;\n            }\n        }\n        return count;\n    }\n\n    private int runLength(int id) {\n        // represents the size in #bytes supported by node 'id' in the tree\n        return 1 << log2(chunkSize) - depthMap[id];\n    }\n\n    private int allocateNode(int d) {\n        int id = 1;\n        int initial = -(1 << d); // has last d bits = 0 and rest all = 1\n        byte val = memoryMap[id];\n        if (val > d) { // unusable\n            return -1;\n        }\n\n        while (val < d || (id & initial) == 0) { // id & initial == 1 << d for all ids at depth d, for < d it is 0\n            id <<= 1;\n            val = memoryMap[id];\n            if (val > d) {\n                id ^= 1;\n                val = memoryMap[id];\n            }\n        }\n        byte value = memoryMap[id];\n        assert value == d && (id & initial) == 1 << d : String.format(\"val = %d, id & initial = %d, d = %d\",\n                value, id & initial, d);\n        memoryMap[id] = unusable; // mark as unusable\n        updateParentsAlloc(id);\n        return id;\n    }\n\n    private void updateParentsAlloc(int id) {\n        while (id > 1) {\n            int parentId = id >>> 1;\n            byte val1 = memoryMap[id];\n            byte val2 = memoryMap[id ^ 1];\n            byte val = val1 < val2 ? val1 : val2;\n            memoryMap[parentId] = val;\n            id = parentId;\n        }\n    }\n\n    public synchronized void freeByteBuffer(ByteBuffer byteBuffer) {\n        long address = ((DirectBuffer) byteBuffer).address();\n        int relativeAddress = (int) (address - bufAddress);\n        int length = byteBuffer.capacity();\n\n        int depth = maxOrder - 1 - log2(length / pageSize);\n        int count = 0;\n        int i;\n        for (i = 0; i < depthMap.length; i++) {\n            if (depthMap[i] == depth) {\n                if (count == relativeAddress) {\n                    break;\n                }\n                count += length;\n            }\n            if (depthMap[i] > depth) {\n                break;\n            }\n        }\n        free(i);\n    }\n\n    private void free(int handle) {\n        if (memoryMap[handle] != depthMap[handle]) {\n            freeBytes += runLength(handle);\n            memoryMap[handle] = depthMap[handle];\n            updateParentsFree(handle);\n        }\n    }\n\n    private void updateParentsFree(int id) {\n        int logChild = depthMap[id] + 1;\n        while (id > 1) {\n            int parentId = id >>> 1;\n            byte val1 = memoryMap[id];\n            byte val2 = memoryMap[id ^ 1];\n            logChild -= 1; // in first iteration equals log, subsequently reduce 1 from logChild as we traverse up\n\n            if (val1 == logChild && val2 == logChild) {\n                memoryMap[parentId] = (byte) (logChild - 1);\n            } else {\n                byte val = val1 < val2 ? val1 : val2;\n                memoryMap[parentId] = val;\n            }\n\n            id = parentId;\n        }\n    }\n\n    private static int log2(int chunkSize) {\n        if (chunkSize <= 0) {\n            LOGGER.warn(\"invalid parameter!\");\n            throw new IllegalArgumentException();\n        }\n        return Integer.SIZE - 1 - Integer.numberOfLeadingZeros(chunkSize);\n    }\n\n    private void printMemoryMap() {\n        int l = 1;\n        for (int i = 0; i < this.maxOrder; i++) {\n            int j = (int) Math.pow(2, i);\n            for (int k = 0; k < j; k++) {\n                System.out.print(this.memoryMap[l] + \"|\");\n                l++;\n            }\n            System.out.println();\n        }\n        System.out.println();\n    }\n\n    public static void main(String[] args) {\n\n        int pageSize = 256;\n        int chunkSize = 1024 * 1024 * 64;\n        ByteBufferChunk byteBufferChunk = new ByteBufferChunk(pageSize, chunkSize);\n        int chunkCount = 8;\n        int allocTimes = 102400;\n        long start = System.currentTimeMillis();\n        for (int i = 0; i < allocTimes; i++) {\n//            System.out.println(\"allocate \"+i);\n//            long start=System.nanoTime();\n            int size = 256;\n            ByteBuffer byteBufer = byteBufferChunk.allocateRun(size);\n//            System.out.println(\"alloc \"+size+\" usage \"+(System.nanoTime()-start));\n//            start=System.nanoTime();\n//            byteBufferArena.recycle(byteBufer);\n//            System.out.println(\"recycle usage \"+(System.nanoTime()-start));\n        }\n        long used = (System.currentTimeMillis() - start);\n        System.out.println(\"total used time  \" + used + \" avg speed \" + allocTimes / used);\n    }\n\n    @Override\n    public int compareTo(Object o) {\n        return -1;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/buffer/ByteBufferChunkList.java",
    "content": "package io.mycat.buffer;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentSkipListSet;\n\n/**\n * 仿照Netty的思路，针对MyCat内存缓冲策略优化\n * ChunkList维护着一个指向一串Chunk的头结点，访问策略由minUsage，maxUsage决定\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 17:19 2016/5/17\n * @see @https://github.com/netty/netty\n */\npublic class ByteBufferChunkList {\n    private static final Logger LOGGER = LoggerFactory.getLogger(ByteBufferChunkList.class);\n    private final int minUsage;\n    private final int maxUsage;\n\n    Set<ByteBufferChunk> byteBufferChunks;\n    ByteBufferChunkList prevList;\n    ByteBufferChunkList nextList;\n\n    public ByteBufferChunkList(int minUsage, int maxUsage, int chunkSize, int pageSize, int numOfChunks) {\n        this.minUsage = minUsage;\n        this.maxUsage = maxUsage;\n        byteBufferChunks = new ConcurrentSkipListSet<>();\n        for (int i = 0; i < numOfChunks; i++) {\n            ByteBufferChunk chunk = new ByteBufferChunk(pageSize, chunkSize);\n            byteBufferChunks.add(chunk);\n        }\n    }\n\n    public ByteBufferChunk getIndex(ByteBuffer buffer) {\n        for(ByteBufferChunk byteBufferChunk : byteBufferChunks){\n            if (byteBufferChunk.isInThisChunk(buffer)) {\n                return byteBufferChunk;\n            }\n        }\n        return null;\n    }\n\n    ByteBuffer allocate(int reqCapacity) {\n        for (ByteBufferChunk cur : byteBufferChunks) {\n            ByteBuffer buf = cur.allocateRun(reqCapacity);\n            if (buf == null) {\n                continue;\n            } else {\n                final int usage = cur.usage();\n                if (usage >= maxUsage) {\n                    ByteBufferChunkList next = nextList;\n                    ByteBufferChunkList current = this;\n                    while (next != null) {\n                        current.byteBufferChunks.remove(cur);\n                        next.byteBufferChunks.add(cur);\n                        if (next.maxUsage > usage) {\n                            break;\n                        }\n                        current = next;\n                        next = next.nextList;\n                    }\n                }\n                return buf;\n            }\n        }\n        return null;\n    }\n\n    boolean free(ByteBuffer buffer) {\n        ByteBufferChunk cur = getIndex(buffer);\n        if (cur == null) {\n            LOGGER.info(\"not in this list!\");\n            return false;\n        }\n        cur.freeByteBuffer(buffer);\n        final int usage = cur.usage();\n        if (usage < minUsage) {\n            ByteBufferChunkList prev = prevList;\n            ByteBufferChunkList current = this;\n            while (prev != null) {\n                current.byteBufferChunks.remove(cur);\n                prev.byteBufferChunks.add(cur);\n                if (prev.minUsage < usage) {\n                    break;\n                }\n                current = prev;\n                prev = prev.prevList;\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/buffer/ByteBufferPage.java",
    "content": "package io.mycat.buffer;\n\nimport java.nio.ByteBuffer;\nimport java.util.BitSet;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport sun.nio.ch.DirectBuffer;\n\n/*\n * 用来保存一个一个ByteBuffer为底层存储的内存页\n */\n@SuppressWarnings(\"restriction\")\npublic class ByteBufferPage {\n\n    private final ByteBuffer buf;\n    private final int chunkSize;    //chunk的大小 一般为4k\n    private final int chunkCount;  //chunk的个数\n    private final BitSet chunkAllocateTrack; //某个chunk是否被分配\n    private final AtomicBoolean allocLockStatus = new AtomicBoolean(false);  //锁\n    private final long startAddress;\n    private final ConcurrentHashMap<Long, Long> relationBufferThreadId;\n\n    public ByteBufferPage(ByteBuffer buf, int chunkSize) {\n        super();\n        this.chunkSize = chunkSize;\n        chunkCount = buf.capacity() / chunkSize;\n        chunkAllocateTrack = new BitSet(chunkCount);\n        relationBufferThreadId = new ConcurrentHashMap<>(chunkCount);\n        this.buf = buf;\n        startAddress = ((sun.nio.ch.DirectBuffer) buf).address();\n    }\n\n    public ByteBuffer allocatChunk(int theChunkCount) {\n    \t//加锁 成功执行不成功返回\n        if (!allocLockStatus.compareAndSet(false, true)) {\n            return null;\n        }\n        int startChunk = -1;//从startChunk开始的\n        int contiueCount = 0;//连续的chunk个数\n        try {\n        \t///枚举寻找连续的N个chunk\n            for (int i = 0; i < chunkCount; i++) {\n            \t//找到一个可用的\n                if (chunkAllocateTrack.get(i) == false) {\n                \t//如果是第一个 则设置startChunk\n                \tif (startChunk == -1) {\n                    \t//从头开始找\n                        startChunk = i;\n                        contiueCount = 1; \n                        if (theChunkCount == 1) {\n                            break;\n                        }\n                    } else {\n                    \t//连续chunk个数加一 ,是否找到连续的chunk个数,是则返回.\n                        if (++contiueCount == theChunkCount) {\n                            break;\n                        }\n                    }\n                } else {\n                \t//不连续了\n                    startChunk = -1;\n                    contiueCount = 0;\n                }\n            }\n            //找到了 \n            if (contiueCount == theChunkCount) {\n                int offStart = startChunk * chunkSize;\n                int offEnd = offStart + theChunkCount * chunkSize;\n                buf.limit(offEnd);\n                buf.position(offStart);\n\n                ByteBuffer newBuf = buf.slice(); //分配buffer\n                //sun.nio.ch.DirectBuffer theBuf = (DirectBuffer) newBuf;\n                //System.out.println(\"offAddress \" + (theBuf.address() - startAddress));\n                //设置chunk为已用\n                markChunksUsed(startChunk, theChunkCount);\n                relationBufferThreadId.put(((DirectBuffer) newBuf).address(), Thread.currentThread().getId());\n\n                return newBuf;\n            } else {\n                //System.out.println(\"contiueCount \" + contiueCount + \" theChunkCount \" + theChunkCount);\n                return null;\n            }\n        } finally {\n            allocLockStatus.set(false);\n        }\n    }\n    //设置已用\n    private void markChunksUsed(int startChunk, int theChunkCount) {\n        for (int i = 0; i < theChunkCount; i++) {\n            chunkAllocateTrack.set(startChunk + i);\n        }\n    }\n    //清空不可用\n    private void markChunksUnused(int startChunk, int theChunkCount) {\n        for (int i = 0; i < theChunkCount; i++) {\n            chunkAllocateTrack.clear(startChunk + i);\n        }\n    }\n\n    /**\n     * 回收buffer\n     * @param parent      当前要释放的buf的parent \n     * @param recycleBuf  当前要释放的recycleBuf\n     * @param startChunk\n     * @param chunkCount\n     * @param relatedThreadId  用于返回当前要释放的recycleBuf关联的线程id\n     * @return\n     */\n    public boolean recycleBuffer(ByteBuffer parent, ByteBuffer recycleBuf, int startChunk, int chunkCount,\n            StringBuilder relatedThreadId) {\n\n        if (parent == this.buf) {\n        \t//获取锁 必须获取成功\n            while (!this.allocLockStatus.compareAndSet(false, true)) {\n                Thread.yield();\n            }\n            //清空已用状态\n            try {\n                markChunksUnused(startChunk,chunkCount);\n                Long threadId = relationBufferThreadId.remove(((DirectBuffer) recycleBuf).address());\n                if (threadId != null) {\n                    relatedThreadId.append(threadId);\n                }\n            } finally {\n            \t//释放锁\n                allocLockStatus.set(false);\n            }\n            return true;\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/buffer/DirectByteBufferPool.java",
    "content": "package io.mycat.buffer;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport sun.nio.ch.DirectBuffer;\n\nimport java.nio.ByteBuffer;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * DirectByteBuffer池，可以分配任意指定大小的DirectByteBuffer，用完需要归还\n * @author wuzhih\n * @author zagnix\n */\n@SuppressWarnings(\"restriction\")\npublic class DirectByteBufferPool implements BufferPool{\n    private static final Logger LOGGER = LoggerFactory.getLogger(DirectByteBufferPool.class);\n    public static final String LOCAL_BUF_THREAD_PREX = \"$_\";\n    private ByteBufferPage[] allPages;\n    private final int chunkSize;\n   // private int prevAllocatedPage = 0;\n    //private AtomicInteger prevAllocatedPage;\n    private AtomicLong prevAllocatedPage;\n    private final  int pageSize;\n    private final short pageCount;\n    private final int conReadBuferChunk ;\n\n     /**\n     * 记录对线程ID->该线程的所使用Direct Buffer的size\n     */\n    private final ConcurrentHashMap<Long,Long> memoryUsage;\n\n    public DirectByteBufferPool(int pageSize, short chunkSize, short pageCount,int conReadBuferChunk) {\n        allPages = new ByteBufferPage[pageCount];\n        this.chunkSize = chunkSize;\n        this.pageSize = pageSize;\n        this.pageCount = pageCount;\n        this.conReadBuferChunk = conReadBuferChunk;\n        //prevAllocatedPage = new AtomicInteger(0);\n        prevAllocatedPage = new AtomicLong(0);\n        for (int i = 0; i < pageCount; i++) {\n            allPages[i] = new ByteBufferPage(ByteBuffer.allocateDirect(pageSize), chunkSize);\n        }\n        memoryUsage = new ConcurrentHashMap<>();\n    }\n\n    public BufferArray allocateArray() {\n        return new BufferArray(this);\n    }\n    /**\n     * TODO 当页不够时，考虑扩展内存池的页的数量...........\n     * @param buffer\n     * @return\n     */\n    public  ByteBuffer expandBuffer(ByteBuffer buffer){\n        int oldCapacity = buffer.capacity();\n        int newCapacity = oldCapacity << 1;\n        ByteBuffer newBuffer = allocate(newCapacity);\n        if(newBuffer != null){\n            int newPosition = buffer.position();\n            buffer.flip();\n            newBuffer.put(buffer);\n            newBuffer.position(newPosition);\n            recycle(buffer);\n            return  newBuffer;\n        }\n        return null;\n    }\n\n    public ByteBuffer allocate(int size) {\n       final int theChunkCount = size / chunkSize + (size % chunkSize == 0 ? 0 : 1);\n        int selectedPage =  (int)(prevAllocatedPage.incrementAndGet() % allPages.length);\n        ByteBuffer byteBuf = allocateBuffer(theChunkCount, 0, selectedPage);\n        if (byteBuf == null) {\n            byteBuf = allocateBuffer(theChunkCount, selectedPage, allPages.length);\n        }\n        final long threadId = Thread.currentThread().getId();\n\n        if(byteBuf !=null){\n\n            // 这里必须加锁，因为并发情况下如果allocate和recycle函数操作同一个数据，假设它们都先get到数据，然后allocate先put操作，\n            // recycle后进行put操作，这样allocate的put的数据就被覆盖掉\n            final ByteBuffer finlBuffer = byteBuf;\n         memoryUsage.compute(threadId, (aLong, aLong2) -> {\n             if (aLong2 != null){\n                 return memoryUsage.get(threadId) + finlBuffer.capacity();\n             }else {\n                 return (long)finlBuffer.capacity();\n             }\n         });\n        }\n\n        if(byteBuf==null){\n            return  ByteBuffer.allocate(size);\n        }\n        return byteBuf;\n    }\n\n    public void recycle(ByteBuffer theBuf) {\n    \t//堆内buffer直接就清空就好\n      \tif(theBuf !=null && (!(theBuf instanceof DirectBuffer) )){\n    \t\ttheBuf.clear();\n    \t\treturn;\n         }\n\n\t\tfinal long size = theBuf.capacity();\n\n\t\tboolean recycled = false;\n        StringBuilder relatedThreadId = new StringBuilder();\n\n\t\tDirectBuffer thisNavBuf = (DirectBuffer) theBuf;//\n\t\tint chunkCount = theBuf.capacity() / chunkSize; //chunk的个数\n\t\tDirectBuffer parentBuf = (DirectBuffer) thisNavBuf.attachment(); //page的DirectBuffer\n\t\tint startChunk = (int) ((thisNavBuf.address() - parentBuf.address()) / chunkSize); //开始chunk的序号\n\t\tfor (int i = 0; i < allPages.length; i++) { //在所有的页面中查找当前buffer分配的\n            if ((recycled = allPages[i].recycleBuffer((ByteBuffer) parentBuf, theBuf, startChunk, chunkCount,\n                    relatedThreadId) == true)) {\n                break;\n            }\n        }\n\n        final Long threadId = relatedThreadId.length() > 0 ? Long.parseLong(relatedThreadId.toString())\n                : Thread.currentThread().getId();\n\n\t\tmemoryUsage.computeIfAbsent(threadId, aLong -> (long)(memoryUsage.get(threadId) - size));\n\n\t\tif (recycled == false) {\n\t\t\tLOGGER.warn(\"warning ,not recycled buffer \" + theBuf);\n\t\t}\n\n    }\n\n    private ByteBuffer allocateBuffer(int theChunkCount, int startPage, int endPage) {\n        for (int i = startPage; i < endPage; i++) {\n            ByteBuffer buffer = allPages[i].allocatChunk(theChunkCount);\n            if (buffer != null) {\n                prevAllocatedPage.getAndSet(i);\n                return buffer;\n            }\n        }\n        return null;\n    }\n\n    public int getChunkSize() {\n        return chunkSize;\n    }\n\n\t @Override\n    public ConcurrentHashMap<Long,Long> getNetDirectMemoryUsage() {\n        return memoryUsage;\n    }\n\n    public int getPageSize() {\n        return pageSize;\n    }\n\n    public short getPageCount() {\n        return pageCount;\n    }\n\n    public long capacity() {\n\treturn (long) pageSize * pageCount;\n    }\n\n    public long size(){\n        return  (long) pageSize * chunkSize * pageCount;\n    }\n\n    //TODO\n    public  int getSharedOptsCount(){\n        return 0;\n    }\n\n\n\n    public ByteBufferPage[] getAllPages() {\n\t\treturn allPages;\n\t}\n\n\tpublic int getConReadBuferChunk() {\n        return conReadBuferChunk;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/buffer/MyCatMemoryAllocator.java",
    "content": "package io.mycat.buffer;\n\nimport com.google.common.util.concurrent.ThreadFactoryBuilder;\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.ByteBufAllocator;\nimport io.netty.buffer.CompositeByteBuf;\nimport io.netty.buffer.PooledByteBufAllocator;\nimport io.netty.util.internal.PlatformDependent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.nio.ByteBuffer;\nimport java.util.concurrent.*;\n\n/**\n * Netty Direct Memory 分配器，为mycat提供内存池管理功能\n *\n * @author zagnix\n * @create 2017-01-18 11:01\n */\n\npublic class MyCatMemoryAllocator implements ByteBufAllocator {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(MyCatMemoryAllocator.class);\n    public final ConcurrentHashMap<Long,ByteBuf> recycleMaps = new ConcurrentHashMap<>();\n\n    private final static MyCatMemoryAllocator INSTANCE =\n           new MyCatMemoryAllocator(Runtime.getRuntime().availableProcessors()*2);\n\n    /** netty memory pool alloctor*/\n    private final PooledByteBufAllocator alloc;\n    /**arena 的数量，一般设置cpu cores*2 */\n    private final int numberOfArenas;\n\n    /** ChunkSize 大小 = pageSize << maxOrder */\n    private final int chunkSize;\n\n    /**页大小*/\n    private final int pageSize;\n\n    /**\n     * numberOfArenas 设置为处理器cores*2\n     * @param numberOfArenas\n     */\n    public MyCatMemoryAllocator(int numberOfArenas){\n        this.numberOfArenas = numberOfArenas;\n        if (!PlatformDependent.hasUnsafe()) {\n           LOGGER.warn(\"Using direct memory, but sun.misc.Unsafe not available.\");\n        }\n        boolean preferDirect = true;\n\n        this.pageSize = 8192*2;\n        int maxOrder = 11;\n        this.chunkSize = pageSize << maxOrder;\n        int numDirectArenas = numberOfArenas;\n        int numHeapArenas = 0;\n\n        /** for 4.1.x*/\n        this.alloc = new PooledByteBufAllocator(\n                preferDirect,\n                numHeapArenas,\n                numDirectArenas,\n                pageSize,\n                maxOrder,\n                512,\n                256,\n                64,\n                true);\n\n\n        /**for 5.0.x\n        this.alloc = new PooledByteBufAllocator(preferDirect);**/\n    }\n\n    public static MyCatMemoryAllocator getINSTANCE() {\n        return INSTANCE;\n    }\n\n    /**\n     * @return alloc\n     */\n    public PooledByteBufAllocator getAlloc() {\n        return alloc;\n    }\n\n    /**\n     * Returns the number of arenas.\n     *\n     * @return Number of arenas.\n     */\n    public int getNumberOfArenas() {\n        return numberOfArenas;\n    }\n\n    /**\n     * Returns the chunk size.\n     *\n     * @return Chunk size.\n     */\n    public int getChunkSize() {\n        return chunkSize;\n    }\n\n    /**\n     *   page Size\n     * @return page Size\n     */\n    public int getPageSize() {\n        return pageSize;\n    }\n\n\n    @Override\n    public ByteBuf buffer() {\n        return alloc.buffer();\n    }\n\n    @Override\n    public ByteBuf buffer(int initialCapacity) {\n        return alloc.buffer(initialCapacity);\n    }\n\n    @Override\n    public ByteBuf buffer(int initialCapacity, int maxCapacity) {\n        return alloc.buffer(initialCapacity, maxCapacity);\n    }\n\n    @Override\n    public ByteBuf ioBuffer() {\n        return alloc.ioBuffer();\n    }\n\n    @Override\n    public ByteBuf ioBuffer(int initialCapacity) {\n        return alloc.ioBuffer(initialCapacity);\n    }\n\n    @Override\n    public ByteBuf ioBuffer(int initialCapacity, int maxCapacity) {\n        return alloc.ioBuffer(initialCapacity, maxCapacity);\n    }\n\n    @Override\n    public ByteBuf heapBuffer() {\n        throw new UnsupportedOperationException(\"Heap buffer\");\n    }\n\n    @Override\n    public ByteBuf heapBuffer(int initialCapacity) {\n        throw new UnsupportedOperationException(\"Heap buffer\");\n    }\n\n    @Override\n    public ByteBuf heapBuffer(int initialCapacity, int maxCapacity) {\n        throw new UnsupportedOperationException(\"Heap buffer\");\n    }\n\n    @Override\n    public ByteBuf directBuffer() {\n        return alloc.directBuffer();\n    }\n\n    @Override\n    public ByteBuf directBuffer(int initialCapacity) {\n        return alloc.directBuffer(initialCapacity);\n    }\n\n    @Override\n    public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {\n        return alloc.directBuffer(initialCapacity, maxCapacity);\n    }\n\n    @Override\n    public CompositeByteBuf compositeBuffer() {\n        return alloc.compositeBuffer();\n    }\n\n    @Override\n    public CompositeByteBuf compositeBuffer(int maxNumComponents) {\n        return alloc.compositeBuffer(maxNumComponents);\n    }\n\n    @Override\n    public CompositeByteBuf compositeHeapBuffer() {\n        throw new UnsupportedOperationException(\"Heap buffer\");\n    }\n\n    @Override\n    public CompositeByteBuf compositeHeapBuffer(int maxNumComponents) {\n        throw new UnsupportedOperationException(\"Heap buffer\");\n    }\n\n    @Override\n    public CompositeByteBuf compositeDirectBuffer() {\n        return alloc.compositeDirectBuffer();\n    }\n\n    @Override\n    public CompositeByteBuf compositeDirectBuffer(int maxNumComponents) {\n        return alloc.compositeDirectBuffer(maxNumComponents);\n    }\n\n    @Override\n    public boolean isDirectBufferPooled() {\n        return alloc.isDirectBufferPooled();\n    }\n\n    @Override\n    public int calculateNewCapacity(int i, int i1) {\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/buffer/NettyBufferPool.java",
    "content": "package io.mycat.buffer;\n\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.PoolArenaMetric;\nimport io.netty.buffer.PoolChunkListMetric;\nimport io.netty.buffer.PoolChunkMetric;\nimport io.netty.util.internal.PlatformDependent;\n\nimport java.nio.ByteBuffer;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 封装netty pooled Direct Memory 接口，为mycat提供内存分配功能\n * 由于Mycat目前使用ByteBuffer,而Netty分配的是ByteBuf，为了管理ByteBuf\n * 在MyCatMemoryAllocator中定义recycleMaps ByteBuffer(address) -->ByteBuf\n * 的映射关系，通过address来回收ByteBuf.\n *\n * @author zagnix\n * @create 2017-04-13\n */\n\npublic class NettyBufferPool implements BufferPool {\n\n\n    MyCatMemoryAllocator allocator;\n    private int chunkSize = 0;\n\n    public NettyBufferPool(int chunkSize) {\n        allocator = MyCatMemoryAllocator.getINSTANCE();\n        this.chunkSize = chunkSize;\n    }\n\n    @Override\n    public ByteBuffer allocate(int size) {\n        ByteBuf byteBuf = allocator.directBuffer(size);\n        ByteBuffer byteBuffer = byteBuf.nioBuffer(0, size);\n        allocator.recycleMaps.put(PlatformDependent.directBufferAddress(byteBuffer), byteBuf);\n        return byteBuffer;\n    }\n\n    @Override\n    public void recycle(ByteBuffer byteBuffer) {\n        ByteBuf byteBuf =\n                allocator.recycleMaps.get(PlatformDependent.directBufferAddress(byteBuffer));\n\n        if (byteBuf != null) {\n            byteBuf.release();\n            allocator.recycleMaps.remove(PlatformDependent.directBufferAddress(byteBuffer));\n        }\n\n    }\n\n    /**\n     * return memory allocator\n     *\n     * @return\n     */\n    public MyCatMemoryAllocator getAllocator() {\n        return allocator;\n    }\n\n    /**\n     * TODO\n     * 下面函数需要将netty相关内存信息导出处理，然后实现\n     * 计算逻辑就是，\n     * 1.先计算PoolChunk分配的页,表示已经消耗的内存，\n     * 2.然后计算小于一页情况，记录小于一页内存使用情况，\n     * 上面二者合起来就是整个netty 使用的内存，\n     * 已经分配了，但是没有使用的内存的情况\n     */\n\n    @Override\n    public long capacity() {\n        return size();\n    }\n\n    @Override\n    public long size() {\n\n        List<PoolArenaMetric> list = allocator.getAlloc().directArenas();\n        long chunkSizeBytes = allocator.getChunkSize();\n        int chunkCount = 0;\n\n        synchronized (this) {\n            /**PoolArenas*/\n            for (PoolArenaMetric pool : list) {\n                List<PoolChunkListMetric> pcks = pool.chunkLists();\n                /**针对PoolChunkList*/\n                for (PoolChunkListMetric pck : pcks) {\n                    Iterator<PoolChunkMetric> it = pck.iterator();\n                    while (it.hasNext()) {\n                        PoolChunkMetric p = it.next();\n                        chunkCount++;\n                    }\n                }\n            }\n        }\n\n        return chunkCount * chunkSizeBytes;\n    }\n\n    @Override\n    public int getConReadBuferChunk() {\n        return 0;\n    }\n\n    @Override\n    public int getSharedOptsCount() {\n        return 0;\n    }\n\n    @Override\n    public int getChunkSize() {\n        return chunkSize;\n    }\n\n    @Override\n    public ConcurrentHashMap<Long, Long> getNetDirectMemoryUsage() {\n        return null;\n    }\n\n    @Override\n    public BufferArray allocateArray() {\n        return new BufferArray(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/cache/CachePool.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache;\n\n/**\n * simple cache pool for implement\n * \n * @author wuzhih\n * \n */\npublic interface CachePool {\n\n\tpublic void putIfAbsent(Object key, Object value);\n\n\tpublic Object get(Object key);\n\n\tpublic void clearCache();\n\n\tpublic CacheStatic getCacheStatic();\n\n\tpublic long getMaxSize();\n\tpublic void clearCache(String cacheName);\n}"
  },
  {
    "path": "src/main/java/io/mycat/cache/CachePoolFactory.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache;\n/**\n * factory used to create cachePool\n * @author wuzhih\n *\n */\npublic abstract class CachePoolFactory {\n\n\t/**\n\t *  create a cache pool instance\n\t * @param poolName\n\t * @param cacheSize\n\t * @param expireSeconds -1 for not expired\n\t * @return\n\t */\n\tpublic abstract CachePool createCachePool(String poolName,int cacheSize,int expireSeconds);\n}"
  },
  {
    "path": "src/main/java/io/mycat/cache/CacheService.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\n/**\n * cache service for other component default using memory cache encache\n * \n * @author wuzhih\n * \n */\npublic class CacheService {\n\tprivate static final Logger logger = LoggerFactory.getLogger(CacheService.class);\n\n\tprivate final Map<String, CachePoolFactory> poolFactorys = new HashMap<String, CachePoolFactory>();\n\tprivate final Map<String, CachePool> allPools = new HashMap<String, CachePool>();\n\n\tpublic CacheService() {\n\n\t\t// load cache pool defined\n\t\ttry {\n\t\t\tinit();\n\t\t} catch (Exception e) {\n\t\t\tif (e instanceof RuntimeException) {\n\t\t\t\tthrow (RuntimeException) e;\n\t\t\t} else {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\n\t}\n\tpublic Map<String, CachePool> getAllCachePools()\n\t{\n\t\treturn this.allPools;\n\t}\n\n\tprivate void init() throws Exception {\n\t\tProperties props = new Properties();\n\t\tprops.load(CacheService.class\n\t\t\t\t.getResourceAsStream(\"/cacheservice.properties\"));\n\t\tfinal String poolFactoryPref = \"factory.\";\n\t\tfinal String poolKeyPref = \"pool.\";\n\t\tfinal String layedPoolKeyPref = \"layedpool.\";\n\t\tString[] keys = props.keySet().toArray(new String[0]);\n\t\tArrays.sort(keys);\n\t\tfor (String key : keys) {\n\n\t\t\tif (key.startsWith(poolFactoryPref)) {\n\t\t\t\tcreatePoolFactory(key.substring(poolFactoryPref.length()),\n\t\t\t\t\t\t(String) props.get(key));\n\t\t\t} else if (key.startsWith(poolKeyPref)) {\n\t\t\t\tString cacheName = key.substring(poolKeyPref.length());\n\t\t\t\tString value = (String) props.get(key);\n\t\t\t\tString[] valueItems = value.split(\",\");\n\t\t\t\tif (valueItems.length < 3) {\n\t\t\t\t\tthrow new java.lang.IllegalArgumentException(\n\t\t\t\t\t\t\t\"invalid cache config ,key:\" + key + \" value:\"\n\t\t\t\t\t\t\t\t\t+ value);\n\t\t\t\t}\n\t\t\t\tString type = valueItems[0];\n\t\t\t\tint size = Integer.parseInt(valueItems[1]);\n\t\t\t\tint timeOut = Integer.parseInt(valueItems[2]);\n\t\t\t\tcreatePool(cacheName, type, size, timeOut);\n\t\t\t} else if (key.startsWith(layedPoolKeyPref)) {\n\t\t\t\tString cacheName = key.substring(layedPoolKeyPref.length());\n\t\t\t\tString value = (String) props.get(key);\n\t\t\t\tString[] valueItems = value.split(\",\");\n\t\t\t\tint index = cacheName.indexOf(\".\");\n\t\t\t\tif (index < 0) {// root layer\n\t\t\t\t\tString type = valueItems[0];\n\t\t\t\t\tint size = Integer.valueOf(valueItems[1]);\n\t\t\t\t\tint timeOut = Integer.valueOf(valueItems[2]);\n\t\t\t\t\tcreateLayeredPool(cacheName, type, size, timeOut);\n\t\t\t\t} else {\n\t\t\t\t\t// root layers' children\n\t\t\t\t\tString parent = cacheName.substring(0, index);\n\t\t\t\t\tString child = cacheName.substring(index + 1);\n\t\t\t\t\tCachePool pool = this.allPools.get(parent);\n\t\t\t\t\tif (pool == null || !(pool instanceof LayerCachePool)) {\n\t\t\t\t\t\tthrow new java.lang.IllegalArgumentException(\n\t\t\t\t\t\t\t\t\"parent pool not exists or not layered cache pool:\"\n\t\t\t\t\t\t\t\t\t\t+ parent + \" the child cache is:\"\n\t\t\t\t\t\t\t\t\t\t+ child);\n\t\t\t\t\t}\n\n\t\t\t\t\tint size = Integer.valueOf(valueItems[0]);\n\t\t\t\t\tint timeOut = Integer.valueOf(valueItems[1]);\n\t\t\t\t\t((DefaultLayedCachePool) pool).createChildCache(child,\n\t\t\t\t\t\t\tsize, timeOut);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void createLayeredPool(String cacheName, String type, int size,\n\t\t\tint expireSeconds) {\n\t\tcheckExists(cacheName);\n\t\tlogger.info(\"create layer cache pool \" + cacheName + \" of type \" + type\n\t\t\t\t+ \" ,default cache size \" + size + \" ,default expire seconds\"\n\t\t\t\t+ expireSeconds);\n\t\tDefaultLayedCachePool layerdPool = new DefaultLayedCachePool(cacheName,\n\t\t\t\tthis.getCacheFact(type), size, expireSeconds);\n\t\tthis.allPools.put(cacheName, layerdPool);\n\n\t}\n\n\tprivate void checkExists(String poolName) {\n\t\tif (allPools.containsKey(poolName)) {\n\t\t\tthrow new java.lang.IllegalArgumentException(\n\t\t\t\t\t\"duplicate cache pool name: \" + poolName);\n\t\t}\n\t}\n\n\tprivate void createPoolFactory(String factryType, String factryClassName)\n\t\t\tthrows Exception {\n\t\tCachePoolFactory factry = (CachePoolFactory) Class.forName(\n\t\t\t\tfactryClassName).newInstance();\n\t\tpoolFactorys.put(factryType, factry);\n\n\t}\n\n\tprivate void createPool(String poolName, String type, int cacheSize,\n\t\t\tint expireSeconds) {\n\t\tcheckExists(poolName);\n\t\tCachePoolFactory cacheFact = getCacheFact(type);\n\t\tCachePool cachePool = cacheFact.createCachePool(poolName, cacheSize,\n\t\t\t\texpireSeconds);\n\t\tallPools.put(poolName, cachePool);\n\n\t}\n\n\tprivate CachePoolFactory getCacheFact(String type) {\n\t\tCachePoolFactory facty = this.poolFactorys.get(type);\n\t\tif (facty == null) {\n\t\t\tthrow new RuntimeException(\"CachePoolFactory not defined for type:\"\n\t\t\t\t\t+ type);\n\t\t}\n\t\treturn facty;\n\t}\n\n\t/**\n\t * get cache pool by name ,caller should cache result\n\t * \n\t * @param poolName\n\t * @return CachePool\n\t */\n\tpublic CachePool getCachePool(String poolName) {\n\t\tCachePool pool = allPools.get(poolName);\n\t\tif (pool == null) {\n\t\t\tthrow new IllegalArgumentException(\"can't find cache pool:\"\n\t\t\t\t\t+ poolName);\n\t\t} else {\n\t\t\treturn pool;\n\t\t}\n\n\t}\n\n\tpublic void clearCache() {\n\n\t\tlogger.info(\"clear all cache pool \");\n\t\tfor (CachePool pool : allPools.values()) {\n\n\t\t\tpool.clearCache();\n\t\t}\n\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/cache/CacheStatic.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache;\n\n/**\n * cache static information\n * \n * @author wuzhih\n * \n */\npublic class CacheStatic {\n\tprivate long maxSize;\n\tprivate long memorySize;\n\tprivate long itemSize;\n\tprivate long accessTimes;\n\tprivate long putTimes;\n\tprivate long hitTimes;\n\tprivate long lastAccesTime;\n\tprivate long lastPutTime;\n\n\tpublic long getMemorySize() {\n\t\treturn memorySize;\n\t}\n\n\tpublic void setMemorySize(long memorySize) {\n\t\tthis.memorySize = memorySize;\n\t}\n\n\tpublic long getItemSize() {\n\t\treturn itemSize;\n\t}\n\n\tpublic void setItemSize(long itemSize) {\n\t\tthis.itemSize = itemSize;\n\t}\n\n\tpublic long getAccessTimes() {\n\t\treturn accessTimes;\n\t}\n\n\tpublic void setAccessTimes(long accessTimes) {\n\t\tthis.accessTimes = accessTimes;\n\t}\n\n\tpublic long getHitTimes() {\n\t\treturn hitTimes;\n\t}\n\n\tpublic void setHitTimes(long hitTimes) {\n\t\tthis.hitTimes = hitTimes;\n\t}\n\n\tpublic long getLastAccesTime() {\n\t\treturn lastAccesTime;\n\t}\n\n\tpublic void setLastAccesTime(long lastAccesTime) {\n\t\tthis.lastAccesTime = lastAccesTime;\n\t}\n\n\tpublic long getPutTimes() {\n\t\treturn putTimes;\n\t}\n\n\tpublic void setPutTimes(long putTimes) {\n\t\tthis.putTimes = putTimes;\n\t}\n\n\tpublic void incAccessTimes() {\n\t\tthis.accessTimes++;\n\t\tthis.lastAccesTime = System.currentTimeMillis();\n\t}\n\n\tpublic void incHitTimes() {\n\t\tthis.hitTimes++;\n\t\tthis.accessTimes++;\n\t\tthis.lastAccesTime = System.currentTimeMillis();\n\t}\n\n\tpublic void incPutTimes() {\n\t\tthis.putTimes++;\n\t\tthis.lastPutTime = System.currentTimeMillis();\n\t}\n\n\tpublic long getLastPutTime() {\n\t\treturn lastPutTime;\n\t}\n\n\tpublic void setLastPutTime(long lastPutTime) {\n\t\tthis.lastPutTime = lastPutTime;\n\t}\n\n\tpublic long getMaxSize() {\n\t\treturn maxSize;\n\t}\n\n\tpublic void setMaxSize(long maxSize) {\n\t\tthis.maxSize = maxSize;\n\t}\n\n\tpublic void reset() {\n\t\tthis.accessTimes = 0;\n\t\tthis.hitTimes = 0;\n\t\tthis.itemSize = 0;\n\t\tthis.lastAccesTime = 0;\n\t\tthis.lastPutTime = 0;\n\t\tthis.memorySize = 0;\n\t\tthis.putTimes = 0;\n\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CacheStatic [memorySize=\" + memorySize + \", itemSize=\"\n\t\t\t\t+ itemSize + \", accessTimes=\" + accessTimes + \", putTimes=\"\n\t\t\t\t+ putTimes + \", hitTimes=\" + hitTimes + \", lastAccesTime=\"\n\t\t\t\t+ lastAccesTime + \", lastPutTime=\" + lastPutTime + \"]\";\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/cache/DefaultLayedCachePool.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class DefaultLayedCachePool implements LayerCachePool {\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(DefaultLayedCachePool.class);\n    protected Map<String, CachePool> allCaches = new ConcurrentHashMap<String, CachePool>();\n\tprotected final ReentrantLock lock = new ReentrantLock();\n\tprotected int defaultCacheSize;\n\tprotected int defaulExpiredSeconds;\n\tprotected static final String defaultCache = \"default\";\n\tpublic static final String DEFAULT_CACHE_COUNT = \"DEFAULT_CACHE_COUNT\";\n\tpublic static final String DEFAULT_CACHE_EXPIRE_SECONDS = \"DEFAULT_CACHE_EXPIRE_SECONDS\";\n\tprivate final CachePoolFactory poolFactory;\n\tprivate final String name;\n\n\tpublic DefaultLayedCachePool(String name, CachePoolFactory poolFactory,\n\t\t\tint defaultCacheSize, int defaulExpiredSeconds) {\n\t\tsuper();\n\t\tthis.name = name;\n\t\tthis.poolFactory = poolFactory;\n\t\tthis.defaultCacheSize = defaultCacheSize;\n\t\tthis.defaulExpiredSeconds = defaulExpiredSeconds;\n\t}\n\n\tprivate CachePool getCache(String cacheName) {\n\t\tCachePool pool = allCaches.get(cacheName);\n\t\tif (pool == null) {\n\t\t\tlock.lock();\n\t\t\ttry {\n\t\t\t\tpool = allCaches.get(cacheName);\n\t\t\t\tif (pool == null) {\n\t\t\t\t\tpool = this.createChildCache(cacheName,\n\t\t\t\t\t\t\tthis.defaultCacheSize, this.defaulExpiredSeconds);\n\t\t\t\t}\n\n\t\t\t} finally {\n\t\t\t\tlock.unlock();\n\t\t\t}\n\t\t}\n\t\treturn pool;\n\t}\n\n\t/**\n\t * create child cache at runtime\n\t * \n\t * @param cacheName\n\t * @return\n\t */\n\tpublic CachePool createChildCache(String cacheName, int size,\n\t\t\tint expireSeconds) {\n\t\tLOGGER.info(\"create child Cache: \" + cacheName+ \" for layered cache \"+name+ \", size \"+size+\", expire seconds \"+expireSeconds);\n\t\tCachePool child = this.poolFactory.createCachePool(name + \".\"\n\t\t\t\t+ cacheName, size, expireSeconds);\n\t\tallCaches.put(cacheName, child);\n\t\treturn child;\n\t}\n\n\t@Override\n\tpublic void putIfAbsent(Object key, Object value) {\n\t\tputIfAbsent(defaultCache, key, value);\n\n\t}\n\n\t@Override\n\tpublic Object get(Object key) {\n\t\treturn get(defaultCache, key);\n\t}\n\n\t@Override\n\tpublic void clearCache() {\n\t\tLOGGER.info(\"clear cache \");\n\t\tfor (CachePool pool : allCaches.values()) {\n\t\t\tpool.clearCache();\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void putIfAbsent(String primaryKey, Object secondKey, Object value) {\n\t\tCachePool pool = getCache(primaryKey);\n\t\tpool.putIfAbsent(secondKey, value);\n\n\t}\n\n\t@Override\n\tpublic Object get(String primaryKey, Object secondKey) {\n\t\tCachePool pool = getCache(primaryKey);\n\t\treturn pool.get(secondKey);\n\t}\n\n\t@Override\n\tpublic CacheStatic getCacheStatic() {\n\t\tCacheStatic cacheStatic = new CacheStatic();\n\t\tcacheStatic.setMaxSize(this.getMaxSize());\n\t\tfor (CacheStatic singleStatic : getAllCacheStatic().values()) {\n\t\t\tcacheStatic.setItemSize(cacheStatic.getItemSize()\n\t\t\t\t\t+ singleStatic.getItemSize());\n\t\t\tcacheStatic.setHitTimes(cacheStatic.getHitTimes()\n\t\t\t\t\t+ singleStatic.getHitTimes());\n\t\t\tcacheStatic.setAccessTimes(cacheStatic.getAccessTimes()\n\t\t\t\t\t+ singleStatic.getAccessTimes());\n\t\t\tcacheStatic.setPutTimes(cacheStatic.getPutTimes()\n\t\t\t\t\t+ singleStatic.getPutTimes());\n\t\t\tif (cacheStatic.getLastAccesTime() < singleStatic\n\t\t\t\t\t.getLastAccesTime()) {\n\t\t\t\tcacheStatic.setLastAccesTime(singleStatic.getLastAccesTime());\n\t\t\t}\n\t\t\tif (cacheStatic.getLastPutTime() < singleStatic.getLastPutTime()) {\n\t\t\t\tcacheStatic.setLastPutTime(singleStatic.getLastPutTime());\n\t\t\t}\n\n\t\t}\n\t\treturn cacheStatic;\n\t}\n\n\t@Override\n\tpublic Map<String, CacheStatic> getAllCacheStatic() {\n\t\tMap<String, CacheStatic> results = new HashMap<String, CacheStatic>(\n\t\t\t\tthis.allCaches.size());\n\t\tfor (Map.Entry<String, CachePool> entry : allCaches.entrySet()) {\n\t\t\tresults.put(entry.getKey(), entry.getValue().getCacheStatic());\n\t\t}\n\t\treturn results;\n\t}\n\n\t@Override\n\tpublic long getMaxSize() {\n\t\tlong maxSize=0;\n\t\tfor(CachePool cache:this.allCaches.values())\n\t\t{\n\t\t\tmaxSize+=cache.getMaxSize();\n\t\t}\n\t\treturn maxSize;\n\t}\n\n\t//clear cache by cacheName\n\tpublic void clearCache(String cacheName) {\n\t\tLOGGER.info(\"clear cache :\"+cacheName);\n\t\tCachePool pool = getCache(cacheName);\n\t\tpool.clearCache();\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/cache/LayerCachePool.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache;\n\nimport java.util.Map;\n\n/**\n * Layered cache pool\n * \n * @author wuzhih\n * \n */\npublic interface LayerCachePool extends CachePool {\n\n\tpublic void putIfAbsent(String primaryKey, Object secondKey, Object value);\n\n\tpublic Object get(String primaryKey, Object secondKey);\n\n\t/**\n\t * get all cache static, name is cache name\n\t * @return map of CacheStatic\n\t */\n\tpublic Map<String, CacheStatic> getAllCacheStatic();\n\n\tpublic void clearCache(String cacheName);\n}"
  },
  {
    "path": "src/main/java/io/mycat/cache/MysqlDataSetCache.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache;\n\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.Serializable;\n\n/**\n * cache mysql dataset ,for example \"select * from A where .......\",cache all\n * result\n * \n * @author wuzhih\n * \n */\npublic class MysqlDataSetCache implements Serializable {\n\t/**\n\t * \n\t */\n\tprivate static final long serialVersionUID = 5426632041410472392L;\n\t// sql should not inlude page limit ,should store first record and sequnce\n\t// next\n\tprivate String sql;\n\tprivate int total;\n\tprivate String dataFile;\n\tprivate long createTime;\n\tprivate volatile int curCount;\n\tprivate volatile long lastAccesTime;\n\tprivate volatile boolean storing = true;\n\n\tpublic String getSql() {\n\t\treturn sql;\n\t}\n\n\tpublic boolean isStoring() {\n\t\treturn storing;\n\t}\n\n\tpublic void setStoring(boolean storing) {\n\t\tthis.storing = storing;\n\t}\n\n\tpublic void setSql(String sql) {\n\t\tthis.sql = sql;\n\t}\n\n\tpublic int getTotal() {\n\t\treturn total;\n\t}\n\n\tpublic void setTotal(int total) {\n\t\tthis.total = total;\n\t}\n\n\tpublic String getDataFile() {\n\t\treturn dataFile;\n\t}\n\n\tpublic void setDataFile(String dataFile) {\n\t\tthis.dataFile = dataFile;\n\t}\n\n\tpublic long getCreateTime() {\n\t\treturn createTime;\n\t}\n\n\tpublic void setCreateTime(long createTime) {\n\t\tthis.createTime = createTime;\n\t}\n\n\tpublic long getLastAccesTime() {\n\t\treturn lastAccesTime;\n\t}\n\n\tpublic void setLastAccesTime(long lastAccesTime) {\n\t\tthis.lastAccesTime = lastAccesTime;\n\t}\n\n\tpublic void addHeader(byte[] header) throws IOException  {\n\t\twriteFile(header);\n\t}\n\n\tprivate void writeFile(byte[] data) throws IOException {\n\t\tFileOutputStream outf = null;\n\t\ttry {\n\t\t\toutf = new FileOutputStream(dataFile, true);\n\t\t\toutf.write(data);\n\n\t\t} finally {\n\t\t\tif (outf != null) {\n\t\t\t\toutf.close();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void appendRecord(byte[] row) throws IOException {\n\t\twriteFile(row);\n\t\tcurCount++;\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/cache/MysqlDataSetService.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache;\n\nimport java.io.File;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic class MysqlDataSetService {\n\tprivate volatile boolean enabled = false;\n\t// max expire time is 300 seconds\n\tprivate int maxExpire = 300;\n\tprivate final ConcurrentHashMap<String, MysqlDataSetCache> cachedMap = new ConcurrentHashMap<String, MysqlDataSetCache>();\n\tprivate volatile Set<String> needCachedSQL = new HashSet<String>();\n\n\tpublic boolean isEnabled() {\n\t\treturn enabled;\n\t}\n\n\tpublic void setEnabled(boolean enabled) {\n\t\tthis.enabled = enabled;\n\t}\n\n\tpublic int getMaxExpire() {\n\t\treturn maxExpire;\n\t}\n\n\tpublic void setMaxExpire(int maxExpire) {\n\t\tthis.maxExpire = maxExpire;\n\t}\n\n\tprivate static MysqlDataSetService instance = new MysqlDataSetService();\n\n\tpublic static MysqlDataSetService getInstance() {\n\t\treturn instance;\n\t}\n\n\tprivate MysqlDataSetService() {\n\n\t}\n\n\t/**\n\t * sql should not include LIMIT range\n\t * \n\t * @param sql\n\t * @return\n\t */\n\tpublic MysqlDataSetCache findDataSetCache(String sql) {\n\t\tif (!enabled) {\n\t\t\treturn null;\n\t\t}\n\t\tMysqlDataSetCache cache = cachedMap.get(sql);\n\t\tif (validCache(cache)) {\n\t\t\treturn cache;\n\t\t} else {\n\t\t\tcachedMap.remove(sql);\n\t\t}\n\n\t\treturn null;\n\n\t}\n\n\tpublic String needCache(String sql)\n\t{\n\t\treturn needCachedSQL.contains(sql)?sql:null;\n\t}\n\tpublic boolean addIfNotExists(MysqlDataSetCache newCache) {\n\t\treturn (cachedMap.putIfAbsent(newCache.getSql(), newCache) == null);\n\t}\n\n\tprivate boolean validCache(MysqlDataSetCache cache) {\n\t\treturn (!cache.isStoring()\n\t\t\t\t&& (cache.getCreateTime() + this.maxExpire * 1000 < System\n\t\t\t\t\t\t.currentTimeMillis()) && (new File(cache.getDataFile())\n\t\t\t\t.exists()));\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/cache/impl/EnchachePooFactory.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache.impl;\n\nimport io.mycat.cache.CachePool;\nimport io.mycat.cache.CachePoolFactory;\nimport net.sf.ehcache.Cache;\nimport net.sf.ehcache.CacheManager;\nimport net.sf.ehcache.config.CacheConfiguration;\n\npublic class EnchachePooFactory extends CachePoolFactory {\n\n\t@Override\n\tpublic CachePool createCachePool(String poolName, int cacheSize,\n\t\t\tint expiredSeconds) {\n\t\tCacheManager cacheManager = CacheManager.create();\n\t\tCache enCache = cacheManager.getCache(poolName);\n\t\tif (enCache == null) {\n\n\t\t\tCacheConfiguration cacheConf = cacheManager.getConfiguration()\n\t\t\t\t\t.getDefaultCacheConfiguration().clone();\n\t\t\tcacheConf.setName(poolName);\n\t\t\tif (cacheConf.getMaxEntriesLocalHeap() != 0) {\n\t\t\t\tcacheConf.setMaxEntriesLocalHeap(cacheSize);\n\t\t\t} else {\n\t\t\t\tcacheConf.setMaxBytesLocalHeap(String.valueOf(cacheSize));\n\t\t\t}\n\t\t\tcacheConf.setTimeToIdleSeconds(expiredSeconds);\n\t\t\tCache cache = new Cache(cacheConf);\n\t\t\tcacheManager.addCache(cache);\n\t\t\treturn new EnchachePool(poolName,cache,cacheSize);\n\t\t} else {\n\t\t\treturn new EnchachePool(poolName,enCache,cacheSize);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/cache/impl/EnchachePool.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache.impl;\n\nimport net.sf.ehcache.Cache;\nimport net.sf.ehcache.Element;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.cache.CachePool;\nimport io.mycat.cache.CacheStatic;\n\n/**\n * ehcache based cache pool\n * \n * @author wuzhih\n * \n */\npublic class EnchachePool implements CachePool {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(EnchachePool.class);\n\tprivate final Cache enCache;\n\tprivate final CacheStatic cacheStati = new CacheStatic();\n    private final String name;\n    private final long maxSize;\n\tpublic EnchachePool(String name,Cache enCache,long maxSize) {\n\t\tthis.enCache = enCache;\n\t\tthis.name=name;\n\t\tthis.maxSize=maxSize;\n\t\tcacheStati.setMaxSize(this.getMaxSize());\n\n\t}\n\n\t@Override\n\tpublic void putIfAbsent(Object key, Object value) {\n\t\tElement el = new Element(key, value);\n\t\tif (enCache.putIfAbsent(el) == null) {\n\t\t\tcacheStati.incPutTimes();\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(name+\" add cache ,key:\" + key + \" value:\" + value);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic Object get(Object key) {\n\t\tElement cacheEl = enCache.get(key);\n\t\tif (cacheEl != null) {\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(name+\" hit cache ,key:\" + key);\n\t\t\t}\n\t\t\tcacheStati.incHitTimes();\n\t\t\treturn cacheEl.getObjectValue();\n\t\t} else {\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(name+\"  miss cache ,key:\" + key);\n\t\t\t}\n\t\t\tcacheStati.incAccessTimes();\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void clearCache() {\n\t\tLOGGER.info(\"clear cache \"+name);\n\t\tenCache.removeAll();\n\t\tenCache.clearStatistics();\n\t\tcacheStati.reset();\n\t\tcacheStati.setMemorySize(enCache.getMemoryStoreSize());\n\n\t}\n\n\t@Override\n\tpublic CacheStatic getCacheStatic() {\n\t\t\n\t\tcacheStati.setItemSize(enCache.getSize());\n\t\treturn cacheStati;\n\t}\n\n\t@Override\n\tpublic long getMaxSize() {\n\t\treturn maxSize;\n\t}\n\n\t@Override\n\tpublic void clearCache(String cacheName) {\n\t\tif (cacheName != null){\n\t\t\tenCache.remove(cacheName);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/cache/impl/LevelDBCachePooFactory.java",
    "content": "package io.mycat.cache.impl;\r\n\r\n\r\nimport java.io.File;\r\n\r\nimport static org.iq80.leveldb.impl.Iq80DBFactory.factory;\r\n\r\nimport org.iq80.leveldb.DB;\r\nimport org.iq80.leveldb.Options;\r\n\r\nimport io.mycat.cache.CachePool;\r\nimport io.mycat.cache.CachePoolFactory;\r\n\r\npublic class LevelDBCachePooFactory extends CachePoolFactory {\r\n\r\n\t@Override\r\n\tpublic CachePool createCachePool(String poolName, int cacheSize,\r\n\t\t\tint expireSeconds) {\r\n  \t  Options options = new Options();\r\n  \t  options.cacheSize(cacheSize * 1048576);//cacheSize M 大小\r\n  \t  options.createIfMissing(true);\r\n  \t  DB db =null;\r\n  \t  try {\r\n  \t\t db=factory.open(new File(\"leveldb\\\\\"+poolName), options);\r\n  \t    // Use the db in here....\r\n  \t  } catch (Exception e) {\r\n  \t    // Make sure you close the db to shutdown the \r\n  \t    // database and avoid resource leaks.\r\n  \t   // db.close();\r\n  \t  }\r\n\t  return new LevelDBPool(poolName,db,cacheSize);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/cache/impl/LevelDBPool.java",
    "content": "package io.mycat.cache.impl;\n\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\nimport org.iq80.leveldb.DB;\n\nimport io.mycat.cache.CachePool;\nimport io.mycat.cache.CacheStatic;\n\n\npublic class LevelDBPool implements CachePool {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(LevelDBPool.class);\n\tprivate final DB cache;\n\tprivate final CacheStatic cacheStati = new CacheStatic();\n    private final String name;\n    private final long maxSize;\n    \n\tpublic LevelDBPool(String name,DB db,long maxSize) {\n\t\tthis.cache = db;\n\t\tthis.name=name;\n\t\tthis.maxSize=maxSize;\n\t\tcacheStati.setMaxSize(maxSize);\n\t}\n\t@Override\n\tpublic void putIfAbsent(Object key, Object value) {\n\t\t\n\t\tcache.put(toByteArray(key),toByteArray(value));\n\t\tcacheStati.incPutTimes();\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(name+\" add leveldb cache ,key:\" + key + \" value:\" + value);\n\t\t}\t\t\n\t}\n\n\t@Override\n\tpublic Object get(Object key) {\n\t\t\n\t\tObject  ob= toObject(cache.get(toByteArray(key)));\n\t\tif (ob != null) {\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(name+\" hit cache ,key:\" + key);\n\t\t\t}\n\t\t\tcacheStati.incHitTimes();\n\t\t\treturn ob;\n\t\t} else {\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(name+\"  miss cache ,key:\" + key);\n\t\t\t}\n\t\t\tcacheStati.incAccessTimes();\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void clearCache() {\n\t\tLOGGER.info(\"clear cache \"+name);\n\t\t//cache.delete(key);\n\t\tcacheStati.reset();\n\t\t//cacheStati.setMemorySize(cache.g);\n\t\t\n\t}\n\n\t@Override\n\tpublic CacheStatic getCacheStatic() {\n\t\t\n\t\t/*\n\t\tint i=0;\t\t\n\t\ttry {\n\t\t // DBIterator iterator = cache.iterator();\t\n\t\t  for(cache.iterator().seekToFirst(); cache.iterator().hasNext(); cache.iterator().next()) {\n\t\t\t  i++;\n\t\t  }\n\t\t  cache.iterator().close();\n\t\t} catch (Exception e) {\n\t\t\t  // Make sure you close the iterator to avoid resource leaks.\t\t\t  \n\t\t}\t\t\n\t\t//long[] sizes = cache.getApproximateSizes(new Range(bytes(\"TESTDB\"), bytes(\"TESTDC\")));\n\t\t */\n\t\t//cacheStati.setItemSize(cache.getSize());//sizes[0]);//需要修改leveldb的代码\n\t\tcacheStati.setItemSize(cacheStati.getPutTimes());\n\t\treturn cacheStati;\n\t}\n\n\t@Override\n\tpublic long getMaxSize() {\n\t\t\n\t\treturn maxSize;\n\t}\n\n\t@Override\n\tpublic void clearCache(String cacheName) {\n\t\tif (cacheName != null){\n\t\t\tcache.delete(toByteArray(cacheName));\n\t\t}\n\t}\n\n\tpublic  byte[] toByteArray (Object obj) {\n        byte[] bytes = null;        \n        ByteArrayOutputStream bos = new ByteArrayOutputStream();        \n        try {          \n            ObjectOutputStream oos = new ObjectOutputStream(bos);           \n            oos.writeObject(obj);          \n            oos.flush();           \n            bytes = bos.toByteArray ();        \n            oos.close();           \n            bos.close();          \n        } catch (IOException ex) {\n            LOGGER.error(\"toByteArrayError\", ex);\n        }        \n        return bytes;      \n    }     \n         \n        \n    public  Object toObject (byte[] bytes) {        \n        Object obj = null;   \n        if ((bytes==null) || (bytes.length<=0)) {\n        \treturn obj;\n        }\n        try {          \n            ByteArrayInputStream bis = new ByteArrayInputStream (bytes);          \n            ObjectInputStream ois = new ObjectInputStream (bis);          \n            obj = ois.readObject();        \n            ois.close();     \n            bis.close();     \n        } catch (IOException ex) {    \n            LOGGER.error(\"toObjectError\", ex);\n        } catch (ClassNotFoundException ex) {          \n            LOGGER.error(\"toObjectError\", ex);\n        }        \n        return obj;      \n    } \n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/cache/impl/MapDBCachePooFactory.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache.impl;\n\nimport java.util.concurrent.TimeUnit;\n\nimport org.mapdb.DB;\nimport org.mapdb.DBMaker;\nimport org.mapdb.HTreeMap;\n\nimport io.mycat.cache.CachePool;\nimport io.mycat.cache.CachePoolFactory;\n\npublic class MapDBCachePooFactory extends CachePoolFactory {\n\tprivate DB db = DBMaker.newMemoryDirectDB().cacheSize(1000).cacheLRUEnable().make();\n\n\t@Override\n\tpublic CachePool createCachePool(String poolName, int cacheSize,\n\t\t\tint expiredSeconds) {\n\n\t\tHTreeMap<Object, Object> cache = this.db.createHashMap(poolName)\n\t\t\t\t.expireMaxSize(cacheSize)\n\t\t\t\t.expireAfterAccess(expiredSeconds, TimeUnit.SECONDS)\n\t\t\t\t.makeOrGet();\n\t\treturn new MapDBCachePool(cache, cacheSize);\n\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/cache/impl/MapDBCachePool.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache.impl;\n\nimport org.mapdb.HTreeMap;\n\nimport io.mycat.cache.CachePool;\nimport io.mycat.cache.CacheStatic;\n\npublic class MapDBCachePool implements CachePool {\n\n\tprivate final HTreeMap<Object, Object> htreeMap;\n\tprivate final CacheStatic cacheStati = new CacheStatic();\n    private final long maxSize;\n\tpublic MapDBCachePool(HTreeMap<Object, Object> htreeMap,long maxSize) {\n\t\tthis.htreeMap = htreeMap;\n\t\tthis.maxSize=maxSize;\n\t\tcacheStati.setMaxSize(maxSize);\n\t}\n\n\t@Override\n\tpublic void putIfAbsent(Object key, Object value) {\n\t\tif (htreeMap.putIfAbsent(key, value) == null) {\n\t\t\tcacheStati.incPutTimes();\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic Object get(Object key) {\n\t\tObject value = htreeMap.get(key);\n\t\tif (value != null) {\n\t\t\tcacheStati.incHitTimes();\n\t\t\treturn value;\n\t\t} else {\n\t\t\tcacheStati.incAccessTimes();\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void clearCache() {\n\t\thtreeMap.clear();\n\t\tcacheStati.reset();\n\n\t}\n\n\t@Override\n\tpublic CacheStatic getCacheStatic() {\n\t\t\n\t\tcacheStati.setItemSize(htreeMap.sizeLong());\n\t\treturn cacheStati;\n\t}\n\n\t@Override\n\tpublic long getMaxSize() {\n\t\treturn maxSize;\n\t}\n\n\t@Override\n\tpublic void clearCache(String cacheName) {\n\t\thtreeMap.remove(cacheName);\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/cache/index/Shard.java",
    "content": "package io.mycat.cache.index;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.SortedMap;\nimport java.util.TreeMap;\n\n/**\n * 分布式索引一致性\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 00:15:03 2016/5/23\n */\npublic class Shard<S> { // S类封装了机器节点的信息 ，如name、password、ip、port等\n\n    private TreeMap<Long, S> nodes; // 虚拟节点\n    private List<S> shards; // 真实机器节点\n    private static final int NODE_NUM = 100; // 每个机器节点关联的虚拟节点个数\n\n    public Shard(List<S> shards) {\n        super();\n        this.shards = shards;\n        init();\n    }\n\n    private void init() { // 初始化一致性hash环\n        nodes = new TreeMap<Long, S>();\n        for (int i = 0; i != shards.size(); ++i) { // 每个真实机器节点都需要关联虚拟节点\n            final S shardInfo = shards.get(i);\n\n            for (int n = 0; n < NODE_NUM; n++) {\n                // 一个真实机器节点关联NODE_NUM个虚拟节点\n                nodes.put(hash(\"SHARD-\" + i + \"-NODE-\" + n), shardInfo);\n            }\n        }\n    }\n\n    public S getShardInfo(String key) {\n        SortedMap<Long, S> tail = nodes.tailMap(hash(key)); // 沿环的顺时针找到一个虚拟节点\n        if (tail.size() == 0) {\n            return nodes.get(nodes.firstKey());\n        }\n        return tail.get(tail.firstKey()); // 返回该虚拟节点对应的真实机器节点的信息\n    }\n\n    /**\n     * MurMurHash算法，是非加密HASH算法，性能很高，\n     * 比传统的CRC32,MD5，SHA-1（这两个算法都是加密HASH算法，复杂度本身就很高，带来的性能上的损害也不可避免）\n     * 等HASH算法要快很多，而且据说这个算法的碰撞率很低.\n     * http://murmurhash.googlepages.com/\n     */\n    private Long hash(String key) {\n\n        ByteBuffer buf = ByteBuffer.wrap(key.getBytes());\n        int seed = 0x1234ABCD;\n\n        ByteOrder byteOrder = buf.order();\n        buf.order(ByteOrder.LITTLE_ENDIAN);\n\n        long m = 0xc6a4a7935bd1e995L;\n        int r = 47;\n\n        long h = seed ^ (buf.remaining() * m);\n\n        long k;\n        while (buf.remaining() >= 8) {\n            k = buf.getLong();\n\n            k *= m;\n            k ^= k >>> r;\n            k *= m;\n\n            h ^= k;\n            h *= m;\n        }\n\n        if (buf.remaining() > 0) {\n            ByteBuffer finish = ByteBuffer.allocate(8).order(\n                    ByteOrder.LITTLE_ENDIAN);\n            // for big-endian version, do this first:\n            // finish.position(8-buf.remaining());\n            finish.put(buf).rewind();\n            h ^= finish.getLong();\n            h *= m;\n        }\n\n        h ^= h >>> r;\n        h *= m;\n        h ^= h >>> r;\n\n        buf.order(byteOrder);\n        return h;\n    }\n\n    public static void main(String[] args) {\n        List<String> stringList = new ArrayList<>();\n        stringList.add(\"host1\");\n        stringList.add(\"host2\");\n        stringList.add(\"host3\");\n        stringList.add(\"host4\");\n        stringList.add(\"host5\");\n        Shard<String> stringShard = new Shard<>(stringList);\n        for (int i = 0; i < 10; i++) {\n            System.out.println(i+\":\"+stringShard.getShardInfo(\"\"+i));\n        }\n        stringList = new ArrayList<>();\n        stringList.add(\"host1\");\n        stringList.add(\"host2\");\n        stringList.add(\"host3\");\n        stringList.add(\"host4\");\n        stringShard = new Shard<>(stringList);\n        for (int i = 0; i < 10; i++) {\n            System.out.println(i+\":\"+stringShard.getShardInfo(\"\"+i));\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/catlets/Catlet.java",
    "content": "package io.mycat.catlets;\n\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.sqlengine.EngineCtx;\n/**\n * mycat catlet ,used to execute sql and return result to client,some like\n * database's procedure.\n * must implemented as a stateless class and can process many SQL concurrently \n * \n * @author wuzhih\n * \n */\npublic interface Catlet {\n\n\t/*\n\t * execute sql in EngineCtx and return result to client\n\t */\n\tvoid processSQL(String sql, EngineCtx ctx);\n\t\n\tvoid route(SystemConfig sysConfig, SchemaConfig schema,\n\t\t\tint sqlType, String realSQL, String charset, ServerConnection sc,\n\t\t\tLayerCachePool cachePool) ;\n\t//void setRoute(RouteResultset rrs);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/catlets/JoinParser.java",
    "content": "package io.mycat.catlets;\r\n\r\n\r\nimport java.util.LinkedHashMap;\r\nimport java.util.List;\r\n\r\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\r\n\r\nimport com.alibaba.druid.sql.SQLUtils;\r\nimport com.alibaba.druid.sql.ast.SQLExpr;\r\nimport com.alibaba.druid.sql.ast.SQLOrderBy;\r\nimport com.alibaba.druid.sql.ast.SQLOrderingSpecification;\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\nimport com.alibaba.druid.sql.ast.expr.SQLAggregateExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;\r\nimport com.alibaba.druid.sql.ast.expr.SQLBooleanExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLCharExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLInListExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLNullExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLNumberExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;\r\nimport com.alibaba.druid.sql.ast.statement.SQLJoinTableSource;\r\nimport com.alibaba.druid.sql.ast.statement.SQLJoinTableSource.JoinType;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectItem;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQuery;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\r\nimport com.alibaba.druid.sql.ast.statement.SQLTableSource;\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\r\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\r\n\r\n/**  \r\n * 功能详细描述:分片join,解析join语句\r\n * @author sohudo[http://blog.csdn.net/wind520]\r\n * @create 2015年01月25日 \r\n * @version 0.0.1\r\n */\r\n\r\n\r\npublic class JoinParser {\r\n\t\r\n\tprotected static final Logger LOGGER = LoggerFactory.getLogger(JoinParser.class);\r\n\t\r\n    private MySqlSelectQueryBlock mysqlQuery;\r\n    private String stmt=\"\";\r\n    private String joinType;\r\n    private String masterTable;    \r\n    private TableFilter tableFilter; // a table -> b table 的链表 \r\n    \r\n    //private LinkedHashMap<String,String> fieldAliasMap = new LinkedHashMap<String,String>();\r\n    \r\n\tpublic JoinParser(MySqlSelectQueryBlock selectQuery,String stmt) {\r\n\t\tthis.mysqlQuery=selectQuery;\r\n\t\tthis.stmt=stmt;\r\n\t}\r\n\t\r\n\tpublic void parser(){\r\n\t   masterTable=\"\";\t   \r\n\t   \r\n\t   SQLTableSource table=mysqlQuery.getFrom();\t //a 表  \r\n\t   parserTable(table,tableFilter,false); // 组成链表\r\n\t   \r\n\t   parserFields(mysqlQuery.getSelectList());  //查询字段放到各个查询表中。\r\n\t   parserMasterTable();\t //查询主表 别名   \r\n\t   \r\n\t   parserWhere(mysqlQuery.getWhere(),\"\"); // where 条件放到各个查询表中。\t   \r\n\t // getJoinField();\r\n\t   parserOrderBy(mysqlQuery.getOrderBy());  // order 条件放到各个查询表中。\r\n\t   parserLimit(); // limit \r\n//\t   LOGGER.info(\"field \"+fieldAliasMap);\t  \t   \r\n//\t   LOGGER.info(\"master \"+masterTable);\r\n//\t   LOGGER.info(\"join Lkey \"+getJoinLkey()); \r\n//\t   LOGGER.info(\"join Rkey \"+getJoinRkey()); \t   \r\n\t   LOGGER.info(\"SQL: \"+this.stmt);\r\n\t}\r\n\t\r\n\tprivate void parserTable(SQLTableSource table,TableFilter tFilter,boolean isOutJoin){\r\n\t\tif(table instanceof SQLJoinTableSource){\r\n\t\t\tSQLJoinTableSource table1=(SQLJoinTableSource)table;\t\r\n\t\t\tjoinType=table1.getJoinType().toString();\r\n\t\t\tif ((table1.getJoinType()==JoinType.COMMA)||(table1.getJoinType()==JoinType.JOIN)||(table1.getJoinType()==JoinType.INNER_JOIN)\r\n\t\t\t\t\t||(table1.getJoinType()==JoinType.LEFT_OUTER_JOIN))\t{\t\t\t\t\t\r\n\t\t\t\ttFilter=setTableFilter(tFilter,getTableFilter(table1.getLeft(),isOutJoin));\r\n\t\t\t\tif (tableFilter==null){\r\n\t\t\t\t\ttableFilter=tFilter;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t//parserTable(table1.getLeft());\t//SQLExprTableSource\r\n\t\t\tparserTable(table1.getRight(),tFilter,true);\r\n\t\t\t\r\n\t\t\tSQLExpr expr=table1.getCondition();//SQLBinaryOpExpr\r\n\t\t\tparserJoinKey(expr);\r\n\t\t}\r\n\t\telse {\r\n\t\t\ttFilter=setTableFilter(tFilter,getTableFilter(table,isOutJoin));\r\n\t\t\tLOGGER.info(\"table \"+table.toString() +\" Alias:\"+table.getAlias()+\" Hints:\"+table.getHints());\r\n\t\t}\r\n\t}\r\n\tprivate TableFilter setTableFilter(TableFilter tFilter,TableFilter newFilter){\r\n\t\tif (tFilter==null) {\r\n\t\t\ttFilter=newFilter;\r\n\t\t\treturn tFilter;\r\n\t\t}\r\n\t\telse {\r\n\t\t\ttFilter.setTableJoin(newFilter);\t\r\n\t\t\treturn tFilter.getTableJoin();\r\n\t\t}\r\n\t}\r\n\tprivate TableFilter getTableFilter(SQLTableSource table,boolean isOutJoin){\t\r\n\t\tString key   ;\r\n\t\tString value = table.toString().trim();\r\n\t\tif (table.getAlias()==null) {\r\n\t\t\tkey=value;\r\n\t\t}\r\n\t\telse {\r\n\t\t\t key   = table.getAlias().trim();\r\n\t\t}\r\n\t\treturn new TableFilter(value,key,isOutJoin);\t\r\n\t}\r\n\t\r\n\tprivate void parserJoinKey(SQLExpr expr){\t\t\r\n\t\tif (expr==null) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\t parserWhere(expr,\"\");\r\n\t}\r\n\t\r\n\tprivate String getExprFieldName(SQLAggregateExpr expr){\r\n\t\tStringBuilder field = new StringBuilder();\r\n\t\tfor (SQLExpr item :expr.getArguments()){\r\n\t\t\tfield.append(item.toString());\r\n\t\t}\t\t\r\n\t\treturn expr.getMethodName()+\"(\"+field.toString()+\")\";\r\n\t}\r\n\t\r\n\tprivate String getFieldName(SQLSelectItem item){\r\n\t\tif (item.getExpr() instanceof SQLPropertyExpr) {\t\t\t\r\n\t\t\treturn item.getExpr().toString();//字段别名\r\n\t\t}\r\n\t\telse {\r\n\t\t\treturn item.toString();\r\n\t\t}\r\n\t}\r\n\t\r\n\tprivate String getMethodInvokeFieldName(SQLSelectItem item){\r\n\t\tSQLMethodInvokeExpr invoke = (SQLMethodInvokeExpr)item.getExpr();\r\n\t\tList<SQLExpr> itemExprs = invoke.getParameters();\r\n\t\tfor(SQLExpr itemExpr:itemExprs){\r\n\t\t\tif (itemExpr instanceof SQLPropertyExpr) {\r\n\t\t\t\treturn itemExpr.toString();//字段别名\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn item.toString();\r\n\t}\r\n\t\r\n\t\r\n\tprivate void parserFields(List<SQLSelectItem> mysqlSelectList){\r\n\t\t//显示的字段\r\n\t\tString key=\"\";\r\n\t\tString value =\"\";\r\n\t\tString exprfield = \"\";\r\n\t\tfor(SQLSelectItem item : mysqlSelectList) {\r\n\t\t\tif (item.getExpr() instanceof SQLAllColumnExpr) {\r\n\t\t\t\t//*解析\r\n\t\t\t\tsetField(item.toString(), item.toString());\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tif (item.getExpr() instanceof SQLAggregateExpr) {\r\n\t\t\t\t\tSQLAggregateExpr expr =(SQLAggregateExpr)item.getExpr();\r\n\t\t\t\t\t key = getExprFieldName(expr);\r\n\t\t\t\t\t setField(key, value);\r\n\t\t\t\t}else if(item.getExpr() instanceof SQLMethodInvokeExpr){\r\n\t\t\t\t\tkey = getMethodInvokeFieldName(item);\r\n\t\t\t\t\texprfield=getFieldName(item);\r\n//\t\t\t\t\tvalue=item.getAlias();\r\n\t\t\t\t\tsetField(key, value,exprfield);\r\n\t\t\t\t}else {\t\t\t\t\t\r\n\t\t\t\t\tkey=getFieldName(item);\r\n\t\t\t\t\tvalue=item.getAlias();\r\n\t\t\t\t\tsetField(key, value);\r\n\t\t\t\t}\t\t\t\r\n\t\t\t\t\r\n\t\t\t}\r\n\t\t}\t\t\t\r\n\t}\r\n\tprivate void setField(String key,String value){\r\n\t\t//fieldAliasMap.put(key, value);\r\n\t\tif (tableFilter!=null){\r\n\t\t\ttableFilter.addField(key, value);\r\n\t\t}\r\n\t}\r\n\t\r\n\tprivate void setField(String key,String value,String expr){\r\n\t\t//fieldAliasMap.put(key, value);\r\n\t\tif (tableFilter!=null){\r\n\t\t\ttableFilter.addField(key, value,expr);\r\n\t\t}\r\n\t}\r\n\t\r\n\t\r\n\t//判断并获得主表\r\n\tprivate void parserMasterTable(){ \r\n\t\tif (tableFilter!=null){\r\n\t\t   masterTable=tableFilter.getTableAlia();\r\n\t\t}\r\n\t}\t\r\n\r\n\tprivate boolean checkJoinField(String value){\r\n\t\tif (value==null){\r\n\t\t\treturn false;\t\r\n\t\t}\r\n\t\telse {\r\n\t\t\tint i=value.indexOf('.');\t\r\n\t\t\treturn i>0;\r\n\t\t}\r\n\t}\r\n\r\n\t//解析 a.field = b.field \r\n\tprivate void parserWhere(SQLExpr aexpr,String Operator){\r\n\t\t if (aexpr==null) {\r\n\t\t\t return;\r\n\t\t }\r\n\t     if (aexpr instanceof SQLBinaryOpExpr){\r\n\t\t   SQLBinaryOpExpr expr=(SQLBinaryOpExpr)aexpr;  \r\n\t\t   SQLExpr exprL=expr.getLeft();\r\n\t\t   if (!(exprL instanceof SQLBinaryOpExpr))\r\n\t\t   {\r\n\t\t\t  opSQLExpr((SQLBinaryOpExpr)aexpr,Operator);\t\t\t  \r\n\t\t   }\r\n\t\t   else {\r\n\t\t\t// if (expr.getOperator().getName().equals(\"AND\")) { \r\n\t\t\t if (expr.getOperator()==SQLBinaryOperator.BooleanAnd) { \t \r\n\t\t\t   //parserWhere(exprL); \r\n\t\t\t   //parserWhere(expr.getRight());\r\n\t\t\t   andorWhere(exprL,expr.getOperator().getName(),expr.getRight());\r\n\t\t\t }\r\n\t\t\t else if (expr.getOperator()==SQLBinaryOperator.BooleanOr){//.getName().equals(\"OR\")) {  \r\n\t\t\t\tandorWhere(exprL,expr.getOperator().getName(),expr.getRight()); \t\t\t\t\r\n\t\t\t }\r\n\t\t\t else {\r\n\t\t\t\t throw new RuntimeException(\"Can't identify the operation of  of where\"); \r\n\t\t\t }\r\n\t\t   }\r\n\t   }else if(aexpr instanceof SQLInListExpr){\r\n\t\t   SQLInListExpr expr = (SQLInListExpr)aexpr;\r\n\t\t   SQLExpr exprL =  expr.getExpr();\r\n\t\t   String field=exprL.toString();\r\n\t\t   tableFilter.addWhere(field, SQLUtils.toMySqlString(expr), Operator);\r\n\t   }\r\n\t     \r\n\t}\r\n\t\r\n\tprivate void andorWhere(SQLExpr exprL,String Operator,SQLExpr exprR ){ \r\n\t\t   parserWhere(exprL,\"\");\r\n\t\t   parserWhere(exprR,Operator);\r\n\t}\t\r\n\t   \r\n    private void opSQLExpr(SQLBinaryOpExpr expr,String Operator) {\r\n\t\t   if (expr==null) {\r\n\t\t\t   return;\r\n\t\t   }\r\n\t\t   SQLExpr exprL=expr.getLeft();\r\n\t\t   if (!(exprL instanceof SQLBinaryOpExpr))\r\n\t\t   {\r\n\t\t\t   String field=exprL.toString(); //获取表达式 左边的值\r\n\t\t\t   String value=getExpValue(expr.getRight()).toString(); //获取表达式右边的值\r\n\t\t\t   if (expr.getOperator()==SQLBinaryOperator.Equality) {  \r\n\t\t\t\t if (checkJoinField(value)) {//设置joinKey\r\n\t\t\t\t\t//joinLkey=field;\r\n\t\t\t\t\t//joinRkey=value; \r\n\t\t\t\t\ttableFilter.setJoinKey(field,value);\r\n\t\t\t\t }\r\n\t\t\t\t else {\r\n\t\t\t\t\t tableFilter.addWhere(field, value, expr.getOperator().getName(), Operator);\r\n\t\t\t\t }\r\n\t\t\t   }\r\n\t\t\t   else {\r\n\t\t\t\t   tableFilter.addWhere(field, value, expr.getOperator().getName(), Operator);\r\n\t\t\t   }\r\n\t\t   }\t\t\r\n\t}\r\n\r\n\tprivate Object getExpValue(SQLExpr expr){\r\n\t\tif (expr instanceof SQLIntegerExpr){\r\n\t\t\treturn ((SQLIntegerExpr)expr).getNumber().longValue();\r\n\t\t}\r\n\t\tif (expr instanceof SQLNumberExpr){\r\n\t\t\treturn ((SQLNumberExpr)expr).getNumber().doubleValue();\r\n\t\t}\t\t\r\n\t\tif (expr instanceof SQLCharExpr){\r\n\t\t\tString va=((SQLCharExpr)expr).toString();\r\n\t\t\treturn va;//remove(va,'\\'');\r\n\t\t}\r\n\t\tif (expr instanceof SQLBooleanExpr){\t\t\t\r\n\t\t\treturn ((SQLBooleanExpr)expr).getValue();\r\n\t\t}\t\t\t\r\n\t\tif (expr instanceof SQLNullExpr){\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\r\n\t\treturn expr;\t\t\r\n\t}\t\r\n\t\r\n\tprivate void parserOrderBy(SQLOrderBy orderby)   \r\n    {   \r\n\t\tif (orderby != null ){\r\n\t\t\tfor (int i = 0; i < orderby.getItems().size(); i++)\r\n\t        {\r\n\t\t\t  SQLSelectOrderByItem orderitem = orderby.getItems().get(i);\r\n\t\t\t  tableFilter.addOrders(i, orderitem.getExpr().toString(), getSQLExprToAsc(orderitem.getType()));\r\n            }\r\n\t\t}\t\t\r\n    }  \r\n\tprivate void parserLimit(){\r\n\t  int limitoff=0;\r\n\t  int limitnum=0;\r\n\t  if (this.mysqlQuery.getLimit()!=null) {\r\n\t    limitoff=getSQLExprToInt(this.mysqlQuery.getLimit().getOffset());\t\t\t\r\n\t    limitnum=getSQLExprToInt(this.mysqlQuery.getLimit().getRowCount());\r\n\t    tableFilter.addLimit(limitoff,limitnum);\r\n\t  }\r\n\t}\r\n\t\r\n\tprivate int getSQLExprToInt(SQLExpr expr){\r\n\t\tif (expr instanceof SQLIntegerExpr){\r\n\t\t\treturn ((SQLIntegerExpr)expr).getNumber().intValue();\r\n\t\t}\r\n\t\treturn 0;\t\t\r\n\t}\r\n\t\r\n\tprivate String getSQLExprToAsc(SQLOrderingSpecification ASC){\r\n\t\tif (ASC==null ) {\r\n\t\t\treturn \" ASC \";\r\n\t\t}\r\n\t\tif (ASC==SQLOrderingSpecification.DESC){\r\n\t\t\treturn \" DESC \";\r\n\t\t}\r\n\t\telse {\r\n\t\t\treturn \" ASC \";\t\t\r\n\t\t}\r\n\t}\t\t\r\n\t\r\n\tpublic String getChildSQL(){\t\t\r\n\t\t//String sql=\"select \"+joinRkey+\",\"+sql+\" from \"+mtable+\" where \"+joinRkey+\" in \";\r\n\t\tString sql=tableFilter.getTableJoin().getSQL();\r\n\t\treturn sql;\r\n\t}\r\n\t\r\n\tpublic String getSql(){\r\n\t\tstmt=tableFilter.getSQL();\r\n\t\treturn stmt;\r\n\t}\r\n\t\r\n\tpublic String getJoinType(){\r\n\t\treturn joinType;\r\n\t}\r\n\tpublic String getJoinLkey(){\r\n\t\treturn tableFilter.getJoinKey(true);\r\n\t}\t\r\n\tpublic String getJoinRkey(){\r\n\t\treturn tableFilter.getJoinKey(false);\r\n\t}\t\r\n\t\r\n\t//返回a表排序的字段\r\n\tpublic LinkedHashMap<String, Integer> getOrderByCols(){\r\n\t\treturn tableFilter.getOrderByCols();\r\n\t}\r\n\t//返回b表排序的字段\r\n\tpublic LinkedHashMap<String, Integer> getChildByCols(){\r\n\t\treturn tableFilter.getTableJoin().getOrderByCols();\r\n\t}\r\n\t//是否有order 排序\r\n\tpublic boolean hasOrder() {\r\n\t\treturn tableFilter.getOrderByCols().size() > 0 || tableFilter.getTableJoin().getOrderByCols().size() > 0;\t\t\r\n\t\t\r\n\t}\r\n\t/*\r\n\t * limit 的 start*/\r\n\tpublic int getOffset() {\r\n\t\treturn tableFilter.getOffset();\r\n\t}\r\n\r\n\t/*\r\n\t * limit 的 rowCount*/\r\n\tpublic int getRowCount() {\r\n\t\treturn tableFilter.getRowCount();\r\n\t}\r\n\t/*\r\n\t * 是否有limit 输出。\r\n\t */\r\n\tpublic boolean hasLimit() {\r\n\t\treturn tableFilter.getOffset() > 0 ||  tableFilter.getRowCount() > 0 ; \r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/catlets/ShareJoin.java",
    "content": "package io.mycat.catlets;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQuery;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\n\nimport io.mycat.backend.mysql.nio.handler.MiddlerQueryResultHandler;\nimport io.mycat.backend.mysql.nio.handler.MiddlerResultHandler;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.Fields;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.server.NonBlockingSession;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.sqlengine.AllJobFinishedListener;\nimport io.mycat.sqlengine.EngineCtx;\nimport io.mycat.sqlengine.SQLJobHandler;\nimport io.mycat.sqlengine.mpp.ColMeta;\nimport io.mycat.sqlengine.mpp.OrderCol;\nimport io.mycat.sqlengine.mpp.tmp.RowDataSorter;\nimport io.mycat.util.ByteUtil;\nimport io.mycat.util.ResultSetUtil;\n/**  \n * 功能详细描述:分片join\n * @author sohudo[http://blog.csdn.net/wind520]\n * @create 2015年01月22日 下午6:50:23 \n * @version 0.0.1\n */\n\npublic class ShareJoin implements Catlet {\n\tprivate EngineCtx ctx;\n\tprivate RouteResultset rrs ;\n\tprivate JoinParser joinParser;\n\t\n\tprivate Map<String, byte[]> rows = new ConcurrentHashMap<String, byte[]>();\n\tprivate Map<String,String> ids = new ConcurrentHashMap<String,String>();\n\t//private ConcurrentLinkedQueue<String> ids = new ConcurrentLinkedQueue<String>();\n\t\n\tprivate List<byte[]> fields; //主表的字段\n\tprivate ArrayList<byte[]> allfields;//所有的字段\n\tprivate boolean isMfield=false;\n\tprivate int mjob=0;\n\tprivate int maxjob=0;\n\tprivate int joinindex=0;//关联join表字段的位置\n\tprivate int sendField=0;\n\tprivate boolean childRoute=false;\n\tprivate boolean jointTableIsData=false;\n\t// join 字段的类型，一般情况都是int, long; 增加该字段为了支持非int,long类型的(一般为varchar)joinkey的sharejoin\n \t// 参见：io.mycat.server.packet.FieldPacket 属性： public int type;\n \t// 参见：http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnDefinition\n \tprivate int joinKeyType = Fields.FIELD_TYPE_LONG; // 默认 join 字段为int型\n \t\n\t//重新路由使用\n\tprivate SystemConfig sysConfig; \n\tprivate SchemaConfig schema;\n\tprivate int sqltype; \n\tprivate String charset; \n\tprivate ServerConnection sc;\t\n\tprivate LayerCachePool cachePool;\n\t\n\tprivate RowDataSorter sorter; //排序器。\n\tprivate volatile boolean isInit = false;\n\tpublic void setRoute(RouteResultset rrs){\n\t\tthis.rrs =rrs;\n\t}\t\n\t\n\tpublic void route(SystemConfig sysConfig, SchemaConfig schema,int sqlType, String realSQL, String charset, ServerConnection sc,\tLayerCachePool cachePool) {\n\t\tint rs = ServerParse.parse(realSQL);\n\t\tthis.sqltype = rs & 0xff;\n\t\tthis.sysConfig=sysConfig; \n\t\tthis.schema=schema;\n\t\tthis.charset=charset; \n\t\tthis.sc=sc;\t\n\t\tthis.cachePool=cachePool;\t\t\n\t\ttry {\n\t\t //  RouteStrategy routes=RouteStrategyFactory.getRouteStrategy();\t\n\t\t  // rrs =RouteStrategyFactory.getRouteStrategy().route(sysConfig, schema, sqlType2, realSQL,charset, sc, cachePool);\t\t   \n\t\t\tMySqlStatementParser parser = new MySqlStatementParser(realSQL);\t\t\t\n\t\t\tSQLStatement statement = parser.parseStatement();\n\t\t\tif(statement instanceof SQLSelectStatement) {\n\t\t\t   SQLSelectStatement st=(SQLSelectStatement)statement;\n\t\t\t   SQLSelectQuery sqlSelectQuery =st.getSelect().getQuery();\n\t\t\t\tif(sqlSelectQuery instanceof MySqlSelectQueryBlock) {\n\t\t\t\t\tMySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock)sqlSelectQuery;\n\t\t\t\t\tjoinParser=new JoinParser(mysqlSelectQuery,realSQL);\n\t\t\t\t\tjoinParser.parser();\n\t\t\t\t}\t\n\t\t\t}\n\t\t   /*\t\n\t\t   if (routes instanceof DruidMysqlRouteStrategy) {\n\t\t\t   SQLSelectStatement st=((DruidMysqlRouteStrategy) routes).getSQLStatement();\n\t\t\t   SQLSelectQuery sqlSelectQuery =st.getSelect().getQuery();\n\t\t\t\tif(sqlSelectQuery instanceof MySqlSelectQueryBlock) {\n\t\t\t\t\tMySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock)st.getSelect().getQuery();\n\t\t\t\t\tjoinParser=new JoinParser(mysqlSelectQuery,realSQL);\n\t\t\t\t\tjoinParser.parser();\n\t\t\t\t}\n\t\t   }\n\t\t   */\n\t\t} catch (Exception e) {\n\t\t\n\t\t}\n\t}\n\tprivate void getRoute(String sql){\n\t\ttry {\n\t\t  if (joinParser!=null){\n\t\t\trrs =RouteStrategyFactory.getRouteStrategy().route(sysConfig, schema, sqltype,sql,charset, sc, cachePool);\n\t\t  }\n\t\t} catch (Exception e) {\n\t\t\t\n\t\t}\n\t}\n\tprivate String[] getDataNodes(){\t\t\n\t\tString[] dataNodes =new String[rrs.getNodes().length] ;\n\t\tfor (int i=0;i<rrs.getNodes().length;i++){\n\t\t\tdataNodes[i]=rrs.getNodes()[i].getName();\n\t\t}\n\t\treturn dataNodes;\n\t}\n\tprivate String getDataNode(String[] dataNodes){\n\t\tString dataNode=\"\";\n\t\tfor (int i=0;i<dataNodes.length;i++){\n\t\t\tdataNode+=dataNodes[i]+\",\";\n\t\t}\n\t\treturn dataNode;\n\t}\n\t\n\tpublic void processSQL(String sql, EngineCtx ctx) {\n\t\t/*\n\t\t *  获取左边表的sql 获取路由\n\t\t * */\n\t\tString ssql=joinParser.getSql();\n\t\tgetRoute(ssql);\n\t\tRouteResultsetNode[] nodes = rrs.getNodes();\n\t\tif (nodes == null || nodes.length == 0 || nodes[0].getName() == null\n\t\t\t\t|| nodes[0].getName().equals(\"\")) {\n\t\t\tctx.getSession().getSource().writeErrMessage(ErrorCode.ER_NO_DB_ERROR,\n\t\t\t\t\t\"No dataNode found ,please check tables defined in schema:\"\n\t\t\t\t\t\t\t+ ctx.getSession().getSource().getSchema());\n\t\t\treturn;\n\t\t} \n\t\tthis.ctx=ctx;\n\t\t//是否可以流式输出\n\t\tif(joinParser.hasLimit() || joinParser.hasOrder()){\n\t\t\tctx.setIsStreamOutputResult(false);\n\t\t}\n\t\t\n\t\tString[] dataNodes =getDataNodes();\n\t\tmaxjob=dataNodes.length;\n\t \n\t\t/*\n\t\t *  发送左边表的sql 到每个分片节点去查询\n\t\t * */\n    \t//huangyiming\n\t\tShareDBJoinHandler joinHandler = new ShareDBJoinHandler(this,joinParser.getJoinLkey(),sc.getSession2());\t\t\n\t\tctx.executeNativeSQLSequnceJob(dataNodes, ssql, joinHandler);\n    \tEngineCtx.LOGGER.info(\"Catlet exec:\"+getDataNode(getDataNodes())+\" sql:\" +ssql);\n    \tfinal ShareJoin shareJoin = this;\n\t\tctx.setAllJobFinishedListener(new AllJobFinishedListener() {\n\t\t\t@Override\n\t\t\tpublic void onAllJobFinished(EngineCtx ctx) {\t\t\t\t\n\t\t\t\t if (!jointTableIsData) {\n\t\t\t\t\t ctx.writeHeader(fields);\n\t\t\t\t }\n\t\t\t\t \n\t\t\t\t MiddlerResultHandler middlerResultHandler = sc.getSession2().getMiddlerResultHandler();\n\n\t\t\t\t\tif(  middlerResultHandler !=null ){\n\t\t\t\t\t\t//sc.getSession2().setCanClose(false);\n\t\t\t\t\t\tmiddlerResultHandler.secondEexcute(); \n\t\t\t\t\t} else{\n\t\t\t\t\t\tshareJoin.writeEof();\n\t\t\t\t\t}\n\t\t\t\tEngineCtx.LOGGER.info(\"发送数据OK\"); \n\t\t\t}\n\t\t});\n\t}\n\t\n    \n\n\tpublic void putDBRow(String id,String nid, byte[] rowData,int findex){\n    \trows.put(id, rowData);\t// 主键 -> 一行数据\n    \tids.put(id, nid);       // 主键 ->  joinkey value\n    \tjoinindex=findex;\n\t\t//ids.offer(nid);\n\t\tint batchSize = 999;\n\t\t// 满1000条，发送一个查询请求\n\t\tif (ids.size() > batchSize) {\n\t\t\tcreateQryJob(batchSize);\n\t\t}            \t\n    }\n    \n    public void putDBFields(List<byte[]> mFields){\n    \t if (!isMfield){\n    \t\t fields=mFields; \n    \t }    \t\n    }    \n   \n    //发送最后的主表查询语句    \n   public void endJobInput(String dataNode, boolean failed){\n\t   mjob++; //结束任务+1\n\t   if (mjob>=maxjob){ \n\t\t   //发送最后一个右边表的查询语句。\n\t\t createQryJob(Integer.MAX_VALUE);\n\t     ctx.endJobInput();\n\t   }\n\t  // EngineCtx.LOGGER.info(\"完成\"+mjob+\":\" + dataNode+\" failed:\"+failed);\n   }\n   \n\t//private void createQryJob(String dataNode,int batchSize) {\t\n\tprivate void createQryJob(int batchSize) {\t\n\t\tint count = 0;\n\t\tMap<String, byte[]> batchRows = new ConcurrentHashMap<String, byte[]>();\n\t\tString theId = null;\n\t\tStringBuilder sb = new StringBuilder().append('(');\n\t\tString svalue=\"\";\n\t\tfor(Map.Entry<String,String> e: ids.entrySet() ){\n\t\t\ttheId=e.getKey();\n\t\t\tbyte[] rowbyte = rows.remove(theId);\n\t\t\tif(rowbyte!=null){\n\t\t\t\tbatchRows.put(theId, rowbyte);\n\t\t\t}\t\t\t\n\t\t\tif (!svalue.equals(e.getValue())){\n\t\t\t\tif(joinKeyType == Fields.FIELD_TYPE_VAR_STRING \n\t\t\t\t\t\t|| joinKeyType == Fields.FIELD_TYPE_STRING){ // joinkey 为varchar\n\t\t\t\t\t\tsb.append(\"'\").append(e.getValue()).append(\"'\").append(','); // ('digdeep','yuanfang') \n\t\t\t\t}else{ // 默认joinkey为int/long\n\t\t\t\t\tsb.append(e.getValue()).append(','); // (1,2,3) \n\t\t\t\t}\n\t\t\t}\n\t\t\tsvalue=e.getValue();\n\t\t\tif (count++ > batchSize) {\n\t\t\t\tbreak;\n\t\t\t}\t\t\t\n\t\t}\n\t\t/*\n\t\twhile ((theId = ids.poll()) != null) {\n\t\t\tbatchRows.put(theId, rows.remove(theId));\n\t\t\tsb.append(theId).append(',');\n\t\t\tif (count++ > batchSize) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t*/\n\t\tif (count == 0) {\n\t\t\treturn;\n\t\t}\n\t\tjointTableIsData=true;\n\t\tsb.deleteCharAt(sb.length() - 1).append(')');\n\t\t//select * from tableB where joinKey in (id1,id2);\n\t\tString sql = String.format(joinParser.getChildSQL(), sb);\n\t\t\n\t\t//if (!childRoute){\n\t\t  getRoute(sql);\n\t\t //childRoute=true;\n\t\t//}\n\t\t \n\t\t //\n\t\tctx.executeNativeSQLParallJob(getDataNodes(),sql, new ShareRowOutPutDataHandler(this,fields,joinindex,joinParser.getJoinRkey(), batchRows,ctx.getSession()));\n\t\tEngineCtx.LOGGER.info(\"SQLParallJob:\"+getDataNode(getDataNodes())+\" sql:\" + sql);\t\t\n\t}  \n\tpublic void writeHeader(String dataNode,List<byte[]> afields, List<byte[]> bfields) {\n\t\tsendField++;\n\t\tif (sendField==1){\t\t  \t\n\t\t\t//huangyiming add 只是中间过程数据不能发送给客户端\n\t\t\tMiddlerResultHandler middlerResultHandler = sc.getSession2().getMiddlerResultHandler();\n \t\t\tif(middlerResultHandler ==null ){\n\t\t\t\t ctx.writeHeader(afields, bfields);\n \t\t\t}  \n \t\t  setAllFields(afields, bfields);\n\t\t // EngineCtx.LOGGER.info(\"发送字段2:\" + dataNode);\t\t  \t\t   \n\t\t}\n\t   setRowDataSorterHeader(afields, bfields);\t\t\t    \t\t\t  \t\t    \t\t\t\n\n\t}\n\t//设置排序结果收集齐\n\tprivate void setRowDataSorterHeader(List<byte[]> afields, List<byte[]> bfields) {\n\t\tif(!ctx.getIsStreamOutputResult() && !isInit) { \t\t\t\t\t\t\n\t\t\t   synchronized (joinParser) {\n\t\t\t\t   if(!isInit){\n\t\t \t\t\t  LinkedHashMap<String, Integer> orderByCols =  joinParser.getOrderByCols();\n\t\t \t\t\t  LinkedHashMap<String, Integer> childOrderByCols =  joinParser.getChildByCols();\n\t\t \t\t\t  \n\t\t \t\t\t  OrderCol[] orderCols = new OrderCol[orderByCols.size() + childOrderByCols.size()];\n\t\t \t\t\t  //a 表 排序字段\n\t\t \t\t\t  for(String fileldName : orderByCols.keySet()) {\n\t\t \t\t\t\t  ColMeta colMeta =  getCommonFieldIndex(afields, fileldName); //colMeta 放置类型，字段的位置\n\t\t \t\t\t\t  int val = orderByCols.get(fileldName);\n\t\t \t\t\t\t  int orignIndex =  TableFilter.decodeOrignOrder(val); //字段在排序时候的位子\n\t\t \t\t\t\t  int orderType =  TableFilter.decodeOrderType(val); //0:asc or  1:desc\n\t\t \t\t\t\t  orderCols[orignIndex] = new OrderCol(colMeta, orderType); \n\t\t \t\t\t  }\n\t\t \t\t\t  \n\t\t \t\t\t  //b 表\n\t\t \t\t\t  for(String fileldName : childOrderByCols.keySet()) {\n\t\t \t\t\t\t  ColMeta colMeta =  getCommonFieldIndex(bfields, fileldName);\n\t\t \t\t\t\t  colMeta.setColIndex(colMeta.getColIndex() + afields.size() -1); // b的字段的位子 在a表字段之后 而且 少了一个joinKey 所以-1 \n \t\t \t\t\t\t  int val = childOrderByCols.get(fileldName);\n\t\t \t\t\t\t  int orignIndex =  TableFilter.decodeOrignOrder(val);\n\t\t \t\t\t\t  int orderType =  TableFilter.decodeOrderType(val);\n\t\t \t\t\t\t  orderCols[orignIndex] = new OrderCol(colMeta, orderType);\n\t\t \t\t\t  }\n\t\t \t\t\t  \n\t\t \t\t\t  RowDataSorter tmp = new RowDataSorter(orderCols);\t\t \t\t\t  \n\t\t \t\t\t  tmp.setLimit(joinParser.getOffset(), joinParser.getRowCount());\n\t\t \t\t\t  sorter = tmp;\n\t\t \t\t\t  isInit = true;\n\t\t\t\t   }\n\t\t\t   }\n \t\t   }\n\t}\n\tprivate void setAllFields(List<byte[]> afields, List<byte[]> bfields){\t\t\n\t\tallfields=new ArrayList<byte[]>();\n\t\tfor (byte[] field : afields) {\n\t\t\tallfields.add(field);\n\t\t}\n\t\t//EngineCtx.LOGGER.info(\"所有字段2:\" +allfields.size());\n\t\tfor (int i=1;i<bfields.size();i++){\n\t\t\tallfields.add(bfields.get(i));\n\t\t}\n\t\t\n\t}\n\tpublic List<byte[]> getAllFields(){\t\t\n\t\treturn allfields;\n\t}\n\tpublic void writeRow(RowDataPacket rowDataPkg){\t\t\t\n\t\tif(ctx.getIsStreamOutputResult()){\n\t\t\tctx.writeRow(rowDataPkg);\n\t\t} else {\t\t\t\n\t\t\tif(isInit ){\n\t\t\t\t//保证可见性\n\t\t\t\tsorter.addRow(rowDataPkg);\t\n\t\t\t} else {\t\t\t\t\n\t\t\t\tEngineCtx.LOGGER.error(\"===怎么会还没初始化===\");\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\t\n\tprotected void writeEof() {\t\t\n\t\tboolean t = jointTableIsData;\n\t\tif(ctx.getIsStreamOutputResult() || (!jointTableIsData)){\n\t\t\tctx.writeEof();\n\t\t} else {\n\t\t\t//limit 输出。\n\t\t\tint start = joinParser.getOffset();\n\t\t\tint end = start + joinParser.getRowCount();\n\t\t\tList<RowDataPacket> results = sorter.getSortedResult();\n\t\t\tif (start < 0) {\n\t\t\t\tstart = 0;\n\t\t\t}\n\t\t\t\n\t\t\tif (joinParser.getRowCount() <= 0) {\n\t\t\t\tend = results.size();\n\t\t\t}\n\t\t\t\n\t\t\tif (end > results.size()) {\n\t\t\t\tend = results.size();\n\t\t\t}\t\t\t\n\t\t\tfor (int i = start; i < end; i++) {\n\t\t\t\tctx.writeRow(results.get(i));\n\t\t\t}\n\t\t\tctx.writeEof();\n\t\t}\n\t}\n\t//获取fKey在字段 的索引位置。\n\tpublic int getFieldIndex(List<byte[]> fields,String fkey){\n\t\tint i=0;\n\t\tfor (byte[] field :fields) {\t\n\t\t\t  FieldPacket fieldPacket = new FieldPacket();\n\t\t\t  fieldPacket.read(field);\t\n\t\t\t  if (ByteUtil.getString(fieldPacket.orgName).equals(fkey)){\n\t\t\t\t  joinKeyType = fieldPacket.type;\n\t\t\t\t  return i;\t\t\t\t  \n\t\t\t  }\n\t\t\t  i++;\n\t\t\t}\n\t\treturn i;\t\t\n\t}\t\n\t//获取fKey在字段 的索引位置。\n\tpublic ColMeta getCommonFieldIndex(List<byte[]> fields,String fkey){\n\t\tint i=0;\n\t\tfor (byte[] field :fields) {\t\n\t\t\t  FieldPacket fieldPacket = new FieldPacket();\n\t\t\t  fieldPacket.read(field);\t\n\t\t\t  if (ByteUtil.getString(fieldPacket.orgName).equals(fkey)){\t\t\t\t  \n\t\t\t\t  return new ColMeta(i, fieldPacket.type);\t\t\t\t  \t\t\t\t  \n\t\t\t  }\n\t\t\t  i++;\n\t\t\t}\n\t\treturn null;\t\t\n\t}\t\n}\n\nclass ShareDBJoinHandler implements SQLJobHandler {\n\tprivate List<byte[]> fields;\n\tprivate final ShareJoin ctx;\n\tprivate String joinkey;\n\tprivate NonBlockingSession session;\n\tpublic ShareDBJoinHandler(ShareJoin ctx,String joinField,NonBlockingSession session) {\n\t\tsuper();\n\t\tthis.ctx = ctx;\n\t\tthis.joinkey=joinField;\n\t\tthis.session = session;\n\t\t//EngineCtx.LOGGER.info(\"二次查询:\"  +\" sql:\" + querySQL+\"/\"+joinkey);\n\t}\n\n\t//private Map<String, byte[]> rows = new ConcurrentHashMap<String, byte[]>();\n\t//private ConcurrentLinkedQueue<String> ids = new ConcurrentLinkedQueue<String>();\n\n\t@Override\n\tpublic void onHeader(String dataNode, byte[] header, List<byte[]> fields) {\n\t\tthis.fields = fields;\n\t\tctx.putDBFields(fields);\n\t}\n\t\n\n\t/*\n\tpublic static String getFieldNames(List<byte[]> fields){\n\t\tString str=\"\";\n\t\tfor (byte[] field :fields) {\t\n\t\t  FieldPacket fieldPacket = new FieldPacket();\n\t\t  fieldPacket.read(field);\t\n\t\t  str+=ByteUtil.getString(fieldPacket.name)+\",\";\n\t\t}\n\t\treturn str;\n\t}\n\t\n\tpublic static String getFieldName(byte[] field){\n\t\tFieldPacket fieldPacket = new FieldPacket();\n\t\tfieldPacket.read(field);\t\n\t\treturn ByteUtil.getString(fieldPacket.name);\n\t}\n\t*/\n\t@Override\n\tpublic boolean onRowData(String dataNode, byte[] rowData) {\n\t\tint fid=this.ctx.getFieldIndex(fields,joinkey);\n\t\tString id = ResultSetUtil.getColumnValAsString(rowData, fields, 0);//主键，默认id\n\t\tString nid = ResultSetUtil.getColumnValAsString(rowData, fields, fid); //joinKey 的value\n\t\t// 放入结果集\n\t\t//rows.put(id, rowData);\n\t\tctx.putDBRow(id,nid, rowData,fid);\n\t\treturn false;\n\t}\n\t// 收到结束包调用 或者发生错误时候调用。\n\t@Override\n\tpublic void finished(String dataNode, boolean failed, String errorMsg) {\n\t\tif(failed){\n\t\t\tsession.getSource().writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR, errorMsg);\n\t\t}else{\n\t\t\tctx.endJobInput(dataNode,failed);\n\t\t}\n\t}\n\n}\n\nclass ShareRowOutPutDataHandler implements SQLJobHandler {\n\tprivate final List<byte[]> afields; // a表的字段信息\n\tprivate List<byte[]> bfields; //B表(右边) 字段信息\n\tprivate final ShareJoin ctx; //  sharejoin 的context \n\tprivate final Map<String, byte[]> arows; //\t map a表的记录值  id -> 行记录\n\tprivate int joinL;//A表(左边)关联字段的位置\n\tprivate int joinR;//B表(右边)关联字段的位置\n\tprivate String joinRkey;//B表(右边)关联字段\n\tpublic NonBlockingSession session;\n\n\tpublic ShareRowOutPutDataHandler(ShareJoin ctx,List<byte[]> afields,int joini,String joinField,Map<String, byte[]> arows,NonBlockingSession session) {\n\t\tsuper();\n\t\tthis.afields = afields; // a表的字段信息\n\t\tthis.ctx = ctx;   //  sharejoin 的context \n\t\tthis.arows = arows;\t //\t map a表的记录值  id -> 行记录\n\t\tthis.joinL =joini; //a 表 joinKey的索引位置。\n\t\tthis.joinRkey= joinField; // b 表 joinKey的字段名称。\n\t\tthis.session = session; // mycatSession\n\t\t//EngineCtx.LOGGER.info(\"二次查询:\" +arows.size()+ \" afields：\"+FenDBJoinHandler.getFieldNames(afields));\n    }\n\n\t@Override\n\tpublic void onHeader(String dataNode, byte[] header, List<byte[]> bfields) {\n\t\t  this.bfields=bfields;\t\t\n\t\t  joinR=this.ctx.getFieldIndex(bfields,joinRkey);\n\t\t  MiddlerResultHandler middlerResultHandler = session.getMiddlerResultHandler();\n\n\t\t\tif(  middlerResultHandler ==null ){\n\t\t\t\t  ctx.writeHeader(dataNode,afields, bfields);\n\n\t\t\t} \n \t}\n\n\t//不是主键，获取join左边的的记录\n\tprivate byte[] getRow(Map<String, byte[]> batchRowsCopy,String value,int index){\n\t\tfor(Map.Entry<String,byte[]> e: batchRowsCopy.entrySet() ){\n\t\t\tString key=e.getKey();\n\t\t\tRowDataPacket rowDataPkg = ResultSetUtil.parseRowData(e.getValue(), afields);\n\t\t\t\n\t\t\tbyte[] columnValue = rowDataPkg.fieldValues.get(index); //a 表记录 joinKey的value \n\t\t\tif(columnValue == null )\n\t\t\t\tcontinue;\n\t\t\t\n\t\t\tString id = ByteUtil.getString(columnValue);\n\t\t\tif (id.equals(value)){\n\t\t\t\treturn batchRowsCopy.remove(key);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean onRowData(String dataNode, byte[] rowData) {\n\t\tRowDataPacket rowDataPkgold = ResultSetUtil.parseRowData(rowData, bfields);\n\t\t//拷贝一份batchRows\n\t\tMap<String, byte[]> batchRowsCopy = new ConcurrentHashMap<String, byte[]>();\n\t\tbatchRowsCopy.putAll(arows);\n\t\t// 获取Id字段，\n\t\tString id = ByteUtil.getString(rowDataPkgold.fieldValues.get(joinR));\n\t\t// 查找ID对应的A表的记录\n\t\tbyte[] arow = getRow(batchRowsCopy,id,joinL);//arows.remove(id);\n//\t\tbyte[] arow = getRow(id,joinL);//arows.remove(id);\n\t\twhile (arow!=null) {\n\t\t\tRowDataPacket rowDataPkg = ResultSetUtil.parseRowData(arow,afields );//ctx.getAllFields());\n\t\t\t//将 b记录的值 复制到 a记录中 成为新的记录。\n\t\t\tfor (int i=1;i<rowDataPkgold.fieldCount;i++){\n\t\t\t\t// 设置b.name 字段\n\t\t\t\tbyte[] bname = rowDataPkgold.fieldValues.get(i);\n\t\t\t\trowDataPkg.add(bname);\n\t\t\t\trowDataPkg.addFieldCount(1);\n\t\t\t}\n\t\t\t//RowData(rowDataPkg);\n\t\t\t// huangyiming add\n\t\t\tMiddlerResultHandler middlerResultHandler = session.getMiddlerResultHandler();\n\t\t\tif(null == middlerResultHandler ){\n\t\t\t\t// \n\t\t\t\tctx.writeRow(rowDataPkg);\n\t\t\t\t\n\t\t\t}else{\n\t\t\t\t\n\t\t\t\t if(middlerResultHandler instanceof MiddlerQueryResultHandler){\n\t\t\t\t\t// if(middlerResultHandler.getDataType().equalsIgnoreCase(\"string\")){\n\t\t\t\t\t\t byte[] columnData = rowDataPkg.fieldValues.get(0);\n\t\t\t\t\t\t if(columnData !=null && columnData.length >0){\n \t\t\t\t\t\t\t String rowValue =    new String(columnData);\n\t\t\t\t\t\t\t middlerResultHandler.add(rowValue);\t\n\t\t\t\t\t\t }\n\t\t\t\t   //}\n\t\t\t\t }\n\t\t\t\t\n\t\t\t} \n\t\t\t\n\t\t\tarow = getRow(batchRowsCopy,id,joinL);\n//\t\t   arow = getRow(id,joinL);\n\t\t}\n\t\treturn false;\n\t}\n\t\n\n\t@Override\n\tpublic void finished(String dataNode, boolean failed, String errorMsg) {\n\t\tif(failed){\n\t\t\tsession.getSource().writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR, errorMsg);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/catlets/TableFilter.java",
    "content": "package io.mycat.catlets;\r\n\r\nimport java.util.Iterator;\r\nimport java.util.LinkedHashMap;\r\nimport java.util.Map;\r\nimport java.util.Map.Entry;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport io.mycat.sqlengine.mpp.OrderCol;\r\nimport io.mycat.util.StringUtil;\r\n/**  \r\n * 功能详细描述:分片join,单独的语句\r\n * @author sohudo[http://blog.csdn.net/wind520]\r\n * @create 2015年02月01日 \r\n * @version 0.0.1\r\n */\r\n\r\npublic class TableFilter {\r\n\tprotected static final Logger LOGGER = LoggerFactory.getLogger(TableFilter.class);\r\n\t\r\n\tprivate LinkedHashMap<String,String> fieldAliasMap = new LinkedHashMap<String,String>(); \r\n\tprivate String tName; //table 名称\r\n\tprivate String tAlia; //table别名\r\n\tprivate String where=\"\"; //where 条件\r\n\tprivate String order=\"\"; \r\n\t\r\n\tprivate String parenTable=\"\";//左连接的join的表\r\n    private String joinParentkey=\"\";//左连接的join字段\r\n    private String joinKey=\"\";\t//join字段\r\n\t\r\n\tprivate TableFilter join; //右连接的join表\r\n\tprivate TableFilter parent; //左连接的join的表\r\n\t\r\n\tprivate int offset=0;\r\n\tprivate int rowCount=0;\r\n\t\r\n\tprivate boolean outJoin;\r\n\tprivate boolean allField;\r\n\tpublic TableFilter(String taName,String taAlia,boolean outJoin) {\r\n\t\tthis.tName=taName;\r\n\t\tthis.tAlia=taAlia;\r\n\t\tthis.outJoin=outJoin;\r\n\t\tthis.where=\"\";\r\n\t}\t\r\n\t\r\n\t/**\r\n\t *  a.fieldName 获取表名称\r\n\t *  返回 table 名称。\r\n\t*/\r\n\tprivate String getTablefrom(String key){\r\n\t\tif (key==null){\r\n\t\t\treturn \"\";\t\r\n\t\t}\r\n\t\telse {\r\n\t\t\tint i=key.indexOf('.');\r\n\t\t\tif (i==-1){\r\n\t\t\t\treturn key;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\treturn key.substring(0, i);\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t}\r\n\t/**\r\n\t *  a.fieldName 获取字段\r\n\t *  返回 fieldName\r\n\t*/\r\n\tprivate String getFieldfrom(String key){\r\n\t\tif (key==null){\r\n\t\t\treturn \"\";\t\r\n\t\t}\r\n\t\telse {\r\n\t\t  int i=key.indexOf('.');\r\n\t\t\tif (i==-1){\r\n\t\t\t\treturn key;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\treturn key.substring(i + 1);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\t\r\n\tpublic void addField(String fieldName,String fieldAlia){\r\n\t\tString atable=getTablefrom(fieldName); //表名称\r\n\t\tString afield=getFieldfrom(fieldName); //字段\r\n\t\tboolean allfield=afield.equals(\"*\")?true:false; //是否有*\r\n\t\tif (atable.equals(\"*\")) {\r\n\t\t  fieldAliasMap.put(afield, null); // 放入字段 * 到 fieldMap中。\r\n\t\t  setAllField(allfield); \r\n\t\t  if (join!=null) {\r\n\t\t\t join.addField(fieldName,null); //递归设置子表  \r\n\t\t\t join.setAllField(allfield);\r\n\t\t   }\t\t  \r\n\t\t}\r\n\t\telse {\r\n\t\t  if (atable.equals(tAlia)) { //将字段放入对应的表中。\r\n\t\t    fieldAliasMap.put(afield, fieldAlia);\r\n\t\t    setAllField(allfield);\r\n\t\t }\r\n\t\t  else {\r\n\t\t    if (join!=null) {//B表中查询 是否可以放入字段。\r\n\t\t\t  join.addField(fieldName,fieldAlia);  \r\n\t\t\t  join.setAllField(allfield);\r\n\t\t     }\r\n\t\t   }\r\n\t\t}\r\n\t}\r\n\t//解析字段\r\n\tpublic void addField(String fieldName,String fieldAlia,String expr){\r\n\t\tString atable=getTablefrom(fieldName);\r\n\t\tString afield=getFieldfrom(fieldName);\r\n\t\tboolean allfield=afield.equals(\"*\")?true:false;\r\n\t\tif (atable.equals(\"*\")) {\r\n\t\t  fieldAliasMap.put(afield, null);\r\n\t\t  setAllField(allfield);\r\n\t\t  if (join!=null) {\r\n\t\t\t join.addField(fieldName,null);  \r\n\t\t\t join.setAllField(allfield);\r\n\t\t   }\t\t  \r\n\t\t}\r\n\t\telse {\r\n\t\t  if (atable.equals(tAlia)) {\r\n\t\t\texpr = expr.replace(fieldName, afield);\r\n\t\t    fieldAliasMap.put(expr, fieldAlia);\r\n\t\t    setAllField(allfield);\r\n\t\t }\r\n\t\t  else {\r\n\t\t    if (join!=null) {\r\n\t\t\t  join.addField(fieldName,fieldAlia,expr);  \r\n\t\t\t  join.setAllField(allfield);\r\n\t\t     }\r\n\t\t   }\r\n\t\t}\r\n\t}\r\n\t\r\n\t\r\n\tpublic void addWhere(String fieldName,String value,String Operator,String and){\r\n\t\tString atable=getTablefrom(fieldName);\r\n\t\tString afield=getFieldfrom(fieldName);\r\n\t\tif (atable.equals(tAlia)) {\r\n\t\t\t// 修复在拼接sql的时候少空格的bug\r\n\t\t\twhere=unionsql(where,afield + \" \" + Operator + \" \" + value,and);\r\n\t\t}\r\n\t\telse {\r\n\t\t  if (join!=null) {\r\n\t\t\t  join.addWhere(fieldName,value,Operator,and);  \r\n\t\t  }\r\n\t\t}\r\n\t}\r\n\t\r\n\tpublic void addWhere(String fieldName,String condition,String and){\r\n\t\tString atable=getTablefrom(fieldName);\r\n\t\tString afield=getFieldfrom(fieldName);\r\n\t\tcondition = condition.replace(fieldName, afield);\r\n\t\tif (atable.equals(tAlia)) {\r\n\t\t\twhere=unionsql(where,condition,and);\r\n\t\t}\r\n\t\telse {\r\n\t\t  if (join!=null) {\r\n\t\t\t  join.addWhere(fieldName,condition,and);  \r\n\t\t  }\r\n\t\t}\r\n\t}\r\n\t\r\n\t\r\n    private String unionsql(String key,String value,String Operator){\r\n    \tif (key.trim().equals(\"\")){\r\n    \t\tkey=value;\r\n    \t}\r\n    \telse {\r\n    \t\tkey+=\" \"+Operator+\" \"+value;\r\n    \t}\r\n    \treturn key;\r\n    }\r\n    \r\n    LinkedHashMap<String, Integer> orderByCols = new LinkedHashMap<String, Integer>();\r\n    public void addOrders(int index, String fieldName,String des){\r\n//\t\tString atable=getTablefrom(fieldName);\r\n\t\tString afield=getFieldfrom(fieldName);\r\n\t\r\n\t\tif (fieldInTable(fieldName)) {\r\n\t\t\torder=unionsql(order,afield+\" \"+des,\",\");\r\n\t\t\t//将order 类型加入到链表中. 還得看下是否使用別名\r\n\t\t\t\r\n\t\t\tint orderType = StringUtil.isEmpty(des)||\"asc\".equals(des.toLowerCase().trim())?OrderCol.COL_ORDER_TYPE_ASC:OrderCol.COL_ORDER_TYPE_DESC;\r\n\t\t\torderByCols.put(afield, encodeOrignOrderType(index, orderType));\r\n\t\t}\r\n\t\telse {\r\n\t\t  if (join!=null) {\r\n\t\t\t  join.addOrders(index, fieldName,des);  \r\n\t\t  }\r\n\t\t}\r\n\t}\r\n    /*\r\n     * 判断字段是否在表中。\r\n     * */\r\n    private boolean fieldInTable(String fieldName) {\r\n\t\tString atable=getTablefrom(fieldName);\r\n\t\tif(atable.equals(tAlia) ){\r\n\t\t\treturn true;\r\n\t\t}\t\t\r\n\t\tif(StringUtil.isEmpty(atable)){\r\n\t\t\tString afield=getFieldfrom(fieldName);\r\n\t\t\tfor(String tableField : fieldAliasMap.keySet()) {\r\n\t\t\t\tif(afield.equals(fieldAliasMap.get(tableField))) {\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n    \treturn false;\r\n    }\r\n    \r\n\tpublic LinkedHashMap<String, Integer> getOrderByCols(){\r\n\t\treturn orderByCols;\r\n\t}\t\r\n\tpublic void addLimit(int offset,int rowCount){\r\n\t\tthis.offset=offset;\r\n\t\tthis.rowCount=rowCount;\r\n\t}\r\n\tpublic void setJoinKey(String fieldName,String value){\r\n\t\tif (parent==null){\r\n\t\t\tif (join!=null)\t{\r\n\t\t\t\tjoin.setJoinKey(fieldName,value);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse {\r\n\t\t//1 ，fileName 为当前的joinKey \r\n\t\t//2 value 为当前的joinKey \r\n\t\t int i=joinLkey(fieldName,value);\r\n\t\t if (i==1){\r\n\t\t\t joinParentkey=getFieldfrom(value);\r\n\t\t\t parenTable   =getTablefrom(value);\r\n\t\t\t joinKey=getFieldfrom(fieldName);\r\n\t\t }\r\n\t\t else {\t\t\t\r\n\t\t   if (i==2){\r\n\t\t\t   joinParentkey=getFieldfrom(fieldName);\r\n\t\t\t   parenTable   =getTablefrom(fieldName);\r\n\t\t\t   joinKey=getFieldfrom(value);\r\n\t\t   }\r\n\t\t   else {\r\n\t\t\t\t  if (join!=null) {\r\n\t\t\t\t\t  join.setJoinKey(fieldName,value);  \r\n\t\t\t\t  }\t\t   \r\n\t\t   }\r\n\t\t }\r\n\t\t}\r\n\t}\r\n\t\r\n\tprivate String getChildJoinKey(boolean left){\r\n\t   if (join!=null){\r\n\t\t\tif (left) {\r\n\t\t\t\treturn join.joinParentkey;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\treturn join.joinKey;\r\n\t\t\t}\t\t   \r\n\t   }\r\n\t   else {\r\n\t\t   return \"\";\r\n\t   }\r\n\t}\r\n\tpublic String getJoinKey(boolean left){\r\n\t\treturn getChildJoinKey(left);\r\n\t}\r\n    private int joinLkey(String fieldName,String value){\r\n    \tString key1=getTablefrom(fieldName);\r\n    \tString key2=getTablefrom(value);    \t\r\n    \tif (key1.equals(tAlia) ) {\r\n    \t\treturn 1;\r\n    \t}\r\n    \t\r\n    \tif (key2.equals(tAlia) ) {\r\n    \t\treturn 2;\r\n    \t}     \t\r\n    \t/*\r\n    \t String bAlia=\"\"; \r\n    \tif (join!=null){\r\n    \t\tbAlia=join.getTableAlia();\r\n    \t}\r\n    \tif (key1.equals(tAlia)&& key2.equals(bAlia) ) {\r\n    \t\treturn 1;\r\n    \t}\r\n    \t\r\n    \tif (key2.equals(tAlia)&& key1.equals(bAlia) ) {\r\n    \t\treturn 2;\r\n    \t} \r\n    \t*/\r\n    \treturn 0;\r\n    }\t\r\n\t\r\n\tpublic String getTableName(){\r\n\t\treturn tName;\r\n\t}\t\r\n\tpublic void setTableName(String value){\r\n\t\ttName=value;\r\n\t}\r\n\t\r\n\tpublic String getTableAlia(){\r\n\t\treturn tAlia;\r\n\t}\t\r\n\tpublic void setTableAlia(String value){\r\n\t\ttAlia=value;\r\n\t}\t\r\n\t\r\n\tpublic boolean getOutJoin(){\r\n\t\treturn outJoin;\r\n\t}\t\r\n\tpublic void setOutJoin(boolean value){\r\n\t\toutJoin=value;\r\n\t}\r\n\t\r\n\t\r\n\tpublic boolean getAllField(){\r\n\t\treturn allField;\r\n\t}\t\r\n\tpublic void setAllField(boolean value){\r\n\t\tallField=value;\r\n\t}\t\r\n\t\r\n\tpublic TableFilter getTableJoin(){\r\n\t\treturn join;\r\n\t}\t\r\n\tpublic void setTableJoin(TableFilter  value){\r\n\t\tjoin=value;\r\n\t\tjoin.setParent(this);\r\n\t}\t\r\n    public TableFilter getParent() {\r\n        return parent;\r\n    }\r\n\r\n    public void setParent(TableFilter parent) {\r\n        this.parent = parent;\r\n    }\t\r\n    \r\n    private String unionField(String field,String key,String Operator){\r\n    \tif (key.trim().equals(\"\")){\r\n    \t\tkey=field;\r\n    \t}\r\n    \telse {\r\n    \t\tkey=field+Operator+\" \"+key;\r\n    \t}\r\n    \treturn key;\r\n    }\r\n    \r\n\tpublic String getSQL(){\r\n\t\tString sql=\"\";\r\n\t\tIterator<Entry<String, String>> iter = fieldAliasMap.entrySet().iterator();\r\n\t\twhile (iter.hasNext()) {\r\n\t\t  Map.Entry<String, String> entry = (Map.Entry<String, String>) iter.next();\r\n\t\t  String key = entry.getKey();\r\n\t\t  String val = entry.getValue();\t\t\r\n\t\t\tif (val==null) {\r\n\t\t\t  sql=unionsql(sql,getFieldfrom(key),\",\");\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tsql = unionsql(sql, getFieldfrom(key) + \" as \" + val, \",\");\r\n\t\t\t}\r\n\t\t  }\r\n        if (parent==null){\t// on/where 等于号左边的表\r\n        \tString parentJoinKey = getJoinKey(true);\r\n        \t// fix sharejoin bug： \r\n        \t// (AbstractConnection.java:458) -close connection,reason:program err:java.lang.IndexOutOfBoundsException:\r\n        \t// 原因是左表的select列没有包含 join 列，在获取结果时报上面的错误\r\n        \tif(sql != null && parentJoinKey != null &&  \r\n        \t\t\tsql.toUpperCase().indexOf(parentJoinKey.trim().toUpperCase()) == -1){\r\n        \t\tsql += \", \" + parentJoinKey;\r\n        \t}\r\n\t\t   sql=\"select \"+sql+\" from \"+tName;\r\n\t\t   if (!(where.trim().equals(\"\"))){\r\n\t\t\t\tsql+=\" where \"+where.trim(); \t\r\n\t\t\t}\r\n        }\r\n        else {\t// on/where 等于号右边边的表\r\n        \tif (allField) {\r\n        \t   sql=\"select \"+sql+\" from \"+tName;\r\n        \t}\r\n        \telse {\r\n        \t   sql=unionField(\"select \"+joinKey,sql,\",\");\r\n        \t   sql=sql+\" from \"+tName;\t\t\r\n        \t   //sql=\"select \"+joinKey+\",\"+sql+\" from \"+tName;\r\n        \t}\r\n    \t\tif (!(where.trim().equals(\"\"))){\r\n    \t\t\tsql+=\" where \"+where.trim()+\" and (\"+joinKey+\" in %s )\"; \t\r\n    \t\t}\r\n    \t\telse {\r\n    \t\t\tsql+=\" where \"+joinKey+\" in %s \"; \r\n    \t\t}\r\n        }        \t\r\n        /*\r\n\t\tif (!(order.trim().equals(\"\"))){\r\n\t\t\tsql+=\" order by \"+order.trim(); \t\r\n\t\t}\t\r\n\t\tif (parent==null){\r\n        \tif ((rowCount>0)&& (offset>0)){\r\n        \t\tsql+=\" limit \"+offset+\",\"+rowCount;\r\n        \t}\r\n        \telse {\r\n        \t\tif (rowCount>0){\r\n        \t\t\tsql+=\" limit \"+rowCount;\r\n        \t\t}\r\n        \t}\r\n\t\t}\t*/\r\n\t\treturn sql; \r\n\t}\r\n\t\t\r\n\tpublic int getOffset() {\r\n\t\treturn offset;\r\n\t}\r\n\r\n\t\r\n\tpublic int getRowCount() {\r\n\t\treturn rowCount;\r\n\t}\r\n\t\r\n\tpublic static int encodeOrignOrderType(int index , int orderType) {\r\n\t\treturn index << 1 | orderType;\r\n\t}\r\n\t\r\n\tpublic static int decodeOrderType(int orignOrderType){\r\n\t\treturn orignOrderType & 1;\r\n\t}\r\n\tpublic static int decodeOrignOrder(int orignOrderType){\r\n\t\treturn orignOrderType >> 1;\r\n\t}\r\n\t\r\n\tpublic static void main(String[] args) {\r\n\t\tint val = encodeOrignOrderType(45, 1);\r\n\t\tSystem.out.println(decodeOrderType(val));\r\n\r\n\t\tSystem.out.println(decodeOrignOrder(val));\r\n\t}\r\n\t\r\n\t\r\n\t@Override\r\n\tpublic String toString() {\r\n\t\treturn \"TableFilter [fieldAliasMap=\" + fieldAliasMap + \", tName=\" + tName + \", tAlia=\" + tAlia + \", where=\"\r\n\t\t\t\t+ where + \", order=\" + order + \", parenTable=\" + parenTable + \", joinParentkey=\" + joinParentkey\r\n\t\t\t\t+ \", joinKey=\" + joinKey + \", join=\" + join + \", offset=\" + offset + \", rowCount=\" + rowCount\r\n\t\t\t\t+ \", outJoin=\" + outJoin + \", allField=\" + allField + \"]\";\r\n\t}\t\r\n\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/config/Alarms.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config;\n\n/**\n * Mycat报警关键词定义\n * \n * @author mycat\n */\npublic interface Alarms {\n    /** 默认报警关键词 **/\n    public static final String DEFAULT           = \"#!MyCat#\";\n    \n    /** 集群无有效的节点可提供服务 **/\n    public static final String CLUSTER_EMPTY     = \"#!CLUSTER_EMPTY#\";\n    \n    /** 数据节点的数据源发生切换 **/\n    public static final String DATANODE_SWITCH   = \"#!DN_SWITCH#\";\n    \n    /** 防火墙非法用户访问 **/\n    public static final String FIREWALL_ATTACK = \"#!QT_ATTACK#\";\n   \n    /** 非法DML **/ \n    public static final String DML_ATTACK = \"#!DML_ATTACK#\";\n    \n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/Capabilities.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config;\n\n/**\n * 处理能力标识定义\n * \n * @author mycat\n */\npublic interface Capabilities {\n\n    /**\n     * server capabilities\n     * \n     * <pre>\n     * server:        11110111 11111111\n     * client_cmd: 11 10100110 10000101\n     * client_jdbc:10 10100010 10001111\n     *  \n     * @see http://dev.mysql.com/doc/refman/5.1/en/mysql-real-connect.html\n     * </pre>\n     */\n    // new more secure passwords\n    public static final int CLIENT_LONG_PASSWORD = 1;\n\n    // Found instead of affected rows\n    // 返回找到（匹配）的行数，而不是改变了的行数。\n    public static final int CLIENT_FOUND_ROWS = 2;\n\n    // Get all column flags\n    public static final int CLIENT_LONG_FLAG = 4;\n\n    // One can specify db on connect\n    public static final int CLIENT_CONNECT_WITH_DB = 8;\n\n    // Don't allow database.table.column\n    // 不允许“数据库名.表名.列名”这样的语法。这是对于ODBC的设置。\n    // 当使用这样的语法时解析器会产生一个错误，这对于一些ODBC的程序限制bug来说是有用的。\n    public static final int CLIENT_NO_SCHEMA = 16;\n\n    // Can use compression protocol\n    // 使用压缩协议\n    public static final int CLIENT_COMPRESS = 32;\n\n    // Odbc client\n    public static final int CLIENT_ODBC = 64;\n\n    // Can use LOAD DATA LOCAL\n    public static final int CLIENT_LOCAL_FILES = 128;\n\n    // Ignore spaces before '('\n    // 允许在函数名后使用空格。所有函数名可以预留字。\n    public static final int CLIENT_IGNORE_SPACE = 256;\n\n    // New 4.1 protocol This is an interactive client\n    public static final int CLIENT_PROTOCOL_41 = 512;\n\n    // This is an interactive client\n    // 允许使用关闭连接之前的不活动交互超时的描述，而不是等待超时秒数。\n    // 客户端的会话等待超时变量变为交互超时变量。\n    public static final int CLIENT_INTERACTIVE = 1024;\n\n    // Switch to SSL after handshake\n    // 使用SSL。这个设置不应该被应用程序设置，他应该是在客户端库内部是设置的。\n    // 可以在调用mysql_real_connect()之前调用mysql_ssl_set()来代替设置。\n    public static final int CLIENT_SSL = 2048;\n\n    // IGNORE sigpipes\n    // 阻止客户端库安装一个SIGPIPE信号处理器。\n    // 这个可以用于当应用程序已经安装该处理器的时候避免与其发生冲突。\n    public static final int CLIENT_IGNORE_SIGPIPE = 4096;\n\n    // Client knows about transactions\n    public static final int CLIENT_TRANSACTIONS = 8192;\n\n    // Old flag for 4.1 protocol\n    public static final int CLIENT_RESERVED = 16384;\n\n    // New 4.1 authentication\n    public static final int CLIENT_SECURE_CONNECTION = 32768;\n\n    // Enable/disable multi-stmt support\n    // 通知服务器客户端可以发送多条语句（由分号分隔）。如果该标志为没有被设置，多条语句执行。\n    public static final int CLIENT_MULTI_STATEMENTS = 65536;\n\n    // Enable/disable multi-results\n    // 通知服务器客户端可以处理由多语句或者存储过程执行生成的多结果集。\n    // 当打开CLIENT_MULTI_STATEMENTS时，这个标志自动的被打开。\n    public static final int CLIENT_MULTI_RESULTS = 131072;\n    \n    public static final int CLIENT_PLUGIN_AUTH = 0x00080000; // 524288\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/ConfigInitializer.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport org.apache.log4j.Logger;\n\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.jdbc.JDBCDatasource;\nimport io.mycat.backend.mysql.nio.MySQLDataSource;\nimport io.mycat.backend.postgresql.PostgreSQLDataSource;\nimport io.mycat.config.loader.ConfigLoader;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLConfigLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.DBHostConfig;\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.config.model.DataNodeConfig;\nimport io.mycat.config.model.FirewallConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.UserConfig;\nimport io.mycat.config.util.ConfigException;\nimport io.mycat.route.sequence.handler.DistributedSequenceHandler;\nimport io.mycat.route.sequence.handler.IncrSequenceMySQLHandler;\nimport io.mycat.route.sequence.handler.IncrSequenceTimeHandler;\nimport io.mycat.route.sequence.handler.IncrSequenceZKHandler;\n\n/**\n * @author mycat\n */\npublic class ConfigInitializer {\n\t\n\tprivate static final Logger LOGGER = Logger.getLogger( ConfigInitializer.class );\n\t\n\tprivate volatile SystemConfig system;\n\tprivate volatile MycatCluster cluster;\n\tprivate volatile FirewallConfig firewall;\n\tprivate volatile Map<String, UserConfig> users;\n\tprivate volatile Map<String, SchemaConfig> schemas;\n\tprivate volatile Map<String, PhysicalDBNode> dataNodes;\n\tprivate volatile Map<String, PhysicalDBPool> dataHosts;\n\n\tpublic ConfigInitializer(boolean loadDataHost) {\n\t\t\n\t\t//读取rule.xml和schema.xml\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader();\n\t\t\n\t\t//读取server.xml\n\t\tXMLConfigLoader configLoader = new XMLConfigLoader(schemaLoader);\n\t\t\n\t\tschemaLoader = null;\n\t\t\n\t\t//加载配置\n\t\tthis.system = configLoader.getSystemConfig();\n\t\tthis.users = configLoader.getUserConfigs();\n\t\tthis.schemas = configLoader.getSchemaConfigs();\n\t\t\n\t\t//是否重新加载DataHost和对应的DataNode\n\t\tif (loadDataHost) {\n\t\t\tthis.dataHosts = initDataHosts(configLoader);\n\t\t\tthis.dataNodes = initDataNodes(configLoader);\n\t\t}\n\t\t\n\t\t//权限管理\n\t\tthis.firewall = configLoader.getFirewallConfig();\n\t\tthis.cluster = initCobarCluster(configLoader);\n\t\t\n\t\t//不同类型的全局序列处理器的配置加载\n\t\tif (system.getSequenceHandlerType() == SystemConfig.SEQUENCEHANDLER_MYSQLDB) {\n\t\t\tIncrSequenceMySQLHandler.getInstance().load();\n\t\t}\n\t\t\n\t\tif (system.getSequenceHandlerType() == SystemConfig.SEQUENCEHANDLER_LOCAL_TIME) {\n\t\t\tIncrSequenceTimeHandler.getInstance().load();\n\t\t}\n\t\t\n\t\tif (system.getSequenceHandlerType() == SystemConfig.SEQUENCEHANDLER_ZK_DISTRIBUTED) {\n\t\t\tDistributedSequenceHandler.getInstance(system).load();\n\t\t}\n\t\t\n\t\tif (system.getSequenceHandlerType() == SystemConfig.SEQUENCEHANDLER_ZK_GLOBAL_INCREMENT) {\n\t\t\tIncrSequenceZKHandler.getInstance().load();\n\t\t}\n\t\t\n\t\t/**\n\t\t * 配置文件初始化， 自检\n\t\t */\n\t\tthis.selfChecking0();\n\t}\n\t\n\tprivate void selfChecking0() throws ConfigException {\n\t\t\n\t\t// 检查user与schema配置对应以及schema配置不为空\n\t\tif (users == null || users.isEmpty()) {\n\t\t\tthrow new ConfigException(\"SelfCheck### user all node is empty!\");\n\t\t\t\n\t\t} else {\n\t\t\t\n\t\t\tfor (UserConfig uc : users.values()) {\n\t\t\t\tif (uc == null) {\n\t\t\t\t\tthrow new ConfigException(\"SelfCheck### users node within the item is empty!\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tSet<String> authSchemas = uc.getSchemas();\n\t\t\t\tif (authSchemas == null) {\n\t\t\t\t\tthrow new ConfigException(\"SelfCheck### user \" + uc.getName() + \"refered schemas is empty!\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tfor (String schema : authSchemas) {\n\t\t\t\t\tif ( !schemas.containsKey(schema) ) {\n\t\t\t\t\t\tString errMsg = \"SelfCheck###  schema \" + schema + \" refered by user \" + uc.getName() + \" is not exist!\";\n\t\t\t\t\t\tthrow new ConfigException(errMsg);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\t\n\t\t\n\t\t\n\t\t// schema 配置检测\t\t\n\t\tfor (SchemaConfig sc : schemas.values()) {\n\t\t\tif (null == sc) {\n\t\t\t\tthrow new ConfigException(\"SelfCheck### schema all node is empty!\");\n\t\t\t\t\n\t\t\t} else {\t\t\t\t\n\t\t\t\t// check dataNode / dataHost 节点\n\t\t\t\tif ( this.dataNodes != null &&  this.dataHosts != null  ) {\t\t\t\t\t\n\t\t\t\t\tSet<String> dataNodeNames = sc.getAllDataNodes();\n\t\t\t\t\tfor(String dataNodeName: dataNodeNames) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tPhysicalDBNode node = this.dataNodes.get(dataNodeName);\n\t\t\t\t\t\tif ( node == null ) {\n\t\t\t\t\t\t\tthrow new ConfigException(\"SelfCheck### schema dbnode is empty!\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\t\n\t\t\n\t}\n\t\n\tpublic void testConnection() {\n\t\t\n\t\t// 实际链路的连接测试\t\t\n\t\tif ( this.dataNodes != null &&  this.dataHosts != null  ) {\n\t\t\t\n\t\t\tMap<String, Boolean> map = new HashMap<String, Boolean>();\n\t\t\t\n\t\t\tfor(PhysicalDBNode dataNode: dataNodes.values() ) {\n\t\t\t\t\n\t\t\t\tString database = dataNode.getDatabase();\t\t\n\t\t\t\tPhysicalDBPool pool = dataNode.getDbPool();\n\t\t\t\t\n\t\t\t\tfor (PhysicalDatasource ds : pool.getAllDataSources()) {\t\t\t\t\t\t\t\n\t\t\t\t\tString key = ds.getName() + \"_\" + database;\n\t\t\t\t\tif ( map.get( key ) == null ) {\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\tmap.put( key, false );\n\t\t\t\t\t\t\n\t\t\t\t\t\tboolean isConnected = false;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tisConnected = ds.testConnection( database );\t\t\n\t\t\t\t\t\t\tmap.put( key, isConnected );\n\t\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t\tLOGGER.warn(\"test conn error:\", e);\n\t\t\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t}\t\t\t\t\t\t\t\t\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//\n\t\t\tboolean isConnectivity = true;\n\t\t\tfor (Map.Entry<String, Boolean> entry : map.entrySet()) {\n\t\t\t\tString key = entry.getKey();\n\t\t\t\tBoolean value = entry.getValue();\n\t\t\t\tif ( !value && isConnectivity ) {\n\t\t\t\t\tLOGGER.warn(\"SelfCheck### test \" + key + \" database connection failed \");\t\t\t\t\t\t\t\n\t\t\t\t\tisConnectivity = false;\n\t\t\t\t\t\n\t\t\t\t} else {\n\t\t\t\t\tLOGGER.info(\"SelfCheck### test \" + key + \" database connection success \");\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif ( !isConnectivity ) {\n\t\t\t\tthrow new ConfigException(\"SelfCheck### there are some datasource connection failed, pls check!\");\n\t\t\t}\n\t\t\t\t\n\t\t}\n\t\t\n\t}\n\n\tpublic SystemConfig getSystem() {\n\t\treturn system;\n\t}\n\n\tpublic MycatCluster getCluster() {\n\t\treturn cluster;\n\t}\n\n\tpublic FirewallConfig getFirewall() {\n\t\treturn firewall;\n\t}\n\n\tpublic Map<String, UserConfig> getUsers() {\n\t\treturn users;\n\t}\n\n\tpublic Map<String, SchemaConfig> getSchemas() {\n\t\treturn schemas;\n\t}\n\n\tpublic Map<String, PhysicalDBNode> getDataNodes() {\n\t\treturn dataNodes;\n\t}\n\n\tpublic Map<String, PhysicalDBPool> getDataHosts() {\n\t\treturn this.dataHosts;\n\t}\n\n\tprivate MycatCluster initCobarCluster(ConfigLoader configLoader) {\n\t\treturn new MycatCluster(configLoader.getClusterConfig());\n\t}\n\n\tprivate Map<String, PhysicalDBPool> initDataHosts(ConfigLoader configLoader) {\n\t\tMap<String, DataHostConfig> nodeConfs = configLoader.getDataHosts();\n\t\tboolean isBooster=\"booster\".equalsIgnoreCase(ZkConfig.getInstance().getValue(ZkParamCfg.MYCAT_SERVER_TYPE) ) ;\n\t\t//根据DataHost建立PhysicalDBPool，其实就是实际数据库连接池，每个DataHost对应一个PhysicalDBPool\n\t\tMap<String, PhysicalDBPool> nodes = new HashMap<String, PhysicalDBPool>(\n\t\t\t\tnodeConfs.size());\n\t\tfor (DataHostConfig conf : nodeConfs.values()) {\n\t\t\tif(isBooster){\n\t\t\t\tconf.setMinCon(2);\n\t\t\t}\n\t\t\t//建立PhysicalDBPool\n\t\t\tPhysicalDBPool pool = getPhysicalDBPool(conf, configLoader);\n\t\t\tnodes.put(pool.getHostName(), pool);\n\t\t}\n\t\treturn nodes;\n\t}\n\n\tprivate PhysicalDatasource[] createDataSource(DataHostConfig conf,\n\t\t\tString hostName, String dbType, String dbDriver,\n\t\t\tDBHostConfig[] nodes, boolean isRead) {\n\t\tPhysicalDatasource[] dataSources = new PhysicalDatasource[nodes.length];\n\t\tif (dbType.equals(\"mysql\") && dbDriver.equals(\"native\")) {\n\t\t\tfor (int i = 0; i < nodes.length; i++) {\n\t\t\t\t//设置最大idle时间，默认为30分钟\n\t\t\t\tnodes[i].setIdleTimeout(system.getIdleTimeout());\n\t\t\t\tMySQLDataSource ds = new MySQLDataSource(nodes[i], conf, isRead);\n\t\t\t\tdataSources[i] = ds;\n\t\t\t}\n\n\t\t} else if (dbDriver.equals(\"jdbc\")) {\n\t\t\tfor (int i = 0; i < nodes.length; i++) {\n\t\t\t\tnodes[i].setIdleTimeout(system.getIdleTimeout());\n\t\t\t\tJDBCDatasource ds = new JDBCDatasource(nodes[i], conf, isRead);\n\t\t\t\tdataSources[i] = ds;\n\t\t\t}\n\t\t} else if (\"postgresql\".equalsIgnoreCase(dbType) && dbDriver.equalsIgnoreCase(\"native\")){\n\t\t\tfor (int i = 0; i < nodes.length; i++) {\n\t\t\t\tnodes[i].setIdleTimeout(system.getIdleTimeout());\n\t\t\t\tPostgreSQLDataSource ds = new PostgreSQLDataSource(nodes[i], conf, isRead);\n\t\t\t\tdataSources[i] = ds;\n\t\t\t}\n\t\t} else{\n\t\t\tthrow new ConfigException(\"not supported yet !\" + hostName);\n\t\t}\n\t\treturn dataSources;\n\t}\n\n\tprivate PhysicalDBPool getPhysicalDBPool(DataHostConfig conf,\n\t\t\tConfigLoader configLoader) {\n\t\tString name = conf.getName();\n\t\t//数据库类型，我们这里只讨论MySQL\n\t\tString dbType = conf.getDbType();\n\t\t//连接数据库驱动，我们这里只讨论MyCat自己实现的native\n\t\tString dbDriver = conf.getDbDriver();\n\t\t//针对所有写节点创建PhysicalDatasource\n\t\tPhysicalDatasource[] writeSources = createDataSource(conf, name,\n\t\t\t\tdbType, dbDriver, conf.getWriteHosts(), false);\n\t\tMap<Integer, DBHostConfig[]> readHostsMap = conf.getReadHosts();\n\t\tMap<Integer, PhysicalDatasource[]> readSourcesMap = new HashMap<Integer, PhysicalDatasource[]>(\n\t\t\t\treadHostsMap.size());\n\t\t//对于每个读节点建立key为writeHost下标value为readHost的PhysicalDatasource[]的哈希表\n\t\tfor (Map.Entry<Integer, DBHostConfig[]> entry : readHostsMap.entrySet()) {\n\t\t\tPhysicalDatasource[] readSources = createDataSource(conf, name,\n\t\t\t\t\tdbType, dbDriver, entry.getValue(), true);\n\t\t\treadSourcesMap.put(entry.getKey(), readSources);\n\t\t}\n\t\tPhysicalDBPool pool = new PhysicalDBPool(conf.getName(), conf,\n\t\t\t\twriteSources, readSourcesMap, conf.getBalance(),\n\t\t\t\tconf.getWriteType());\n\t\tpool.setSlaveIDs(conf.getSlaveIDs());\n\t\treturn pool;\n\t}\n\n\tprivate Map<String, PhysicalDBNode> initDataNodes(ConfigLoader configLoader) {\n\t\tMap<String, DataNodeConfig> nodeConfs = configLoader.getDataNodes();\n\t\tMap<String, PhysicalDBNode> nodes = new HashMap<String, PhysicalDBNode>(\n\t\t\t\tnodeConfs.size());\n\t\tfor (DataNodeConfig conf : nodeConfs.values()) {\n\t\t\tPhysicalDBPool pool = this.dataHosts.get(conf.getDataHost());\n\t\t\tif (pool == null) {\n\t\t\t\tthrow new ConfigException(\"dataHost not exists \"\n\t\t\t\t\t\t+ conf.getDataHost());\n\n\t\t\t}\n\t\t\tPhysicalDBNode dataNode = new PhysicalDBNode(conf.getName(),\n\t\t\t\t\tconf.getDatabase(), pool);\n\t\t\tnodes.put(dataNode.getName(), dataNode);\n\t\t}\n\t\treturn nodes;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/ErrorCode.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config;\n\n/**\n * @author mycat\n */\npublic interface ErrorCode {\n\n\t// mycat error code\n    public static final int ERR_BAD_LOGICDB = 3000;\n    public static final int ERR_OPEN_SOCKET = 3001;\n    public static final int ERR_CONNECT_SOCKET = 3002;\n    public static final int ERR_REGISTER = 3004;\n    public static final int ERR_READ = 3005;\n    public static final int ERR_PUT_WRITE_QUEUE = 3006;\n    public static final int ERR_WRITE_BY_EVENT = 3007;\n    public static final int ERR_WRITE_BY_QUEUE = 3008;\n    public static final int ERR_HANDLE_DATA = 3009;\n    public static final int ERR_NOT_SUPPORTED = 3010;\n    public static final int ERR_MULTI_NODE_FAILED = 3011;\n    public static final int ERR_WRONG_USED = 3012;\n    public static final int ERR_FOUND_EXCEPTION = 3344;\n\t// mysql error code\n    public static final int ER_HASHCHK = 1000;\n    public static final int ER_NISAMCHK = 1001;\n    public static final int ER_NO = 1002;\n    public static final int ER_YES = 1003;\n    public static final int ER_CANT_CREATE_FILE = 1004;\n    public static final int ER_CANT_CREATE_TABLE = 1005;\n    public static final int ER_CANT_CREATE_DB = 1006;\n    public static final int ER_DB_CREATE_EXISTS = 1007;\n    public static final int ER_DB_DROP_EXISTS = 1008;\n    public static final int ER_DB_DROP_DELETE = 1009;\n    public static final int ER_DB_DROP_RMDIR = 1010;\n    public static final int ER_CANT_DELETE_FILE = 1011;\n    public static final int ER_CANT_FIND_SYSTEM_REC = 1012;\n    public static final int ER_CANT_GET_STAT = 1013;\n    public static final int ER_CANT_GET_WD = 1014;\n    public static final int ER_CANT_LOCK = 1015;\n    public static final int ER_CANT_OPEN_FILE = 1016;\n    public static final int ER_FILE_NOT_FOUND = 1017;\n    public static final int ER_CANT_READ_DIR = 1018;\n    public static final int ER_CANT_SET_WD = 1019;\n    public static final int ER_CHECKREAD = 1020;\n    public static final int ER_DISK_FULL = 1021;\n    public static final int ER_DUP_KEY = 1022;\n    public static final int ER_ERROR_ON_CLOSE = 1023;\n    public static final int ER_ERROR_ON_READ = 1024;\n    public static final int ER_ERROR_ON_RENAME = 1025;\n    public static final int ER_ERROR_ON_WRITE = 1026;\n    public static final int ER_FILE_USED = 1027;\n    public static final int ER_FILSORT_ABORT = 1028;\n    public static final int ER_FORM_NOT_FOUND = 1029;\n    public static final int ER_GET_ERRNO = 1030;\n    public static final int ER_ILLEGAL_HA = 1031;\n    public static final int ER_KEY_NOT_FOUND = 1032;\n    public static final int ER_NOT_FORM_FILE = 1033;\n    public static final int ER_NOT_KEYFILE = 1034;\n    public static final int ER_OLD_KEYFILE = 1035;\n    public static final int ER_OPEN_AS_READONLY = 1036;\n    public static final int ER_OUTOFMEMORY = 1037;\n    public static final int ER_OUT_OF_SORTMEMORY = 1038;\n    public static final int ER_UNEXPECTED_EOF = 1039;\n    public static final int ER_CON_COUNT_ERROR = 1040;\n    public static final int ER_OUT_OF_RESOURCES = 1041;\n    public static final int ER_BAD_HOST_ERROR = 1042;\n    public static final int ER_HANDSHAKE_ERROR = 1043;\n    public static final int ER_DBACCESS_DENIED_ERROR = 1044;\n    public static final int ER_ACCESS_DENIED_ERROR = 1045;\n    public static final int ER_NO_DB_ERROR = 1046;\n    public static final int ER_UNKNOWN_COM_ERROR = 1047;\n    public static final int ER_BAD_NULL_ERROR = 1048;\n    public static final int ER_BAD_DB_ERROR = 1049;\n    public static final int ER_TABLE_EXISTS_ERROR = 1050;\n    public static final int ER_BAD_TABLE_ERROR = 1051;\n    public static final int ER_NON_UNIQ_ERROR = 1052;\n\tpublic static final int ER_SERVER_SHUTDOWN = 1053;\n\tpublic static final int ER_BAD_FIELD_ERROR = 1054;\n\tpublic static final int ER_WRONG_FIELD_WITH_GROUP = 1055;\n\tpublic static final int ER_WRONG_GROUP_FIELD = 1056;\n\tpublic static final int ER_WRONG_SUM_SELECT = 1057;\n\tpublic static final int ER_WRONG_VALUE_COUNT = 1058;\n\tpublic static final int ER_TOO_LONG_IDENT = 1059;\n\tpublic static final int ER_DUP_FIELDNAME = 1060;\n\tpublic static final int ER_DUP_KEYNAME = 1061;\n\tpublic static final int ER_DUP_ENTRY = 1062;\n\tpublic static final int ER_WRONG_FIELD_SPEC = 1063;\n\tpublic static final int ER_PARSE_ERROR = 1064;\n\tpublic static final int ER_EMPTY_QUERY = 1065;\n\tpublic static final int ER_NONUNIQ_TABLE = 1066;\n\tpublic static final int ER_INVALID_DEFAULT = 1067;\n\tpublic static final int ER_MULTIPLE_PRI_KEY = 1068;\n\tpublic static final int ER_TOO_MANY_KEYS = 1069;\n\tpublic static final int ER_TOO_MANY_KEY_PARTS = 1070;\n\tpublic static final int ER_TOO_LONG_KEY = 1071;\n\tpublic static final int ER_KEY_COLUMN_DOES_NOT_EXITS = 1072;\n\tpublic static final int ER_BLOB_USED_AS_KEY = 1073;\n\tpublic static final int ER_TOO_BIG_FIELDLENGTH = 1074;\n\tpublic static final int ER_WRONG_AUTO_KEY = 1075;\n\tpublic static final int ER_READY = 1076;\n\tpublic static final int ER_NORMAL_SHUTDOWN = 1077;\n\tpublic static final int ER_GOT_SIGNAL = 1078;\n\tpublic static final int ER_SHUTDOWN_COMPLETE = 1079;\n\tpublic static final int ER_FORCING_CLOSE = 1080;\n\tpublic static final int ER_IPSOCK_ERROR = 1081;\n\tpublic static final int ER_NO_SUCH_INDEX = 1082;\n\tpublic static final int ER_WRONG_FIELD_TERMINATORS = 1083;\n\tpublic static final int ER_BLOBS_AND_NO_TERMINATED = 1084;\n\tpublic static final int ER_TEXTFILE_NOT_READABLE = 1085;\n\tpublic static final int ER_FILE_EXISTS_ERROR = 1086;\n\tpublic static final int ER_LOAD_INFO = 1087;\n\tpublic static final int ER_ALTER_INFO = 1088;\n\tpublic static final int ER_WRONG_SUB_KEY = 1089;\n\tpublic static final int ER_CANT_REMOVE_ALL_FIELDS = 1090;\n\tpublic static final int ER_CANT_DROP_FIELD_OR_KEY = 1091;\n\tpublic static final int ER_INSERT_INFO = 1092;\n\tpublic static final int ER_UPDATE_TABLE_USED = 1093;\n\tpublic static final int ER_NO_SUCH_THREAD = 1094;\n\tpublic static final int ER_KILL_DENIED_ERROR = 1095;\n\tpublic static final int ER_NO_TABLES_USED = 1096;\n\tpublic static final int ER_TOO_BIG_SET = 1097;\n\tpublic static final int ER_NO_UNIQUE_LOGFILE = 1098;\n\tpublic static final int ER_TABLE_NOT_LOCKED_FOR_WRITE = 1099;\n\tpublic static final int ER_TABLE_NOT_LOCKED = 1100;\n\tpublic static final int ER_BLOB_CANT_HAVE_DEFAULT = 1101;\n\tpublic static final int ER_WRONG_DB_NAME = 1102;\n\tpublic static final int ER_WRONG_TABLE_NAME = 1103;\n\tpublic static final int ER_TOO_BIG_SELECT = 1104;\n\tpublic static final int ER_UNKNOWN_ERROR = 1105;\n\tpublic static final int ER_UNKNOWN_PROCEDURE = 1106;\n\tpublic static final int ER_WRONG_PARAMCOUNT_TO_PROCEDURE = 1107;\n\tpublic static final int ER_WRONG_PARAMETERS_TO_PROCEDURE = 1108;\n\tpublic static final int ER_UNKNOWN_TABLE = 1109;\n\tpublic static final int ER_FIELD_SPECIFIED_TWICE = 1110;\n\tpublic static final int ER_INVALID_GROUP_FUNC_USE = 1111;\n\tpublic static final int ER_UNSUPPORTED_EXTENSION = 1112;\n\tpublic static final int ER_TABLE_MUST_HAVE_COLUMNS = 1113;\n\tpublic static final int ER_RECORD_FILE_FULL = 1114;\n\tpublic static final int ER_UNKNOWN_CHARACTER_SET = 1115;\n\tpublic static final int ER_TOO_MANY_TABLES = 1116;\n\tpublic static final int ER_TOO_MANY_FIELDS = 1117;\n\tpublic static final int ER_TOO_BIG_ROWSIZE = 1118;\n\tpublic static final int ER_STACK_OVERRUN = 1119;\n\tpublic static final int ER_WRONG_OUTER_JOIN = 1120;\n\tpublic static final int ER_NULL_COLUMN_IN_INDEX = 1121;\n\tpublic static final int ER_CANT_FIND_UDF = 1122;\n\tpublic static final int ER_CANT_INITIALIZE_UDF = 1123;\n\tpublic static final int ER_UDF_NO_PATHS = 1124;\n\tpublic static final int ER_UDF_EXISTS = 1125;\n\tpublic static final int ER_CANT_OPEN_LIBRARY = 1126;\n\tpublic static final int ER_CANT_FIND_DL_ENTRY = 1127;\n\tpublic static final int ER_FUNCTION_NOT_DEFINED = 1128;\n\tpublic static final int ER_HOST_IS_BLOCKED = 1129;\n\tpublic static final int ER_HOST_NOT_PRIVILEGED = 1130;\n\tpublic static final int ER_PASSWORD_ANONYMOUS_USER = 1131;\n\tpublic static final int ER_PASSWORD_NOT_ALLOWED = 1132;\n\tpublic static final int ER_PASSWORD_NO_MATCH = 1133;\n\tpublic static final int ER_UPDATE_INFO = 1134;\n\tpublic static final int ER_CANT_CREATE_THREAD = 1135;\n\tpublic static final int ER_WRONG_VALUE_COUNT_ON_ROW = 1136;\n\tpublic static final int ER_CANT_REOPEN_TABLE = 1137;\n\tpublic static final int ER_INVALID_USE_OF_NULL = 1138;\n\tpublic static final int ER_REGEXP_ERROR = 1139;\n\tpublic static final int ER_MIX_OF_GROUP_FUNC_AND_FIELDS = 1140;\n\tpublic static final int ER_NONEXISTING_GRANT = 1141;\n\tpublic static final int ER_TABLEACCESS_DENIED_ERROR = 1142;\n\tpublic static final int ER_COLUMNACCESS_DENIED_ERROR = 1143;\n\tpublic static final int ER_ILLEGAL_GRANT_FOR_TABLE = 1144;\n\tpublic static final int ER_GRANT_WRONG_HOST_OR_USER = 1145;\n\tpublic static final int ER_NO_SUCH_TABLE = 1146;\n\tpublic static final int ER_NONEXISTING_TABLE_GRANT = 1147;\n\tpublic static final int ER_NOT_ALLOWED_COMMAND = 1148;\n\tpublic static final int ER_SYNTAX_ERROR = 1149;\n\tpublic static final int ER_DELAYED_CANT_CHANGE_LOCK = 1150;\n\tpublic static final int ER_TOO_MANY_DELAYED_THREADS = 1151;\n\tpublic static final int ER_ABORTING_CONNECTION = 1152;\n\tpublic static final int ER_NET_PACKET_TOO_LARGE = 1153;\n\tpublic static final int ER_NET_READ_ERROR_FROM_PIPE = 1154;\n\tpublic static final int ER_NET_FCNTL_ERROR = 1155;\n\tpublic static final int ER_NET_PACKETS_OUT_OF_ORDER = 1156;\n\tpublic static final int ER_NET_UNCOMPRESS_ERROR = 1157;\n\tpublic static final int ER_NET_READ_ERROR = 1158;\n\tpublic static final int ER_NET_READ_INTERRUPTED = 1159;\n\tpublic static final int ER_NET_ERROR_ON_WRITE = 1160;\n\tpublic static final int ER_NET_WRITE_INTERRUPTED = 1161;\n\tpublic static final int ER_TOO_LONG_STRING = 1162;\n\tpublic static final int ER_TABLE_CANT_HANDLE_BLOB = 1163;\n\tpublic static final int ER_TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164;\n\tpublic static final int ER_DELAYED_INSERT_TABLE_LOCKED = 1165;\n\tpublic static final int ER_WRONG_COLUMN_NAME = 1166;\n\tpublic static final int ER_WRONG_KEY_COLUMN = 1167;\n\tpublic static final int ER_WRONG_MRG_TABLE = 1168;\n\tpublic static final int ER_DUP_UNIQUE = 1169;\n\tpublic static final int ER_BLOB_KEY_WITHOUT_LENGTH = 1170;\n\tpublic static final int ER_PRIMARY_CANT_HAVE_NULL = 1171;\n\tpublic static final int ER_TOO_MANY_ROWS = 1172;\n\tpublic static final int ER_REQUIRES_PRIMARY_KEY = 1173;\n\tpublic static final int ER_NO_RAID_COMPILED = 1174;\n\tpublic static final int ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175;\n\tpublic static final int ER_KEY_DOES_NOT_EXITS = 1176;\n\tpublic static final int ER_CHECK_NO_SUCH_TABLE = 1177;\n\tpublic static final int ER_CHECK_NOT_IMPLEMENTED = 1178;\n\tpublic static final int ER_CANT_DO_THIS_DURING_AN_TRANSACTION = 1179;\n\tpublic static final int ER_ERROR_DURING_COMMIT = 1180;\n\tpublic static final int ER_ERROR_DURING_ROLLBACK = 1181;\n\tpublic static final int ER_ERROR_DURING_FLUSH_LOGS = 1182;\n\tpublic static final int ER_ERROR_DURING_CHECKPOINT = 1183;\n\tpublic static final int ER_NEW_ABORTING_CONNECTION = 1184;\n\tpublic static final int ER_DUMP_NOT_IMPLEMENTED = 1185;\n\tpublic static final int ER_FLUSH_MASTER_BINLOG_CLOSED = 1186;\n\tpublic static final int ER_INDEX_REBUILD = 1187;\n\tpublic static final int ER_MASTER = 1188;\n\tpublic static final int ER_MASTER_NET_READ = 1189;\n\tpublic static final int ER_MASTER_NET_WRITE = 1190;\n\tpublic static final int ER_FT_MATCHING_KEY_NOT_FOUND = 1191;\n\tpublic static final int ER_LOCK_OR_ACTIVE_TRANSACTION = 1192;\n\tpublic static final int ER_UNKNOWN_SYSTEM_VARIABLE = 1193;\n\tpublic static final int ER_CRASHED_ON_USAGE = 1194;\n\tpublic static final int ER_CRASHED_ON_REPAIR = 1195;\n\tpublic static final int ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196;\n\tpublic static final int ER_TRANS_CACHE_FULL = 1197;\n\tpublic static final int ER_SLAVE_MUST_STOP = 1198;\n\tpublic static final int ER_SLAVE_NOT_RUNNING = 1199;\n\tpublic static final int ER_BAD_SLAVE = 1200;\n\tpublic static final int ER_MASTER_INFO = 1201;\n\tpublic static final int ER_SLAVE_THREAD = 1202;\n\tpublic static final int ER_TOO_MANY_USER_CONNECTIONS = 1203;\n\tpublic static final int ER_SET_CONSTANTS_ONLY = 1204;\n\tpublic static final int ER_LOCK_WAIT_TIMEOUT = 1205;\n\tpublic static final int ER_LOCK_TABLE_FULL = 1206;\n\tpublic static final int ER_READ_ONLY_TRANSACTION = 1207;\n\tpublic static final int ER_DROP_DB_WITH_READ_LOCK = 1208;\n\tpublic static final int ER_CREATE_DB_WITH_READ_LOCK = 1209;\n\tpublic static final int ER_WRONG_ARGUMENTS = 1210;\n\tpublic static final int ER_NO_PERMISSION_TO_CREATE_USER = 1211;\n\tpublic static final int ER_UNION_TABLES_IN_DIFFERENT_DIR = 1212;\n\tpublic static final int ER_LOCK_DEADLOCK = 1213;\n\tpublic static final int ER_TABLE_CANT_HANDLE_FT = 1214;\n\tpublic static final int ER_CANNOT_ADD_FOREIGN = 1215;\n\tpublic static final int ER_NO_REFERENCED_ROW = 1216;\n\tpublic static final int ER_ROW_IS_REFERENCED = 1217;\n\tpublic static final int ER_CONNECT_TO_MASTER = 1218;\n\tpublic static final int ER_QUERY_ON_MASTER = 1219;\n\tpublic static final int ER_ERROR_WHEN_EXECUTING_COMMAND = 1220;\n\tpublic static final int ER_WRONG_USAGE = 1221;\n\tpublic static final int ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222;\n\tpublic static final int ER_CANT_UPDATE_WITH_READLOCK = 1223;\n\tpublic static final int ER_MIXING_NOT_ALLOWED = 1224;\n\tpublic static final int ER_DUP_ARGUMENT = 1225;\n\tpublic static final int ER_USER_LIMIT_REACHED = 1226;\n\tpublic static final int ER_SPECIFIC_ACCESS_DENIED_ERROR = 1227;\n\tpublic static final int ER_LOCAL_VARIABLE = 1228;\n\tpublic static final int ER_GLOBAL_VARIABLE = 1229;\n\tpublic static final int ER_NO_DEFAULT = 1230;\n\tpublic static final int ER_WRONG_VALUE_FOR_VAR = 1231;\n\tpublic static final int ER_WRONG_TYPE_FOR_VAR = 1232;\n\tpublic static final int ER_VAR_CANT_BE_READ = 1233;\n\tpublic static final int ER_CANT_USE_OPTION_HERE = 1234;\n\tpublic static final int ER_NOT_SUPPORTED_YET = 1235;\n\tpublic static final int ER_MASTER_FATAL_ERROR_READING_BINLOG = 1236;\n\tpublic static final int ER_SLAVE_IGNORED_TABLE = 1237;\n\tpublic static final int ER_INCORRECT_GLOBAL_LOCAL_VAR = 1238;\n\tpublic static final int ER_WRONG_FK_DEF = 1239;\n\tpublic static final int ER_KEY_REF_DO_NOT_MATCH_TABLE_REF = 1240;\n\tpublic static final int ER_OPERAND_COLUMNS = 1241;\n\tpublic static final int ER_SUBQUERY_NO_1_ROW = 1242;\n\tpublic static final int ER_UNKNOWN_STMT_HANDLER = 1243;\n\tpublic static final int ER_CORRUPT_HELP_DB = 1244;\n\tpublic static final int ER_CYCLIC_REFERENCE = 1245;\n\tpublic static final int ER_AUTO_CONVERT = 1246;\n\tpublic static final int ER_ILLEGAL_REFERENCE = 1247;\n\tpublic static final int ER_DERIVED_MUST_HAVE_ALIAS = 1248;\n\tpublic static final int ER_SELECT_REDUCED = 1249;\n\tpublic static final int ER_TABLENAME_NOT_ALLOWED_HERE = 1250;\n\tpublic static final int ER_NOT_SUPPORTED_AUTH_MODE = 1251;\n\tpublic static final int ER_SPATIAL_CANT_HAVE_NULL = 1252;\n\tpublic static final int ER_COLLATION_CHARSET_MISMATCH = 1253;\n\tpublic static final int ER_SLAVE_WAS_RUNNING = 1254;\n\tpublic static final int ER_SLAVE_WAS_NOT_RUNNING = 1255;\n\tpublic static final int ER_TOO_BIG_FOR_UNCOMPRESS = 1256;\n\tpublic static final int ER_ZLIB_Z_MEM_ERROR = 1257;\n\tpublic static final int ER_ZLIB_Z_BUF_ERROR = 1258;\n\tpublic static final int ER_ZLIB_Z_DATA_ERROR = 1259;\n\tpublic static final int ER_CUT_VALUE_GROUP_CONCAT = 1260;\n\tpublic static final int ER_WARN_TOO_FEW_RECORDS = 1261;\n\tpublic static final int ER_WARN_TOO_MANY_RECORDS = 1262;\n\tpublic static final int ER_WARN_NULL_TO_NOTNULL = 1263;\n\tpublic static final int ER_WARN_DATA_OUT_OF_RANGE = 1264;\n\tpublic static final int WARN_DATA_TRUNCATED = 1265;\n\tpublic static final int ER_WARN_USING_OTHER_HANDLER = 1266;\n\tpublic static final int ER_CANT_AGGREGATE_2COLLATIONS = 1267;\n\tpublic static final int ER_DROP_USER = 1268;\n\tpublic static final int ER_REVOKE_GRANTS = 1269;\n\tpublic static final int ER_CANT_AGGREGATE_3COLLATIONS = 1270;\n\tpublic static final int ER_CANT_AGGREGATE_NCOLLATIONS = 1271;\n\tpublic static final int ER_VARIABLE_IS_NOT_STRUCT = 1272;\n\tpublic static final int ER_UNKNOWN_COLLATION = 1273;\n\tpublic static final int ER_SLAVE_IGNORED_SSL_PARAMS = 1274;\n\tpublic static final int ER_SERVER_IS_IN_SECURE_AUTH_MODE = 1275;\n\tpublic static final int ER_WARN_FIELD_RESOLVED = 1276;\n\tpublic static final int ER_BAD_SLAVE_UNTIL_COND = 1277;\n\tpublic static final int ER_MISSING_SKIP_SLAVE = 1278;\n\tpublic static final int ER_UNTIL_COND_IGNORED = 1279;\n\tpublic static final int ER_WRONG_NAME_FOR_INDEX = 1280;\n\tpublic static final int ER_WRONG_NAME_FOR_CATALOG = 1281;\n\tpublic static final int ER_WARN_QC_RESIZE = 1282;\n\tpublic static final int ER_BAD_FT_COLUMN = 1283;\n\tpublic static final int ER_UNKNOWN_KEY_CACHE = 1284;\n\tpublic static final int ER_WARN_HOSTNAME_WONT_WORK = 1285;\n\tpublic static final int ER_UNKNOWN_STORAGE_ENGINE = 1286;\n\tpublic static final int ER_WARN_DEPRECATED_SYNTAX = 1287;\n\tpublic static final int ER_NON_UPDATABLE_TABLE = 1288;\n\tpublic static final int ER_FEATURE_DISABLED = 1289;\n\tpublic static final int ER_OPTION_PREVENTS_STATEMENT = 1290;\n\tpublic static final int ER_DUPLICATED_VALUE_IN_TYPE = 1291;\n\tpublic static final int ER_TRUNCATED_WRONG_VALUE = 1292;\n\tpublic static final int ER_TOO_MUCH_AUTO_TIMESTAMP_COLS = 1293;\n\tpublic static final int ER_INVALID_ON_UPDATE = 1294;\n\tpublic static final int ER_UNSUPPORTED_PS = 1295;\n\tpublic static final int ER_GET_ERRMSG = 1296;\n\tpublic static final int ER_GET_TEMPORARY_ERRMSG = 1297;\n\tpublic static final int ER_UNKNOWN_TIME_ZONE = 1298;\n\tpublic static final int ER_WARN_INVALID_TIMESTAMP = 1299;\n\tpublic static final int ER_INVALID_CHARACTER_STRING = 1300;\n\tpublic static final int ER_WARN_ALLOWED_PACKET_OVERFLOWED = 1301;\n\tpublic static final int ER_CONFLICTING_DECLARATIONS = 1302;\n\tpublic static final int ER_SP_NO_RECURSIVE_CREATE = 1303;\n\tpublic static final int ER_SP_ALREADY_EXISTS = 1304;\n\tpublic static final int ER_SP_DOES_NOT_EXIST = 1305;\n\tpublic static final int ER_SP_DROP_FAILED = 1306;\n\tpublic static final int ER_SP_STORE_FAILED = 1307;\n\tpublic static final int ER_SP_LILABEL_MISMATCH = 1308;\n\tpublic static final int ER_SP_LABEL_REDEFINE = 1309;\n\tpublic static final int ER_SP_LABEL_MISMATCH = 1310;\n\tpublic static final int ER_SP_UNINIT_VAR = 1311;\n\tpublic static final int ER_SP_BADSELECT = 1312;\n\tpublic static final int ER_SP_BADRETURN = 1313;\n\tpublic static final int ER_SP_BADSTATEMENT = 1314;\n\tpublic static final int ER_UPDATE_LOG_DEPRECATED_IGNORED = 1315;\n\tpublic static final int ER_UPDATE_LOG_DEPRECATED_TRANSLATED = 1316;\n\tpublic static final int ER_QUERY_INTERRUPTED = 1317;\n\tpublic static final int ER_SP_WRONG_NO_OF_ARGS = 1318;\n\tpublic static final int ER_SP_COND_MISMATCH = 1319;\n\tpublic static final int ER_SP_NORETURN = 1320;\n\tpublic static final int ER_SP_NORETURNEND = 1321;\n\tpublic static final int ER_SP_BAD_CURSOR_QUERY = 1322;\n\tpublic static final int ER_SP_BAD_CURSOR_SELECT = 1323;\n\tpublic static final int ER_SP_CURSOR_MISMATCH = 1324;\n\tpublic static final int ER_SP_CURSOR_ALREADY_OPEN = 1325;\n\tpublic static final int ER_SP_CURSOR_NOT_OPEN = 1326;\n\tpublic static final int ER_SP_UNDECLARED_VAR = 1327;\n\tpublic static final int ER_SP_WRONG_NO_OF_FETCH_ARGS = 1328;\n\tpublic static final int ER_SP_FETCH_NO_DATA = 1329;\n\tpublic static final int ER_SP_DUP_PARAM = 1330;\n\tpublic static final int ER_SP_DUP_VAR = 1331;\n\tpublic static final int ER_SP_DUP_COND = 1332;\n\tpublic static final int ER_SP_DUP_CURS = 1333;\n\tpublic static final int ER_SP_CANT_ALTER = 1334;\n\tpublic static final int ER_SP_SUBSELECT_NYI = 1335;\n\tpublic static final int ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG = 1336;\n\tpublic static final int ER_SP_VARCOND_AFTER_CURSHNDLR = 1337;\n\tpublic static final int ER_SP_CURSOR_AFTER_HANDLER = 1338;\n\tpublic static final int ER_SP_CASE_NOT_FOUND = 1339;\n\tpublic static final int ER_FPARSER_TOO_BIG_FILE = 1340;\n\tpublic static final int ER_FPARSER_BAD_HEADER = 1341;\n\tpublic static final int ER_FPARSER_EOF_IN_COMMENT = 1342;\n\tpublic static final int ER_FPARSER_ERROR_IN_PARAMETER = 1343;\n\tpublic static final int ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344;\n\tpublic static final int ER_VIEW_NO_EXPLAIN = 1345;\n\tpublic static final int ER_FRM_UNKNOWN_TYPE = 1346;\n\tpublic static final int ER_WRONG_OBJECT = 1347;\n\tpublic static final int ER_NONUPDATEABLE_COLUMN = 1348;\n\tpublic static final int ER_VIEW_SELECT_DERIVED = 1349;\n\tpublic static final int ER_VIEW_SELECT_CLAUSE = 1350;\n\tpublic static final int ER_VIEW_SELECT_VARIABLE = 1351;\n\tpublic static final int ER_VIEW_SELECT_TMPTABLE = 1352;\n\tpublic static final int ER_VIEW_WRONG_LIST = 1353;\n\tpublic static final int ER_WARN_VIEW_MERGE = 1354;\n\tpublic static final int ER_WARN_VIEW_WITHOUT_KEY = 1355;\n\tpublic static final int ER_VIEW_INVALID = 1356;\n\tpublic static final int ER_SP_NO_DROP_SP = 1357;\n\tpublic static final int ER_SP_GOTO_IN_HNDLR = 1358;\n\tpublic static final int ER_TRG_ALREADY_EXISTS = 1359;\n\tpublic static final int ER_TRG_DOES_NOT_EXIST = 1360;\n\tpublic static final int ER_TRG_ON_VIEW_OR_TEMP_TABLE = 1361;\n\tpublic static final int ER_TRG_CANT_CHANGE_ROW = 1362;\n\tpublic static final int ER_TRG_NO_SUCH_ROW_IN_TRG = 1363;\n\tpublic static final int ER_NO_DEFAULT_FOR_FIELD = 1364;\n\tpublic static final int ER_DIVISION_BY_ZERO = 1365;\n\tpublic static final int ER_TRUNCATED_WRONG_VALUE_FOR_FIELD = 1366;\n\tpublic static final int ER_ILLEGAL_VALUE_FOR_TYPE = 1367;\n\tpublic static final int ER_VIEW_NONUPD_CHECK = 1368;\n\tpublic static final int ER_VIEW_CHECK_FAILED = 1369;\n\tpublic static final int ER_PROCACCESS_DENIED_ERROR = 1370;\n\tpublic static final int ER_RELAY_LOG_FAIL = 1371;\n\tpublic static final int ER_PASSWD_LENGTH = 1372;\n\tpublic static final int ER_UNKNOWN_TARGET_BINLOG = 1373;\n\tpublic static final int ER_IO_ERR_LOG_INDEX_READ = 1374;\n\tpublic static final int ER_BINLOG_PURGE_PROHIBITED = 1375;\n\tpublic static final int ER_FSEEK_FAIL = 1376;\n\tpublic static final int ER_BINLOG_PURGE_FATAL_ERR = 1377;\n\tpublic static final int ER_LOG_IN_USE = 1378;\n\tpublic static final int ER_LOG_PURGE_UNKNOWN_ERR = 1379;\n\tpublic static final int ER_RELAY_LOG_INIT = 1380;\n\tpublic static final int ER_NO_BINARY_LOGGING = 1381;\n\tpublic static final int ER_RESERVED_SYNTAX = 1382;\n\tpublic static final int ER_WSAS_FAILED = 1383;\n\tpublic static final int ER_DIFF_GROUPS_PROC = 1384;\n\tpublic static final int ER_NO_GROUP_FOR_PROC = 1385;\n\tpublic static final int ER_ORDER_WITH_PROC = 1386;\n\tpublic static final int ER_LOGGING_PROHIBIT_CHANGING_OF = 1387;\n\tpublic static final int ER_NO_FILE_MAPPING = 1388;\n\tpublic static final int ER_WRONG_MAGIC = 1389;\n\tpublic static final int ER_PS_MANY_PARAM = 1390;\n\tpublic static final int ER_KEY_PART_0 = 1391;\n\tpublic static final int ER_VIEW_CHECKSUM = 1392;\n\tpublic static final int ER_VIEW_MULTIUPDATE = 1393;\n\tpublic static final int ER_VIEW_NO_INSERT_FIELD_LIST = 1394;\n\tpublic static final int ER_VIEW_DELETE_MERGE_VIEW = 1395;\n\tpublic static final int ER_CANNOT_USER = 1396;\n\tpublic static final int ER_XAER_NOTA = 1397;\n\tpublic static final int ER_XAER_INVAL = 1398;\n\tpublic static final int ER_XAER_RMFAIL = 1399;\n\tpublic static final int ER_XAER_OUTSIDE = 1400;\n\tpublic static final int ER_XAER_RMERR = 1401;\n\tpublic static final int ER_XA_RBROLLBACK = 1402;\n\tpublic static final int ER_NONEXISTING_PROC_GRANT = 1403;\n\tpublic static final int ER_PROC_AUTO_GRANT_FAIL = 1404;\n\tpublic static final int ER_PROC_AUTO_REVOKE_FAIL = 1405;\n\tpublic static final int ER_DATA_TOO_LONG = 1406;\n\tpublic static final int ER_SP_BAD_SQLSTATE = 1407;\n\tpublic static final int ER_STARTUP = 1408;\n\tpublic static final int ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR = 1409;\n\tpublic static final int ER_CANT_CREATE_USER_WITH_GRANT = 1410;\n\tpublic static final int ER_WRONG_VALUE_FOR_TYPE = 1411;\n\tpublic static final int ER_TABLE_DEF_CHANGED = 1412;\n\tpublic static final int ER_SP_DUP_HANDLER = 1413;\n\tpublic static final int ER_SP_NOT_VAR_ARG = 1414;\n\tpublic static final int ER_SP_NO_RETSET = 1415;\n\tpublic static final int ER_CANT_CREATE_GEOMETRY_OBJECT = 1416;\n\tpublic static final int ER_FAILED_ROUTINE_BREAK_BINLOG = 1417;\n\tpublic static final int ER_BINLOG_UNSAFE_ROUTINE = 1418;\n\tpublic static final int ER_BINLOG_CREATE_ROUTINE_NEED_SUPER = 1419;\n\tpublic static final int ER_EXEC_STMT_WITH_OPEN_CURSOR = 1420;\n\tpublic static final int ER_STMT_HAS_NO_OPEN_CURSOR = 1421;\n\tpublic static final int ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG = 1422;\n\tpublic static final int ER_NO_DEFAULT_FOR_VIEW_FIELD = 1423;\n\tpublic static final int ER_SP_NO_RECURSION = 1424;\n\tpublic static final int ER_TOO_BIG_SCALE = 1425;\n\tpublic static final int ER_TOO_BIG_PRECISION = 1426;\n\tpublic static final int ER_M_BIGGER_THAN_D = 1427;\n\tpublic static final int ER_WRONG_LOCK_OF_SYSTEM_TABLE = 1428;\n\tpublic static final int ER_CONNECT_TO_FOREIGN_DATA_SOURCE = 1429;\n\tpublic static final int ER_QUERY_ON_FOREIGN_DATA_SOURCE = 1430;\n\tpublic static final int ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST = 1431;\n\tpublic static final int ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE = 1432;\n\tpublic static final int ER_FOREIGN_DATA_STRING_INVALID = 1433;\n\tpublic static final int ER_CANT_CREATE_FEDERATED_TABLE = 1434;\n\tpublic static final int ER_TRG_IN_WRONG_SCHEMA = 1435;\n\tpublic static final int ER_STACK_OVERRUN_NEED_MORE = 1436;\n\tpublic static final int ER_TOO_LONG_BODY = 1437;\n\tpublic static final int ER_WARN_CANT_DROP_DEFAULT_KEYCACHE = 1438;\n\tpublic static final int ER_TOO_BIG_DISPLAYWIDTH = 1439;\n\tpublic static final int ER_XAER_DUPID = 1440;\n\tpublic static final int ER_DATETIME_FUNCTION_OVERFLOW = 1441;\n\tpublic static final int ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG = 1442;\n\tpublic static final int ER_VIEW_PREVENT_UPDATE = 1443;\n\tpublic static final int ER_PS_NO_RECURSION = 1444;\n\tpublic static final int ER_SP_CANT_SET_AUTOCOMMIT = 1445;\n\tpublic static final int ER_NO_VIEW_USER = 1446;\n\tpublic static final int ER_VIEW_FRM_NO_USER = 1447;\n\tpublic static final int ER_VIEW_OTHER_USER = 1448;\n\tpublic static final int ER_NO_SUCH_USER = 1449;\n\tpublic static final int ER_FORBID_SCHEMA_CHANGE = 1450;\n\tpublic static final int ER_ROW_IS_REFERENCED_2 = 1451;\n\tpublic static final int ER_NO_REFERENCED_ROW_2 = 1452;\n\tpublic static final int ER_SP_BAD_VAR_SHADOW = 1453;\n\tpublic static final int ER_PARTITION_REQUIRES_VALUES_ERROR = 1454;\n\tpublic static final int ER_PARTITION_WRONG_VALUES_ERROR = 1455;\n\tpublic static final int ER_PARTITION_MAXVALUE_ERROR = 1456;\n\tpublic static final int ER_PARTITION_SUBPARTITION_ERROR = 1457;\n\tpublic static final int ER_PARTITION_WRONG_NO_PART_ERROR = 1458;\n\tpublic static final int ER_PARTITION_WRONG_NO_SUBPART_ERROR = 1459;\n\tpublic static final int ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR = 1460;\n\tpublic static final int ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR = 1461;\n\tpublic static final int ER_FIELD_NOT_FOUND_PART_ERROR = 1462;\n\tpublic static final int ER_LIST_OF_FIELDS_ONLY_IN_HASH_ERROR = 1463;\n\tpublic static final int ER_INCONSISTENT_PARTITION_INFO_ERROR = 1464;\n\tpublic static final int ER_PARTITION_FUNC_NOT_ALLOWED_ERROR = 1465;\n\tpublic static final int ER_PARTITIONS_MUST_BE_DEFINED_ERROR = 1466;\n\tpublic static final int ER_RANGE_NOT_INCREASING_ERROR = 1467;\n\tpublic static final int ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR = 1468;\n\tpublic static final int ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR = 1469;\n\tpublic static final int ER_PARTITION_ENTRY_ERROR = 1470;\n\tpublic static final int ER_MIX_HANDLER_ERROR = 1471;\n\tpublic static final int ER_PARTITION_NOT_DEFINED_ERROR = 1472;\n\tpublic static final int ER_TOO_MANY_PARTITIONS_ERROR = 1473;\n\tpublic static final int ER_SUBPARTITION_ERROR = 1474;\n\tpublic static final int ER_CANT_CREATE_HANDLER_FILE = 1475;\n\tpublic static final int ER_BLOB_FIELD_IN_PART_FUNC_ERROR = 1476;\n\tpublic static final int ER_CHAR_SET_IN_PART_FIELD_ERROR = 1477;\n\tpublic static final int ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF = 1478;\n\tpublic static final int ER_NO_PARTS_ERROR = 1479;\n\tpublic static final int ER_PARTITION_MGMT_ON_NONPARTITIONED = 1480;\n\tpublic static final int ER_DROP_PARTITION_NON_EXISTENT = 1481;\n\tpublic static final int ER_DROP_LAST_PARTITION = 1482;\n\tpublic static final int ER_COALESCE_ONLY_ON_HASH_PARTITION = 1483;\n\tpublic static final int ER_ONLY_ON_RANGE_LIST_PARTITION = 1484;\n\tpublic static final int ER_ADD_PARTITION_SUBPART_ERROR = 1485;\n\tpublic static final int ER_ADD_PARTITION_NO_NEW_PARTITION = 1486;\n\tpublic static final int ER_COALESCE_PARTITION_NO_PARTITION = 1487;\n\tpublic static final int ER_REORG_PARTITION_NOT_EXIST = 1488;\n\tpublic static final int ER_SAME_NAME_PARTITION = 1489;\n\tpublic static final int ER_CONSECUTIVE_REORG_PARTITIONS = 1490;\n\tpublic static final int ER_REORG_OUTSIDE_RANGE = 1491;\n\tpublic static final int ER_DROP_PARTITION_FAILURE = 1492;\n\tpublic static final int ER_DROP_PARTITION_WHEN_FK_DEFINED = 1493;\n\tpublic static final int ER_PLUGIN_IS_NOT_LOADED = 1494;\n\tpublic static final int ER_USER_READ_ONLY = 1495;\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/Fields.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config;\n\n/**\n * 字段类型及标识定义\n * \n * @author mycat\n */\npublic interface Fields {\n\n    /** field data type */\n    public static final int FIELD_TYPE_DECIMAL = 0;\n    public static final int FIELD_TYPE_TINY = 1;\n    public static final int FIELD_TYPE_SHORT = 2;\n    public static final int FIELD_TYPE_LONG = 3;\n    public static final int FIELD_TYPE_FLOAT = 4;\n    public static final int FIELD_TYPE_DOUBLE = 5;\n    public static final int FIELD_TYPE_NULL = 6;\n    public static final int FIELD_TYPE_TIMESTAMP = 7;\n    public static final int FIELD_TYPE_LONGLONG = 8;\n    public static final int FIELD_TYPE_INT24 = 9;\n    public static final int FIELD_TYPE_DATE = 10;\n    public static final int FIELD_TYPE_TIME = 11;\n    public static final int FIELD_TYPE_DATETIME = 12;\n    public static final int FIELD_TYPE_YEAR = 13;\n    public static final int FIELD_TYPE_NEWDATE = 14;\n    public static final int FIELD_TYPE_VARCHAR = 15;\n    public static final int FIELD_TYPE_BIT = 16;\n    public static final int FIELD_TYPE_NEW_DECIMAL = 246;\n    public static final int FIELD_TYPE_ENUM = 247;\n    public static final int FIELD_TYPE_SET = 248;\n    public static final int FIELD_TYPE_TINY_BLOB = 249;\n    public static final int FIELD_TYPE_MEDIUM_BLOB = 250;\n    public static final int FIELD_TYPE_LONG_BLOB = 251;\n    public static final int FIELD_TYPE_BLOB = 252;\n    public static final int FIELD_TYPE_VAR_STRING = 253;\n    public static final int FIELD_TYPE_STRING = 254;\n    public static final int FIELD_TYPE_GEOMETRY = 255;\n\n    /** field flag */\n    public static final int NOT_NULL_FLAG = 0x0001;\n    public static final int PRI_KEY_FLAG = 0x0002;\n    public static final int UNIQUE_KEY_FLAG = 0x0004;\n    public static final int MULTIPLE_KEY_FLAG = 0x0008;\n    public static final int BLOB_FLAG = 0x0010;\n    public static final int UNSIGNED_FLAG = 0x0020;\n    public static final int ZEROFILL_FLAG = 0x0040;\n    public static final int BINARY_FLAG = 0x0080;\n    public static final int ENUM_FLAG = 0x0100;\n    public static final int AUTO_INCREMENT_FLAG = 0x0200;\n    public static final int TIMESTAMP_FLAG = 0x0400;\n    public static final int SET_FLAG = 0x0800;\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/Isolations.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config;\n\n/**\n * 事务隔离级别定义\n * \n * @author mycat\n */\npublic interface Isolations {\n\n    public static final int READ_UNCOMMITTED = 1;\n    public static final int READ_COMMITTED = 2;\n    public static final int REPEATED_READ = 3;\n    public static final int SERIALIZABLE = 4;\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/MycatCluster.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.config.model.ClusterConfig;\nimport io.mycat.config.model.MycatNodeConfig;\n\n/**\n * @author mycat\n */\npublic final class MycatCluster {\n\n    private final Map<String, MycatNode> nodes;\n    private final Map<String, List<String>> groups;\n\n    public MycatCluster(ClusterConfig clusterConf) {\n        this.nodes = new HashMap<String, MycatNode>(clusterConf.getNodes().size());\n        this.groups = clusterConf.getGroups();\n        for (MycatNodeConfig conf : clusterConf.getNodes().values()) {\n            String name = conf.getName();\n            MycatNode node = new MycatNode(conf);\n            this.nodes.put(name, node);\n        }\n    }\n\n    public Map<String, MycatNode> getNodes() {\n        return nodes;\n    }\n\n    public Map<String, List<String>> getGroups() {\n        return groups;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/MycatConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config;\n\nimport java.io.IOException;\nimport java.net.StandardSocketOptions;\nimport java.nio.channels.NetworkChannel;\nimport java.util.ArrayList;\nimport java.util.Map;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.config.model.FirewallConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.UserConfig;\nimport io.mycat.net.AbstractConnection;\nimport io.mycat.util.TimeUtil;\n\n/**\n * @author mycat\n */\npublic class MycatConfig {\n\t\n\tprivate static final int RELOAD = 1;\n\tprivate static final int ROLLBACK = 2;\n    private static final int RELOAD_ALL = 3;\n\n\tprivate volatile SystemConfig system;\n\tprivate volatile MycatCluster cluster;\n\tprivate volatile MycatCluster _cluster;\n\tprivate volatile FirewallConfig firewall;\n\tprivate volatile FirewallConfig _firewall;\n\tprivate volatile Map<String, UserConfig> users;\n\tprivate volatile Map<String, UserConfig> _users;\n\tprivate volatile Map<String, SchemaConfig> schemas;\n\tprivate volatile Map<String, SchemaConfig> _schemas;\n\tprivate volatile Map<String, PhysicalDBNode> dataNodes;\n\tprivate volatile Map<String, PhysicalDBNode> _dataNodes;\n\tprivate volatile Map<String, PhysicalDBPool> dataHosts;\n\tprivate volatile Map<String, PhysicalDBPool> _dataHosts;\n\tprivate long reloadTime;\n\tprivate long rollbackTime;\n\tprivate int status;\n\tprivate final ReentrantLock lock;\n\n\tpublic MycatConfig() {\n\t\t\n\t\t//读取schema.xml，rule.xml和server.xml\n\t\tConfigInitializer confInit = new ConfigInitializer(true);\n\t\tthis.system = confInit.getSystem();\n\t\tthis.users = confInit.getUsers();\n\t\tthis.schemas = confInit.getSchemas();\n\t\tthis.dataHosts = confInit.getDataHosts();\n\n\t\tthis.dataNodes = confInit.getDataNodes();\n\t\tfor (PhysicalDBPool dbPool : dataHosts.values()) {\n\t\t\tdbPool.setSchemas(getDataNodeSchemasOfDataHost(dbPool.getHostName()));\n\t\t}\n\t\t\n\t\tthis.firewall = confInit.getFirewall();\n\t\tthis.cluster = confInit.getCluster();\n\t\t\n\t\t//初始化重加载配置时间\n\t\tthis.reloadTime = TimeUtil.currentTimeMillis();\n\t\tthis.rollbackTime = -1L;\n\t\tthis.status = RELOAD;\n\t\t\n\t\t//配置加载锁\n\t\tthis.lock = new ReentrantLock();\n\t}\n\n\tpublic SystemConfig getSystem() {\n\t\treturn system;\n\t}\n\n\tpublic void setSocketParams(AbstractConnection con, boolean isFrontChannel)\n\t\t\tthrows IOException {\n\t\t\n\t\tint sorcvbuf = 0;\n\t\tint sosndbuf = 0;\n\t\tint soNoDelay = 0;\n\t\tif ( isFrontChannel ) {\n\t\t\tsorcvbuf = system.getFrontsocketsorcvbuf();\n\t\t\tsosndbuf = system.getFrontsocketsosndbuf();\n\t\t\tsoNoDelay = system.getFrontSocketNoDelay();\n\t\t} else {\n\t\t\tsorcvbuf = system.getBacksocketsorcvbuf();\n\t\t\tsosndbuf = system.getBacksocketsosndbuf();\n\t\t\tsoNoDelay = system.getBackSocketNoDelay();\n\t\t}\n\t\t\n\t\tNetworkChannel channel = con.getChannel();\n\t\tchannel.setOption(StandardSocketOptions.SO_RCVBUF, sorcvbuf);\n\t\tchannel.setOption(StandardSocketOptions.SO_SNDBUF, sosndbuf);\n\t\tchannel.setOption(StandardSocketOptions.TCP_NODELAY, soNoDelay == 1);\n\t\tchannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);\n\t\tchannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);\n\t\t\n\t\tcon.setMaxPacketSize(system.getMaxPacketSize());\n\t\tcon.setPacketHeaderSize(system.getPacketHeaderSize());\n\t\tcon.setIdleTimeout(system.getIdleTimeout());\n\t\tcon.setCharset(system.getCharset());\n\n\t}\n\n\tpublic Map<String, UserConfig> getUsers() {\n\t\treturn users;\n\t}\n\n\tpublic Map<String, UserConfig> getBackupUsers() {\n\t\treturn _users;\n\t}\n\n\tpublic Map<String, SchemaConfig> getSchemas() {\n\t\treturn schemas;\n\t}\n\n\tpublic Map<String, SchemaConfig> getBackupSchemas() {\n\t\treturn _schemas;\n\t}\n\n\tpublic Map<String, PhysicalDBNode> getDataNodes() {\n\t\treturn dataNodes;\n\t}\n\t\n\tpublic void setDataNodes( Map<String, PhysicalDBNode> map) {\n\t\tthis.dataNodes = map;\n\t}\n\n\tpublic String[] getDataNodeSchemasOfDataHost(String dataHost) {\n\t\tArrayList<String> schemas = new ArrayList<String>(30);\n\t\tfor (PhysicalDBNode dn: dataNodes.values()) {\n\t\t\tif (dn.getDbPool().getHostName().equals(dataHost)) {\n\t\t\t\tschemas.add(dn.getDatabase());\n\t\t\t}\n\t\t}\n\t\treturn schemas.toArray(new String[schemas.size()]);\n\t}\n\n\tpublic Map<String, PhysicalDBNode> getBackupDataNodes() {\n\t\treturn _dataNodes;\n\t}\n\n\tpublic Map<String, PhysicalDBPool> getDataHosts() {\n\t\treturn dataHosts;\n\t}\n\n\tpublic Map<String, PhysicalDBPool> getBackupDataHosts() {\n\t\treturn _dataHosts;\n\t}\n\n\tpublic MycatCluster getCluster() {\n\t\treturn cluster;\n\t}\n\n\tpublic MycatCluster getBackupCluster() {\n\t\treturn _cluster;\n\t}\n\n\tpublic FirewallConfig getFirewall() {\n\t\treturn firewall;\n\t}\n\n\tpublic FirewallConfig getBackupFirewall() {\n\t\treturn _firewall;\n\t}\n\n\tpublic ReentrantLock getLock() {\n\t\treturn lock;\n\t}\n\n\tpublic long getReloadTime() {\n\t\treturn reloadTime;\n\t}\n\n\tpublic long getRollbackTime() {\n\t\treturn rollbackTime;\n\t}\n\n\tpublic void reload(\n\t\t\tMap<String, UserConfig> newUsers, \n\t\t\tMap<String, SchemaConfig> newSchemas,\n\t\t\tMap<String, PhysicalDBNode> newDataNodes, \n\t\t\tMap<String, PhysicalDBPool> newDataHosts, \n\t\t\tMycatCluster newCluster,\n\t\t\tFirewallConfig newFirewall, \n\t\t\tboolean reloadAll) {\n\t\t\n\t\tapply(newUsers, newSchemas, newDataNodes, newDataHosts, newCluster, newFirewall, reloadAll);\n\t\tthis.reloadTime = TimeUtil.currentTimeMillis();\n\t\tthis.status = reloadAll?RELOAD_ALL:RELOAD;\n\t}\n\n\tpublic boolean canRollback() {\n\t\tif (_users == null || _schemas == null || _dataNodes == null\n\t\t\t\t|| _dataHosts == null || _cluster == null\n\t\t\t\t|| _firewall == null || status == ROLLBACK) {\n\t\t\treturn false;\n\t\t} else {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tpublic void rollback(\n\t\t\tMap<String, UserConfig> users,\n\t\t\tMap<String, SchemaConfig> schemas,\n\t\t\tMap<String, PhysicalDBNode> dataNodes,\n\t\t\tMap<String, PhysicalDBPool> dataHosts, \n\t\t\tMycatCluster cluster,\n\t\t\tFirewallConfig firewall) {\n\t\t\n\t\tapply(users, schemas, dataNodes, dataHosts, cluster, firewall, status==RELOAD_ALL);\n\t\tthis.rollbackTime = TimeUtil.currentTimeMillis();\n\t\tthis.status = ROLLBACK;\n\t}\n\n\tprivate void apply(Map<String, UserConfig> newUsers,\n\t\t\tMap<String, SchemaConfig> newSchemas,\n\t\t\tMap<String, PhysicalDBNode> newDataNodes,\n\t\t\tMap<String, PhysicalDBPool> newDataHosts, \n\t\t\tMycatCluster newCluster,\n\t\t\tFirewallConfig newFirewall,\n\t\t\tboolean isLoadAll) {\n\t\t\n\t\tfinal ReentrantLock lock = this.lock;\n\t\tlock.lock();\n\t\ttry {\n\t\t\t\n\t\t\t// old 处理\n\t\t\t// 1、停止老的数据源心跳\n\t\t\t// 2、备份老的数据源配置\n\t\t\t//--------------------------------------------\n\t\t\tif (isLoadAll) {\t\t\t\t\n\t\t\t\tMap<String, PhysicalDBPool> oldDataHosts = this.dataHosts;\n\t\t\t\tif (oldDataHosts != null) {\n\t\t\t\t\tfor (PhysicalDBPool oldDbPool : oldDataHosts.values()) {\n\t\t\t\t\t\tif (oldDbPool != null) {\n\t\t\t\t\t\t\toldDbPool.stopHeartbeat();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis._dataNodes = this.dataNodes;\n\t\t\t\tthis._dataHosts = this.dataHosts;\n\t\t\t}\n\t\t\t\n\t\t\tthis._users = this.users;\n\t\t\tthis._schemas = this.schemas;\n\t\t\tthis._cluster = this.cluster;\n\t\t\tthis._firewall = this.firewall;\n\n\t\t\t// new 处理\n\t\t\t// 1、启动新的数据源心跳\n\t\t\t// 2、执行新的配置\n\t\t\t//---------------------------------------------------\n\t\t\tif (isLoadAll) {\n\t\t\t\tif (newDataHosts != null) {\n\t\t\t\t\tfor (PhysicalDBPool newDbPool : newDataHosts.values()) {\n\t\t\t\t\t\tif ( newDbPool != null) {\n\t\t\t\t\t\t\tnewDbPool.startHeartbeat();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.dataNodes = newDataNodes;\n\t\t\t\tthis.dataHosts = newDataHosts;\n\t\t\t}\t\t\t\n\t\t\tthis.users = newUsers;\n\t\t\tthis.schemas = newSchemas;\n\t\t\tthis.cluster = newCluster;\n\t\t\tthis.firewall = newFirewall;\n\t\t\t\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t}\t\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/MycatNode.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.config.model.MycatNodeConfig;\n\n/**\n * @author mycat\n */\npublic class MycatNode {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(MycatNode.class);\n\n\tprivate final String name;\n\tprivate final MycatNodeConfig config;\n\n\tpublic MycatNode(MycatNodeConfig config) {\n\t\tthis.name = config.getName();\n\t\tthis.config = config;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic MycatNodeConfig getConfig() {\n\t\treturn config;\n\t}\n\n\tpublic boolean isOnline() {\n\t\treturn (true);\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/MycatPrivileges.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.regex.Pattern;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLInsertStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLReplaceStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\nimport com.alibaba.druid.wall.WallCheckResult;\nimport com.alibaba.druid.wall.WallProvider;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.loader.xml.XMLServerLoader;\nimport io.mycat.config.model.FirewallConfig;\nimport io.mycat.config.model.UserConfig;\nimport io.mycat.config.model.UserPrivilegesConfig;\nimport io.mycat.net.handler.FrontendPrivileges;\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\nimport io.mycat.route.parser.druid.MycatStatementParser;\n\n/**\n * @author mycat\n */\npublic class MycatPrivileges implements FrontendPrivileges {\n\t/**\n\t * 无需每次建立连接都new实例。\n\t */\n\tprivate static MycatPrivileges instance = new MycatPrivileges();\n\t\n    private static final Logger ALARM = LoggerFactory.getLogger(\"alarm\");\n    \n    private static boolean check = false;\t\n\tprivate final static ThreadLocal<WallProvider> contextLocal = new ThreadLocal<WallProvider>();\n\n    public static MycatPrivileges instance() {\n    \treturn instance;\n    }\n    \n    private MycatPrivileges() {\n    \tsuper();\n    }\n    \n    @Override\n    public boolean schemaExists(String schema) {\n        MycatConfig conf = MycatServer.getInstance().getConfig();\n        return conf.getSchemas().containsKey(schema);\n    }\n\n    @Override\n    public boolean userExists(String user, String host) {\n    \t//检查用户及白名单\n    \treturn checkFirewallWhiteHostPolicy(user, host);\n    }\n\n    @Override\n    public String getPassword(String user) {\n        MycatConfig conf = MycatServer.getInstance().getConfig();\n        if (user != null && user.equals(conf.getSystem().getClusterHeartbeatUser())) {\n            return conf.getSystem().getClusterHeartbeatPass();\n        } else {\n            UserConfig uc = conf.getUsers().get(user);\n            if (uc != null) {\n                return uc.getPassword();\n            } else {\n                return null;\n            }\n        }\n    }\n\n    @Override\n    public Set<String> getUserSchemas(String user) {\n        MycatConfig conf = MycatServer.getInstance().getConfig();\n        \n        UserConfig uc = conf.getUsers().get(user);\n        if (uc != null) {\n            return uc.getSchemas();\n        } else {\n            return null;\n        }\n    \n     }\n    \n    @Override\n    public Boolean isReadOnly(String user) {\n        MycatConfig conf = MycatServer.getInstance().getConfig();\n       \n        UserConfig uc = conf.getUsers().get(user);\n        if (uc != null) {\n            return uc.isReadOnly();\n        } else {\n            return null;\n        }\n    }\n\n\t@Override\n\tpublic int getBenchmark(String user) {\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n        UserConfig uc = conf.getUsers().get(user);\n        if (uc != null) {\n            return uc.getBenchmark();\n        } else {\n            return 0;\n        }\n\t}\n\n\t/**\n\t * 防火墙白名单处理，根据防火墙配置，判断目前主机是否可以通过某用户登陆\n\t * 白名单配置请参考：\n\t * @see  XMLServerLoader\n\t * @see  FirewallConfig\n\t *\n\t * @modification 修改增加网段白名单识别配置\n\t * @date 2016/12/8\n\t * @modifiedBy Hash Zhang\n\t */\n\t@Override\n\tpublic boolean checkFirewallWhiteHostPolicy(String user, String host) {\n\t\t\n\t\tMycatConfig mycatConfig = MycatServer.getInstance().getConfig();\n        FirewallConfig firewallConfig = mycatConfig.getFirewall();\n        \n        //防火墙 白名单处理\n        boolean isPassed = false;\n        \n        Map<String, List<UserConfig>> whitehost = firewallConfig.getWhitehost();\n        Map<Pattern, List<UserConfig>> whitehostMask = firewallConfig.getWhitehostMask();\n        if ((whitehost == null || whitehost.size() == 0)&&(whitehostMask == null || whitehostMask.size() == 0)) {\n        \tMap<String, UserConfig> users = mycatConfig.getUsers();\n        \tisPassed = users.containsKey(user);\n        \t\n        } else {\n        \tList<UserConfig> list = whitehost.get(host);\n\t\t\tSet<Pattern> patterns = whitehostMask.keySet();\n\t\t\tif(patterns != null && patterns.size() > 0){\n\t\t\t\tfor(Pattern pattern : patterns) {\n\t\t\t\t\tif(pattern.matcher(host).find()){\n\t\t\t\t\t\tisPassed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (list != null) {\n\t\t\t\tfor (UserConfig userConfig : list) {\n\t\t\t\t\tif (userConfig.getName().equals(user)) {\n\t\t\t\t\t\tisPassed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}        \t\n        }\n        \n        if ( !isPassed ) {\n        \t ALARM.error(new StringBuilder().append(Alarms.FIREWALL_ATTACK).append(\"[host=\").append(host)\n                     .append(\",user=\").append(user).append(']').toString());\n        \t return false;\n        }        \n        return true;\n\t}\n\n\t\n\t/**\n\t * @see https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE-wallfilter\n\t */\n\t@Override\n\tpublic boolean checkFirewallSQLPolicy(String user, String sql) {\n\t\t\n\t\tboolean isPassed = true;\n\t\t\n\t\tif( contextLocal.get() == null ){\n\t\t\tFirewallConfig firewallConfig = MycatServer.getInstance().getConfig().getFirewall();\n\t\t\tif ( firewallConfig != null) {\n\t\t\t\tif ( firewallConfig.isCheck() ) {\n\t\t\t\t\tcontextLocal.set(firewallConfig.getProvider());\n\t\t\t\t\tcheck = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif( check ){\n\t\t\tWallCheckResult result = contextLocal.get().check(sql);\n\t\t\t\n\t\t\t// 修复 druid 防火墙在处理SHOW FULL TABLES WHERE Table_type != 'VIEW' 的时候存在的 BUG\n\t\t\t// 此代码有问题，由于Druid WallCheck 对同一条SQL语句只做一次解析，下面代码会导致第二次拦截失效\n\t\t\t// 并且 目前已经提供 ShowFullTables 来处理show full tables 命令，故对代码进行修改 \n//\t\t\tList<SQLStatement> stmts =  result.getStatementList();\n//\t\t\tif ( !stmts.isEmpty() &&  !( stmts.get(0) instanceof SQLShowTablesStatement) ) {\t\t\t\t\n//\t\t\t\tif ( !result.getViolations().isEmpty()) {\t\t\t\t\n//\t\t\t\t\tisPassed = false;\n//\t\t\t\t\tALARM.warn(\"Firewall to intercept the '\" + user + \"' unsafe SQL , errMsg:\"\n//\t\t\t\t\t\t\t+ result.getViolations().get(0).getMessage() +\n//\t\t\t\t\t\t\t\" \\r\\n \" + sql);\n//\t\t        }\t\t\t\t\n//\t\t\t}\n\t\t\t\n\t\t\tif ( !result.getViolations().isEmpty()) {\t\t\t\t\n\t\t\t\tisPassed = false;\n\t\t\t\tALARM.warn(\"Firewall to intercept the '\" + user + \"' unsafe SQL , errMsg:\"\n\t\t\t\t\t\t+ result.getViolations().get(0).getMessage() +\n\t\t\t\t\t\t\" \\r\\n \" + sql);\n\t        }\t\n\t\t\t\n\t\t\t\n\t\t}\n\t\treturn isPassed;\n\t}\n\n\t// 审计SQL权限\n\t@Override\n\tpublic boolean checkDmlPrivilege(String user, String schema, String sql) {\n\n\t\tif ( schema == null ) {\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tboolean isPassed = false;\n\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\tUserConfig userConfig = conf.getUsers().get(user);\n\t\tif (userConfig != null) {\n\t\t\t\n\t\t\tUserPrivilegesConfig userPrivilege = userConfig.getPrivilegesConfig();\n\t\t\tif ( userPrivilege != null && userPrivilege.isCheck() ) {\t\t\t\t\n\t\t\t\n\t\t\t\tUserPrivilegesConfig.SchemaPrivilege schemaPrivilege = userPrivilege.getSchemaPrivilege( schema );\n\t\t\t\tif ( schemaPrivilege != null ) {\n\t\t\n\t\t\t\t\tString tableName = null;\n\t\t\t\t\tint index = -1;\n\t\t\t\t\t\n\t\t\t\t\t//TODO 此处待优化，寻找更优SQL 解析器\n\t\t\t\t\t\n\t\t\t\t\t//修复bug\n\t\t\t\t\t// https://github.com/alibaba/druid/issues/1309\n\t\t\t\t\t//com.alibaba.druid.sql.parser.ParserException: syntax error, error in :'begin',expect END, actual EOF begin\n\t\t\t\t\tif ( sql != null && sql.length() == 5 && sql.equalsIgnoreCase(\"begin\") ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tSQLStatementParser parser = new MycatStatementParser(sql);\t\t\t\n\t\t\t\t\tSQLStatement stmt = parser.parseStatement();\n\n\t\t\t\t\tif (stmt instanceof SQLReplaceStatement || stmt instanceof SQLInsertStatement) {\n\t\t\t\t\t\tindex = 0;\n\t\t\t\t\t} else if (stmt instanceof SQLUpdateStatement ) {\n\t\t\t\t\t\tindex = 1;\n\t\t\t\t\t} else if (stmt instanceof SQLSelectStatement ) {\n\t\t\t\t\t\tindex = 2;\n\t\t\t\t\t} else if (stmt instanceof SQLDeleteStatement ) {\n\t\t\t\t\t\tindex = 3;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( index > -1) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tMycatSchemaStatVisitor schemaStatVisitor = new MycatSchemaStatVisitor();\n\t\t\t\t\t\tstmt.accept(schemaStatVisitor);\n\t\t\t\t\t\tString key = schemaStatVisitor.getCurrentTable();\n\t\t\t\t\t\tif ( key != null ) {\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (key.contains(\"`\")) {\n\t\t\t\t\t\t\t\tkey = key.replaceAll(\"`\", \"\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tint dotIndex = key.indexOf(\".\");\n\t\t\t\t\t\t\tif (dotIndex > 0) {\n\t\t\t\t\t\t\t\ttableName = key.substring(dotIndex + 1);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\ttableName = key;\n\t\t\t\t\t\t\t}\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//获取table 权限, 此处不需要检测空值, 无设置则自动继承父级权限\n\t\t\t\t\t\t\tUserPrivilegesConfig.TablePrivilege tablePrivilege = schemaPrivilege.getTablePrivilege( tableName );\n\t\t\t\t\t\t\tif ( tablePrivilege.getDml()[index] > 0 ) {\n\t\t\t\t\t\t\t\tisPassed = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t//skip\n\t\t\t\t\t\t\tisPassed = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t} else {\t\t\t\t\t\t\n\t\t\t\t\t\t//skip\n\t\t\t\t\t\tisPassed = true;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t} else {\t\t\t\t\t\n\t\t\t\t\t//skip\n\t\t\t\t\tisPassed = true;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} else {\n\t\t\t\t//skip\n\t\t\t\tisPassed = true;\n\t\t\t}\n\n\t\t} else {\n\t\t\t//skip\n\t\t\tisPassed = true;\n\t\t}\n\t\t\n\t\tif( !isPassed ) {\n\t\t\t ALARM.error(new StringBuilder().append(Alarms.DML_ATTACK ).append(\"[sql=\").append( sql )\n                     .append(\",user=\").append(user).append(']').toString());\n\t\t}\n\t\t\n\t\treturn isPassed;\n\t}\n\n\t@Override\n\tpublic boolean checkDataNodeDmlPrivilege(String user, String dataNode, String sql) {\n\t\tif (dataNode == null) {\n\t\t\treturn true;\n\t\t}\n\n\t\tboolean isPassed = false;\n\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\tUserConfig userConfig = conf.getUsers().get(user);\n\t\tif (userConfig != null) {\n\n\t\t\tUserPrivilegesConfig userPrivilege = userConfig.getPrivilegesConfig();\n\t\t\tif (userPrivilege != null && userPrivilege.isCheck()) {\n\n\t\t\t\tUserPrivilegesConfig.DataNodePrivilege dataNodePrivilege = userPrivilege.getDataNodePrivilege(dataNode);\n\t\t\t\tif (dataNodePrivilege != null) {\n\n\t\t\t\t\tif (sql != null && sql.length() == 5 && sql.equalsIgnoreCase(\"begin\")) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\t//获取 dataNode 的 select 权限, 此处不需要检测空值, 无设置则自动继承父级权限\n\t\t\t\t\tif (dataNodePrivilege.getDml()[2] > 0) {\n\t\t\t\t\t\tisPassed = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn isPassed;\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/Versions.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config;\n\n/**\n * @author mycat\n */\npublic abstract class Versions {\n\n    /**协议版本**/\n    public static final byte PROTOCOL_VERSION = 10;\n\n    /**服务器版本**/\n    public static byte[] SERVER_VERSION = \"5.6.29-mycat-1.6.7.5-release-20200428154739\".getBytes();\n\n    public static void setServerVersion(String version) {\n        byte[] mysqlVersionPart = version.getBytes();\n        int startIndex;\n        for (startIndex = 0; startIndex < SERVER_VERSION.length; startIndex++) {\n            if (SERVER_VERSION[startIndex] == '-')\n                break;\n        }\n\n        // 重新拼接mycat version字节数组\n        byte[] newMycatVersion = new byte[mysqlVersionPart.length + SERVER_VERSION.length - startIndex];\n        System.arraycopy(mysqlVersionPart, 0, newMycatVersion, 0, mysqlVersionPart.length);\n        System.arraycopy(SERVER_VERSION, startIndex, newMycatVersion, mysqlVersionPart.length,\n                SERVER_VERSION.length - startIndex);\n        SERVER_VERSION = newMycatVersion;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/Versions.template",
    "content": "/*\n * Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config;\n\n/**\n * @author mycat\n */\npublic abstract class Versions {\n\n    /**协议版本**/\n    public static final byte PROTOCOL_VERSION = 10;\n\n    /**服务器版本**/\n    public static byte[] SERVER_VERSION = \"@server-version@\".getBytes();\n\n    public static void setServerVersion(String version) {\n        byte[] mysqlVersionPart = version.getBytes();\n        int startIndex;\n        for (startIndex = 0; startIndex < SERVER_VERSION.length; startIndex++) {\n            if (SERVER_VERSION[startIndex] == '-')\n                break;\n        }\n\n        // 重新拼接mycat version字节数组\n        byte[] newMycatVersion = new byte[mysqlVersionPart.length + SERVER_VERSION.length - startIndex];\n        System.arraycopy(mysqlVersionPart, 0, newMycatVersion, 0, mysqlVersionPart.length);\n        System.arraycopy(SERVER_VERSION, startIndex, newMycatVersion, mysqlVersionPart.length,\n                SERVER_VERSION.length - startIndex);\n        SERVER_VERSION = newMycatVersion;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/classloader/DynaClassLoader.java",
    "content": "package io.mycat.config.classloader;\n\nimport java.io.BufferedInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\n/**\n * used for mycat's catlet class loader ,catlet's class file is stored in\n * Mycat_home/catlet dir\n * \n * @author wuzhih\n * \n */\npublic class DynaClassLoader {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(\"DynaClassLoader\");\n\t/** key- class full name */\n\tprivate static Map<String, DynaClass> loadedDynaClassMap = new ConcurrentHashMap<String, DynaClass>();\n\tprivate final String extClassHome;\n\tprivate final MyDynaClassLoader myClassLoader;\n\tprivate final long classCheckMilis;\n\n\tpublic DynaClassLoader(String extClassHome, int classCheckSeconds) {\n\t\tsuper();\n\t\tthis.extClassHome = extClassHome;\n\t\tclassCheckMilis = classCheckSeconds * 1000L;\n\t\tmyClassLoader = new MyDynaClassLoader();\n\t\tLOGGER.info(\"dyna class load from \" + extClassHome\n\t\t\t\t+ \",and auto check for class file modified every \"\n\t\t\t\t+ classCheckSeconds + \" seconds\");\n\t}\n\n\tpublic Object getInstanceofClass(String className) throws Exception {\n\t\tDynaClass dynaClass = loadedDynaClassMap.get(className);\n\t\tboolean needReload = (dynaClass == null || (dynaClass\n\t\t\t\t.needReloadClass(classCheckMilis) && checkChanged(dynaClass)));\n\t\tClass<?> newClass = null;\n\t\tif (needReload) {\n\t\t\tnewClass = myClassLoader.loadClass(className);\n\t\t\tdynaClass = loadedDynaClassMap.get(className);\n\t\t} else {\n\t\t\tnewClass = dynaClass.realClass;\n\t\t}\n\n\t\tif (dynaClass != null) {\n\t\t\tObject val = dynaClass.classObj;\n\t\t\tif (val == null) {\n\t\t\t\tval = dynaClass.realClass.newInstance();\n\t\t\t\tdynaClass.classObj = val;\n\n\t\t\t}\n\t\t\treturn val;\n\t\t} else {\n\t\t\treturn newClass.newInstance();\n\t\t}\n\t}\n\n\t/**\n\t * 加载某个类的字节码\n\t * \n\t * @param c\n\t * @return\n\t * @throws IOException\n\t */\n\tprivate static byte[] loadFile(String path) throws IOException {\n\t\tBufferedInputStream in = null;\n\t\ttry {\n\t\t\tin = new BufferedInputStream(new FileInputStream(path));\n\t\t\tbyte[] readed = new byte[1024 * 4];\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tint count = 0;\n\t\t\twhile ((count = in.read(readed)) != -1) {\n\t\t\t\tout.write(readed, 0, count);\n\t\t\t}\n\t\t\treturn out.toByteArray();\n\t\t} finally {\n\t\t\tif (in != null) {\n\t\t\t\tin.close();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean checkChanged(DynaClass dynaClass) throws IOException {\n\t\tboolean isChanged = false;\n\t\tFile f = new File(dynaClass.filePath);\n\t\tif (f.exists()) {\n\t\t\tlong newTime = f.lastModified();\n\t\t\tlong oldTime = dynaClass.lastModified;\n\t\t\tif (oldTime != newTime) {\n\t\t\t\t// need reload\n\t\t\t\tdynaClass.lastModified = newTime;\n\t\t\t\tdynaClass.classObj = null;\n\t\t\t\tdynaClass.realClass = null;\n\t\t\t\tisChanged = true;\n\t\t\t}\n\t\t}\n\t\treturn isChanged;\n\t}\n\n\tclass MyDynaClassLoader extends ClassLoader {\n\t\tpublic MyDynaClassLoader() {\n\t\t}\n\n\t\tpublic MyDynaClassLoader(ClassLoader parentLoader) {\n\t\t\tsuper(parentLoader);\n\t\t}\n\n\t\t/**\n\t\t * 加载某个类\n\t\t * \n\t\t * @param c\n\t\t * @return\n\t\t * @throws ClassNotFoundException\n\t\t * @throws IOException\n\t\t */\n\t\tpublic Class<?> loadClass(String name) throws ClassNotFoundException {\n\t\t\tif (name.startsWith(\"java\") || name.startsWith(\"sun\")\n\t\t\t\t\t|| name.startsWith(\"io.mycat\")) {\n\t\t\t\treturn super.loadClass(name);\n\t\t\t}\n\t\t\tDynaClass dynaClass = loadedDynaClassMap.get(name);\n\t\t\tif (dynaClass != null) {\n\t\t\t\tif (dynaClass.realClass != null) {\n\t\t\t\t\treturn dynaClass.realClass;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tdynaClass = searchFile(extClassHome, name);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOGGER.error(\"SearchFileError\", e);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (dynaClass == null) {\n\t\t\t\treturn super.loadClass(name);\n\t\t\t} else {\n\t\t\t\tLOGGER.info(\"load class from file \"+dynaClass.filePath);\n\t\t\t\tClass<?> cNew = null;\n\t\t\t\tif (dynaClass.isJar) {\n\t\t\t\t\tcNew =dynaClass.realClass;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t   byte[] content;\n\t\t\t\t   try {\n\t\t\t\t\t content = loadFile(dynaClass.filePath);\n\t\t\t\t   } catch (IOException e) {\n\t\t\t\t\t throw new ClassNotFoundException(e.toString());\n\t\t\t\t   }\n\t\t\t\t   cNew = super.defineClass(name, content, 0,content.length);\n\t\t\t\t   dynaClass.realClass = cNew;\n\t\t\t\t}\n\t\t\t\tdynaClass.classObj = null;\n\t\t\t\tloadedDynaClassMap.put(name, dynaClass);\n\t\t\t\treturn cNew;\n\t\t\t}\n\n\t\t}\n\n\t\tprivate DynaClass searchFile(String classpath, String fileName) throws Exception {\n\t\t\tDynaClass dynCls = null;\n\t\t\tString path = fileName.replace('.', File.separatorChar) + \".class\";\n\t\t\tSystem.out.println(\"class \" + classpath + \" file \" + path);\n\t\t\tFile f = new File(classpath, path);\n\t\t\tif (f.isFile()) {\n\t\t\t\tString theName = f.getPath();\n\t\t\t\tSystem.out.println(\"found \" + theName);\n\n\t\t\t\tdynCls = new DynaClass(f.getPath());\n\t\t\t\tdynCls.lastModified = f.lastModified();\n\t\t\t\treturn dynCls;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tpath = fileName.replace('.', File.separatorChar) + \".jar\";\n\t\t\t\t//classpath=\"D:\\\\code\\\\mycat\\\\Mycat-Server\\\\catlet\\\\\";\n\t\t\t\tSystem.out.println(\"jar \" + classpath + \" file \" + path);\n\t\t\t\tf = new File(classpath, path);\n\t\t\t\tif (f.isFile()) {\n\t\t\t\t  try {\n\t\t\t\t\t dynCls = new DynaClass(f.getPath());\n\t\t\t\t\t dynCls.lastModified = f.lastModified();\t\t\t\t\t \n\t\t\t\t     dynCls.realClass=JarLoader.loadJar(classpath+\"/\"+path,fileName);\t\n\t\t\t\t\t dynCls.isJar=true;\n\t\t\t\t\t return dynCls;\n\t\t\t\t  }\n\t\t\t\t  catch(Exception err) {\n\t\t\t\t\t  return null;\n\t\t\t\t  }\n\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t\n\t\t}\n\n\t}\n\n\tpublic void clearUnUsedClass() {\n\t\tlong deadTime = System.currentTimeMillis() - 30 * 60 * 1000L;\n\t\tIterator<Map.Entry<String, DynaClass>> itor = loadedDynaClassMap\n\t\t\t\t.entrySet().iterator();\n\t\twhile (itor.hasNext()) {\n\t\t\tMap.Entry<String, DynaClass> entry = itor.next();\n\t\t\tDynaClass dyCls = entry.getValue();\n\t\t\tif (dyCls.lastModified < deadTime) {\n\t\t\t\tLOGGER.info(\"clear unused catlet \" + entry.getKey());\n\t\t\t\tdyCls.clear();\n\t\t\t\titor.remove();\n\t\t\t}\n\t\t}\n\t}\n}\n\nclass DynaClass {\n\tpublic final String filePath;\n\tpublic volatile long lastModified;\n\tpublic Class<?> realClass;\n\tpublic Object classObj;\n    public boolean isJar=false;\n\tpublic boolean needReloadClass(long classCheckMilis) {\n\t\tif (lastModified + classCheckMilis < System.currentTimeMillis()) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic void clear() {\n\t\tthis.realClass = null;\n\t\tthis.classObj = null;\n\n\t}\n\n\tpublic DynaClass(String filePath) {\n\t\tsuper();\n\t\tthis.filePath = filePath;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/classloader/JarLoader.java",
    "content": "package io.mycat.config.classloader;\r\n\r\n\r\nimport java.util.jar.*;\r\nimport java.lang.reflect.*;\r\nimport java.net.URL;\r\nimport java.net.URLClassLoader;\r\nimport java.io.*;\r\nimport java.util.*;\r\n\r\npublic class JarLoader {\r\n\t  /** Unpack a jar file into a directory. */\r\n\t  public static void unJar(File jarFile, File toDir) throws IOException {\r\n\t    JarFile jar = new JarFile(jarFile);\r\n\t    try {\r\n\t      Enumeration entries = jar.entries();\r\n\t      while (entries.hasMoreElements()) {\r\n\t        JarEntry entry = (JarEntry)entries.nextElement();\r\n\t        if (!entry.isDirectory()) {\r\n\t          InputStream in = jar.getInputStream(entry);\r\n\t          try {\r\n\t            File file = new File(toDir, entry.getName());\r\n\t            if (!file.getParentFile().mkdirs() && !file.getParentFile().isDirectory()) {\r\n\r\n\t                throw new IOException(\"Mkdirs failed to create \" + \r\n\t                                      file.getParentFile().toString());\r\n\r\n\t            }\r\n\t            OutputStream out = new FileOutputStream(file);\r\n\t            try {\r\n\t              byte[] buffer = new byte[8192];\r\n\t              int i;\r\n\t              while ((i = in.read(buffer)) != -1) {\r\n\t                out.write(buffer, 0, i);\r\n\t              }\r\n\t            } finally {\r\n\t              out.close();\r\n\t            }\r\n\t          } finally {\r\n\t            in.close();\r\n\t          }\r\n\t        }\r\n\t      }\r\n\t    } finally {\r\n\t      jar.close();\r\n\t    }\r\n\t  }\r\n\t  \r\n\t  public static Class<?> loadJar(String fileName,String mainJavaclass) throws Exception {\r\n\r\n\t\t    File file = new File(fileName);\r\n\t\t    String mainClassName = null;\r\n\r\n\t\t    JarFile jarFile;\r\n\t\t    try {\r\n\t\t      jarFile = new JarFile(fileName);\r\n\t\t    } catch(IOException io) {\r\n\t\t      throw new IOException(\"Error opening jar: \" + fileName);\r\n\t\t    }\r\n\r\n\t\t    Manifest manifest = jarFile.getManifest();\r\n\t\t    if (manifest != null) {\r\n\t\t      mainClassName = manifest.getMainAttributes().getValue(\"Main-Class\");\r\n\t\t    }\r\n\t\t    jarFile.close();\r\n\r\n\t\t    if (mainClassName == null) {\r\n\t\t      mainClassName = mainJavaclass;\r\n\t\t    }\r\n\t\t    mainClassName = mainClassName.replaceAll(\"/\", \".\");\r\n\r\n\t\t    File tmpDir = new File(System.getProperty(\"java.io.tmpdir\"));\r\n\t\t    tmpDir.mkdirs();\r\n\t\t    if (!tmpDir.isDirectory()) { \r\n\t\t    \tSystem.out.println(\"Mkdirs failed to create \" + tmpDir);\r\n\t\t    }\r\n\t\t    final File workDir = File.createTempFile(\"unjar\", \"\", tmpDir);\r\n\t\t    workDir.delete();\r\n\t\t    workDir.mkdirs();\r\n\t\t    if (!workDir.isDirectory()) {\r\n\t\t    \tSystem.out.println(\"Mkdirs failed to create \" + workDir);\r\n\t\t    }\r\n\r\n\t\t    Runtime.getRuntime().addShutdownHook(new Thread() {\r\n\t\t        public void run() {\r\n\t\t          try {\r\n\t\t            fullyDelete(workDir);\r\n\t\t          } catch (IOException e) {\r\n\t\t          }\r\n\t\t        }\r\n\t\t      });\r\n\r\n\t\t    unJar(file, workDir);\r\n\t\t    \r\n\t\t    ArrayList<URL> classPath = new ArrayList<URL>();\r\n\t\t    classPath.add(new File(workDir+\"/\").toURL());\r\n\t\t    classPath.add(file.toURL());\r\n\t\t    classPath.add(new File(workDir, \"classes/\").toURL());\r\n\t\t    File[] libs = new File(workDir, \"lib\").listFiles();\r\n\t\t    if (libs != null) {\r\n\t\t      for (int i = 0; i < libs.length; i++) {\r\n\t\t        classPath.add(libs[i].toURL());\r\n\t\t      }\r\n\t\t    }\r\n\t\t    \r\n\t\t    ClassLoader loader =\t new URLClassLoader(classPath.toArray(new URL[0]));\r\n\r\n\t\t    Thread.currentThread().setContextClassLoader(loader);\r\n\t\t    Class<?> mainClass = Class.forName(mainClassName, true, loader);\r\n\t\t    return mainClass;\r\n\t\t  }\r\n\t  \r\n\t  public static boolean fullyDelete(File dir) throws IOException {\r\n\t\t    if (!fullyDeleteContents(dir)) {\r\n\t\t      return false;\r\n\t\t    }\r\n\t\t    return dir.delete();\r\n\t\t  }\r\n\r\n\t\t  /**\r\n\t\t   * Delete the contents of a directory, not the directory itself.  If\r\n\t\t   * we return false, the directory may be partially-deleted.\r\n\t\t   */\r\n\t\t  public static boolean fullyDeleteContents(File dir) throws IOException {\r\n\t\t    boolean deletionSucceeded = true;\r\n\t\t    File contents[] = dir.listFiles();\r\n\t\t    if (contents != null) {\r\n\t\t      for (int i = 0; i < contents.length; i++) {\r\n\t\t        if (contents[i].isFile()) {\r\n\t\t          if (!contents[i].delete()) {\r\n\t\t            deletionSucceeded = false;\r\n\t\t            continue; // continue deletion of other files/dirs under dir\r\n\t\t          }\r\n\t\t        } else {\r\n\t\t          //try deleting the directory\r\n\t\t          // this might be a symlink\r\n\t\t          boolean b = false;\r\n\t\t          b = contents[i].delete();\r\n\t\t          if (b){\r\n\t\t            //this was indeed a symlink or an empty directory\r\n\t\t            continue;\r\n\t\t          }\r\n\t\t          // if not an empty directory or symlink let\r\n\t\t          // fullydelete handle it.\r\n\t\t          if (!fullyDelete(contents[i])) {\r\n\t\t            deletionSucceeded = false;\r\n\t\t            continue; // continue deletion of other files/dirs under dir\r\n\t\t          }\r\n\t\t        }\r\n\t\t      }\r\n\t\t    }\r\n\t\t    return deletionSucceeded;\r\n\t\t  }\t\t  \t  \r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/ConfigLoader.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.loader;\n\nimport java.util.Map;\n\nimport io.mycat.config.model.ClusterConfig;\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.config.model.DataNodeConfig;\nimport io.mycat.config.model.FirewallConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.UserConfig;\n\n/**\n * @author mycat\n */\npublic interface ConfigLoader {\n\tSchemaConfig getSchemaConfig(String schema);\n\n\tMap<String, SchemaConfig> getSchemaConfigs();\n\n\tMap<String, DataNodeConfig> getDataNodes();\n\n\tMap<String, DataHostConfig> getDataHosts();\n\n\tSystemConfig getSystemConfig();\n\n\tUserConfig getUserConfig(String user);\n\n\tMap<String, UserConfig> getUserConfigs();\n\n\tFirewallConfig getFirewallConfig();\n\n\tClusterConfig getClusterConfig();\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/SchemaLoader.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.loader;\n\nimport java.util.Map;\n\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.config.model.DataNodeConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.rule.TableRuleConfig;\n\n/**\n * @author mycat\n */\npublic interface SchemaLoader {\n\t\n    Map<String, TableRuleConfig> getTableRules();\n\n    Map<String, DataHostConfig> getDataHosts();\n\n    Map<String, DataNodeConfig> getDataNodes();\n\n    Map<String, SchemaConfig> getSchemas();\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/console/ZookeeperPath.java",
    "content": "package io.mycat.config.loader.console;\n\n/**\n * 专门用来操作zookeeper路径的文件信息\n* 源文件名：ZkPath.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic enum ZookeeperPath {\n\n    /**\n     * zk的路径分隔符\n    * @字段说明 ZK_SEPARATOR\n    */\n    ZK_SEPARATOR(\"/\"),\n\n    /**\n     * 最基础的mycat节点\n     * @字段说明 FLOW_ZK_PATH_LINE\n     */\n    FLOW_ZK_PATH_BASE(\"mycat\"),\n\n    /**\n     * 在当前在线的节点\n    * @字段说明 FLOW_ZK_PATH_LINE\n    */\n    FLOW_ZK_PATH_LINE(\"line\"),\n\n    /**\n     * schema父路径\n    * @字段说明 FOW_ZK_PATH_SCHEMA\n    */\n    FOW_ZK_PATH_SCHEMA(\"schema\"),\n\n    /**\n     * 配制schema信息\n     * @字段说明 FLOW_ZK_PATH_SCHEMA\n     */\n    FLOW_ZK_PATH_SCHEMA_SCHEMA(\"schema\"),\n\n    /**\n     * 对应数据库信息\n    * @字段说明 FLOW_ZK_PATH_SCHEMA_DATANODE\n    */\n    FLOW_ZK_PATH_SCHEMA_DATANODE(\"dataNode\"),\n\n    /**\n     * 数据库信息dataHost\n     * @字段说明 FLOW_ZK_PATH_SCHEMA_DATANODE\n     */\n    FLOW_ZK_PATH_SCHEMA_DATAHOST(\"dataHost\"),\n\n    /**\n     * 路由信息\n     * @字段说明 FLOW_ZK_PATH_SCHEMA_DATANODE\n     */\n    FLOW_ZK_PATH_RULE(\"rules\"),\n\n    /**\n     * 路由信息\n     * @字段说明 FLOW_ZK_PATH_SCHEMA_DATANODE\n     */\n    FLOW_ZK_PATH_RULE_TABLERULE(\"tableRule\"),\n\n    /**\n     * 路由信息\n     * @字段说明 FLOW_ZK_PATH_SCHEMA_DATANODE\n     */\n    FLOW_ZK_PATH_RULE_FUNCTION(\"function\"),\n\n    /**\n     * 服务端配制路径\n    * @字段说明 FLOW_ZK_PATH_SERVER\n    */\n    FLOW_ZK_PATH_SERVER(\"server\"),\n\n    /**\n     * 默认配制信息\n    * @字段说明 FLOW_ZK_PATH_SERVER_DEFAULT\n    */\n    FLOW_ZK_PATH_SERVER_DEFAULT(\"default\"),\n\n    /**\n     * 针对集群的配制信息\n     * @字段说明 FLOW_ZK_PATH_SERVER_DEFAULT\n     */\n    FLOW_ZK_PATH_SERVER_CLUSTER(\"cluster\"),\n\n    /**\n     * 配制的用户信息\n     * @字段说明 FLOW_ZK_PATH_SERVER_DEFAULT\n     */\n    FLOW_ZK_PATH_SERVER_USER(\"user\"),\n\n    /**\n     * 配制的防火墙信息,如黑白名单信息\n     * @字段说明 FLOW_ZK_PATH_SERVER_DEFAULT\n     */\n    FLOW_ZK_PATH_SERVER_FIREWALL(\"firewall\"),\n\n    /**\n     * 表的权限信息\n    * @字段说明 FLOW_ZK_PATH_SERVER_AUTH\n    */\n    FLOW_ZK_PATH_SERVER_AUTH(\"auth\"),\n\n    /**\n     * 序列信息\n     * @字段说明 FLOW_ZK_PATH_SERVER_AUTH\n     */\n    FLOW_ZK_PATH_SEQUENCE(\"sequences\"),\n\n    /**\n     * 序列信息中公共配制信息\n     * @字段说明 FLOW_ZK_PATH_SERVER_AUTH\n     */\n    FLOW_ZK_PATH_SEQUENCE_COMMON(\"common\"),\n\n    /**\n     * 用来存放序列值的信息\n     * @字段说明 FLOW_ZK_PATH_SERVER_AUTH\n     */\n    FLOW_ZK_PATH_SEQUENCE_INSTANCE(\"instance\"),\n\n    /**\n     * 用来存放序列值的\n     * @字段说明 FLOW_ZK_PATH_SERVER_AUTH\n     */\n    FLOW_ZK_PATH_SEQUENCE_LEADER(\"leader\"),\n    \n    /**\n     * 递增序列号\n     * @字段说明 FLOW_ZK_PATH_SERVER_AUTH\n     */\n    FLOW_ZK_PATH_SEQUENCE_INCREMENT_SEQ(\"incr_sequence\"),\n\n    /**\n     * 序列信息中需要单独节点配制的信息\n     * @字段说明 FLOW_ZK_PATH_SERVER_AUTH\n     */\n    FLOW_ZK_PATH_SEQUENCE_CLUSTER(\"cluster\"),\n\n    /**\n     * 缓存信息\n    * @字段说明 FLOW_ZK_PATH_CACHE\n    */\n    FLOW_ZK_PATH_CACHE(\"cache\"),\n\n    /**\n     * 配制切换及状态目录信息\n    * @字段说明 FLOW_ZK_PATH_BINDATA\n    */\n    FLOW_ZK_PATH_BINDATA(\"bindata\"),\n\n\n    /**\n     * 配制切换及状态目录信息\n     * @字段说明 FLOW_ZK_PATH_RULEDATA\n     */\n    FLOW_ZK_PATH_RULEATA(\"ruledata\"),\n    /**\n     * dnindex切换信息\n     * @字段说明 FLOW_ZK_PATH_CACHE\n     */\n    FLOW_ZK_PATH_BINDATA_DNINDEX(\"dnindex\"),\n\n    /**\n     * 迁移的信息\n     * @字段说明 FLOW_ZK_PATH_CACHE\n     */\n    FLOW_ZK_PATH_BINDATA_MOVE(\"move\"),\n\n    /**\n     * 节点单独的配制信息\n    * @字段说明 FLOW_ZK_PATH_NODE\n    */\n    FLOW_ZK_PATH_NODE(\"node\"),\n\n    /**zk写入本地的路径信息\n    * @字段说明 ZK_LOCAL_WRITE_PATH\n    */\n    ZK_LOCAL_WRITE_PATH(\"./\"),\n    // /**zk写入本地的路径信息\n    // * @字段说明 ZK_LOCAL_WRITE_PATH\n    // */\n    // ZK_LOCAL_WRITE_PATH(\"zkdownload/\"),\n\n    /**\n     * zk本地配制目录信息\n     * @字段说明 ZK_LOCAL_WRITE_PATH\n     */\n    ZK_LOCAL_CFG_PATH(\"/zkconf/\"),\n\n    ;\n    /**\n     * 配制的key的信息\n    * @字段说明 key\n    */\n    private String key;\n\n    private ZookeeperPath(String key) {\n        this.key = key;\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/xml/XMLConfigLoader.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.loader.xml;\n\nimport java.util.Map;\n\nimport io.mycat.config.loader.ConfigLoader;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.model.ClusterConfig;\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.config.model.DataNodeConfig;\nimport io.mycat.config.model.FirewallConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.UserConfig;\n\n/**\n * @author mycat\n */\npublic class XMLConfigLoader implements ConfigLoader {\n\n    /** unmodifiable */\n    private final Map<String, DataHostConfig> dataHosts;\n    /** unmodifiable */\n    private final Map<String, DataNodeConfig> dataNodes;\n    /** unmodifiable */\n    private final Map<String, SchemaConfig> schemas;\n    private final SystemConfig system;\n    /** unmodifiable */\n    private final Map<String, UserConfig> users;\n    private final FirewallConfig firewall;\n    private final ClusterConfig cluster;\n\n    public XMLConfigLoader(SchemaLoader schemaLoader) {\n        XMLServerLoader serverLoader = new XMLServerLoader();\n        this.system = serverLoader.getSystem();\n        this.users = serverLoader.getUsers();\n        this.firewall = serverLoader.getFirewall();\n        this.cluster = serverLoader.getCluster();\n        this.dataHosts = schemaLoader.getDataHosts();\n        this.dataNodes = schemaLoader.getDataNodes();\n        this.schemas = schemaLoader.getSchemas();\n        schemaLoader = null;\n    }\n\n    @Override\n    public ClusterConfig getClusterConfig() {\n        return cluster;\n    }\n\n    @Override\n    public FirewallConfig getFirewallConfig() {\n        return firewall;\n    }\n\n    @Override\n    public UserConfig getUserConfig(String user) {\n        return users.get(user);\n    }\n\n    @Override\n    public Map<String, UserConfig> getUserConfigs() {\n        return users;\n    }\n\n    @Override\n    public SystemConfig getSystemConfig() {\n        return system;\n    }\n    @Override\n    public Map<String, SchemaConfig> getSchemaConfigs() {\n        return schemas;\n    }\n\n    @Override\n    public Map<String, DataNodeConfig> getDataNodes() {\n        return dataNodes;\n    }\n\n    @Override\n    public Map<String, DataHostConfig> getDataHosts() {\n        return dataHosts;\n    }\n\n    @Override\n    public SchemaConfig getSchemaConfig(String schema) {\n        return schemas.get(schema);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/xml/XMLRuleLoader.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.config.loader.xml;\r\n\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.lang.reflect.InvocationTargetException;\r\nimport java.sql.SQLSyntaxErrorException;\r\nimport java.util.Collections;\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\nimport org.w3c.dom.Element;\r\nimport org.w3c.dom.Node;\r\nimport org.w3c.dom.NodeList;\r\n\r\nimport io.mycat.config.model.rule.RuleConfig;\r\nimport io.mycat.config.model.rule.TableRuleConfig;\r\nimport io.mycat.config.util.ConfigException;\r\nimport io.mycat.config.util.ConfigUtil;\r\nimport io.mycat.config.util.ParameterMapping;\r\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\r\nimport io.mycat.util.SplitUtil;\r\n\r\n/**\r\n * @author mycat\r\n */\r\n@SuppressWarnings(\"unchecked\")\r\npublic class XMLRuleLoader {\r\n\tprivate final static String DEFAULT_DTD = \"/rule.dtd\";\r\n\tprivate final static String DEFAULT_XML = \"/rule.xml\";\r\n\r\n\tprivate final Map<String, TableRuleConfig> tableRules;\r\n\t// private final Set<RuleConfig> rules;\r\n\tprivate final Map<String, AbstractPartitionAlgorithm> functions;\r\n\r\n\tpublic XMLRuleLoader(String ruleFile) {\r\n\t\t// this.rules = new HashSet<RuleConfig>();\r\n\t\t//rule名 -> rule\r\n\t\tthis.tableRules = new HashMap<String, TableRuleConfig>();\r\n\t\t//function名 -> 具体分片算法\r\n\t\tthis.functions = new HashMap<String, AbstractPartitionAlgorithm>();\r\n\t\tload(DEFAULT_DTD, ruleFile == null ? DEFAULT_XML : ruleFile);\r\n\t}\r\n\r\n\tpublic XMLRuleLoader() {\r\n\t\tthis(null);\r\n\t}\r\n\r\n\tpublic Map<String, TableRuleConfig> getTableRules() {\r\n\t\treturn (Map<String, TableRuleConfig>) (tableRules.isEmpty() ? Collections\r\n\t\t\t\t.emptyMap() : tableRules);\r\n\t}\r\n\r\n\t\r\n\r\n\t\r\n\tprivate void load(String dtdFile, String xmlFile) {\r\n\t\tInputStream dtd = null;\r\n\t\tInputStream xml = null;\r\n\t\ttry {\r\n\t\t\tdtd = XMLRuleLoader.class.getResourceAsStream(dtdFile);\r\n\t\t\txml = XMLRuleLoader.class.getResourceAsStream(xmlFile);\r\n\t\t\t//读取出语意树\r\n\t\t\tElement root = ConfigUtil.getDocument(dtd, xml)\r\n\t\t\t\t\t.getDocumentElement();\r\n\t\t\t//加载Function\r\n\t\t\tloadFunctions(root);\r\n\t\t\t//加载TableRule\r\n\t\t\tloadTableRules(root);\r\n\t\t} catch (ConfigException e) {\r\n\t\t\tthrow e;\r\n\t\t} catch (Exception e) {\r\n\t\t\tthrow new ConfigException(e);\r\n\t\t} finally {\r\n\t\t\tif (dtd != null) {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tdtd.close();\r\n\t\t\t\t} catch (IOException e) {\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (xml != null) {\r\n\t\t\t\ttry {\r\n\t\t\t\t\txml.close();\r\n\t\t\t\t} catch (IOException e) {\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * tableRule标签结构：\r\n\t * <tableRule name=\"sharding-by-month\">\r\n\t *     <rule>\r\n\t *         <columns>create_date</columns>\r\n\t *         <algorithm>partbymonth</algorithm>\r\n\t *     </rule>\r\n\t * </tableRule>\r\n\t * @param root\r\n\t * @throws SQLSyntaxErrorException\r\n     */\r\n\tprivate void loadTableRules(Element root) throws SQLSyntaxErrorException {\r\n\t\t//获取每个tableRule标签\r\n\t\tNodeList list = root.getElementsByTagName(\"tableRule\");\r\n\t\tfor (int i = 0, n = list.getLength(); i < n; ++i) {\r\n\t\t\tNode node = list.item(i);\r\n\t\t\tif (node instanceof Element) {\r\n\t\t\t\tElement e = (Element) node;\r\n\t\t\t\t//先判断是否重复\r\n\t\t\t\tString name = e.getAttribute(\"name\");\r\n\t\t\t\tif (tableRules.containsKey(name)) {\r\n\t\t\t\t\tthrow new ConfigException(\"table rule \" + name\r\n\t\t\t\t\t\t\t+ \" duplicated!\");\r\n\t\t\t\t}\r\n\t\t\t\t//获取rule标签\r\n\t\t\t\tNodeList ruleNodes = e.getElementsByTagName(\"rule\");\r\n\t\t\t\tint length = ruleNodes.getLength();\r\n\t\t\t\tif (length > 1) {\r\n\t\t\t\t\tthrow new ConfigException(\"only one rule can defined :\"\r\n\t\t\t\t\t\t\t+ name);\r\n\t\t\t\t}\r\n\t\t\t\t//目前只处理第一个，未来可能有多列复合逻辑需求\r\n\t\t\t\t//RuleConfig是保存着rule与function对应关系的对象\r\n\t\t\t\tRuleConfig rule = loadRule((Element) ruleNodes.item(0));\r\n\t\t\t\tString funName = rule.getFunctionName();\r\n\t\t\t\t//判断function是否存在，获取function\r\n\t\t\t\tAbstractPartitionAlgorithm func = functions.get(funName);\r\n\t\t\t\tif (func == null) {\r\n\t\t\t\t\tthrow new ConfigException(\"can't find function of name :\"\r\n\t\t\t\t\t\t\t+ funName);\r\n\t\t\t\t}\r\n\t\t\t\trule.setRuleAlgorithm(func);\r\n\t\t\t\t//保存到tableRules\r\n\t\t\t\ttableRules.put(name, new TableRuleConfig(name, rule));\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprivate RuleConfig loadRule(Element element) throws SQLSyntaxErrorException {\r\n\t\t//读取columns\r\n\t\tElement columnsEle = ConfigUtil.loadElement(element, \"columns\");\r\n\t\tString column = columnsEle.getTextContent();\r\n\t\tString[] columns = SplitUtil.split(column, ',', true);\r\n\t\tif (columns.length > 1) {\r\n\t\t\tthrow new ConfigException(\"table rule coulmns has multi values:\"\r\n\t\t\t\t\t+ columnsEle.getTextContent());\r\n\t\t}\r\n\t\t//读取algorithm\r\n\t\tElement algorithmEle = ConfigUtil.loadElement(element, \"algorithm\");\r\n\t\tString algorithm = algorithmEle.getTextContent();\r\n\t\treturn new RuleConfig(column.toUpperCase(), algorithm);\r\n\t}\r\n\r\n\t/**\r\n\t * function标签结构：\r\n\t * <function name=\"partbymonth\" class=\"io.mycat.route.function.PartitionByMonth\">\r\n\t *     <property name=\"dateFormat\">yyyy-MM-dd</property>\r\n\t *     <property name=\"sBeginDate\">2015-01-01</property>\r\n\t * </function>\r\n\t * @param root\r\n\t * @throws ClassNotFoundException\r\n\t * @throws InstantiationException\r\n\t * @throws IllegalAccessException\r\n\t * @throws InvocationTargetException\r\n     */\r\n\tprivate void loadFunctions(Element root) throws ClassNotFoundException,\r\n\t\t\tInstantiationException, IllegalAccessException,\r\n\t\t\tInvocationTargetException {\r\n\t\tNodeList list = root.getElementsByTagName(\"function\");\r\n\t\tfor (int i = 0, n = list.getLength(); i < n; ++i) {\r\n\t\t\tNode node = list.item(i);\r\n\t\t\tif (node instanceof Element) {\r\n\t\t\t\tElement e = (Element) node;\r\n\t\t\t\t//获取name标签\r\n\t\t\t\tString name = e.getAttribute(\"name\");\r\n\t\t\t\t//如果Map已有，则function重复\r\n\t\t\t\tif (functions.containsKey(name)) {\r\n\t\t\t\t\tthrow new ConfigException(\"rule function \" + name\r\n\t\t\t\t\t\t\t+ \" duplicated!\");\r\n\t\t\t\t}\r\n\t\t\t\t//获取class标签\r\n\t\t\t\tString clazz = e.getAttribute(\"class\");\r\n\t\t\t\t//根据class利用反射新建分片算法\r\n\t\t\t\tAbstractPartitionAlgorithm function = createFunction(name, clazz);\r\n\t\t\t\t//根据读取参数配置分片算法\r\n\t\t\t\tParameterMapping.mapping(function, ConfigUtil.loadElements(e));\r\n\t\t\t\t//每个AbstractPartitionAlgorithm可能会实现init来初始化\r\n\t\t\t\tfunction.init();\r\n\t\t\t\t//放入functions map\r\n\t\t\t\tfunctions.put(name, function);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprivate AbstractPartitionAlgorithm createFunction(String name, String clazz)\r\n\t\t\tthrows ClassNotFoundException, InstantiationException,\r\n\t\t\tIllegalAccessException, InvocationTargetException {\r\n\t\tClass<?> clz = Class.forName(clazz);\r\n\t\t//判断是否继承AbstractPartitionAlgorithm\r\n\t\tif (!AbstractPartitionAlgorithm.class.isAssignableFrom(clz)) {\r\n\t\t\tthrow new IllegalArgumentException(\"rule function must implements \"\r\n\t\t\t\t\t+ AbstractPartitionAlgorithm.class.getName() + \", name=\" + name);\r\n\t\t}\r\n\t\treturn (AbstractPartitionAlgorithm) clz.newInstance();\r\n\t}\r\n\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/xml/XMLSchemaLoader.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.loader.xml;\n\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.model.*;\nimport io.mycat.config.model.rule.RuleConfig;\nimport io.mycat.config.model.rule.TableRuleConfig;\nimport io.mycat.config.util.ConfigException;\nimport io.mycat.config.util.ConfigUtil;\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\nimport io.mycat.route.function.TableRuleAware;\nimport io.mycat.util.DecryptUtil;\nimport io.mycat.util.ObjectUtil;\nimport io.mycat.util.SplitUtil;\nimport io.mycat.util.StringUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\n\n/**\n * @author mycat\n */\n@SuppressWarnings(\"unchecked\")\npublic class XMLSchemaLoader implements SchemaLoader {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(XMLSchemaLoader.class);\n\n    private final static String DEFAULT_DTD = \"/schema.dtd\";\n    private final static String DEFAULT_XML = \"/schema.xml\";\n\n    private final Map<String, TableRuleConfig> tableRules;\n    private final Map<String, DataHostConfig> dataHosts;\n    private final Map<String, DataNodeConfig> dataNodes;\n    private final Map<String, SchemaConfig> schemas;\n\n    public XMLSchemaLoader(String schemaFile, String ruleFile) {\n        //先读取rule.xml\n        XMLRuleLoader ruleLoader = new XMLRuleLoader(ruleFile);\n        //将tableRules拿出，用于这里加载Schema做rule有效判断，以及之后的分片路由计算\n        this.tableRules = ruleLoader.getTableRules();\n        //释放ruleLoader\n        ruleLoader = null;\n        this.dataHosts = new HashMap<String, DataHostConfig>();\n        this.dataNodes = new HashMap<String, DataNodeConfig>();\n        this.schemas = new HashMap<String, SchemaConfig>();\n        //读取加载schema配置\n        this.load(DEFAULT_DTD, schemaFile == null ? DEFAULT_XML : schemaFile);\n    }\n\n    public XMLSchemaLoader() {\n        this(null, null);\n    }\n\n    @Override\n    public Map<String, TableRuleConfig> getTableRules() {\n        return tableRules;\n    }\n\n    @Override\n    public Map<String, DataHostConfig> getDataHosts() {\n        return (Map<String, DataHostConfig>) (dataHosts.isEmpty() ? Collections.emptyMap() : dataHosts);\n    }\n\n    @Override\n    public Map<String, DataNodeConfig> getDataNodes() {\n        return (Map<String, DataNodeConfig>) (dataNodes.isEmpty() ? Collections.emptyMap() : dataNodes);\n    }\n\n    @Override\n    public Map<String, SchemaConfig> getSchemas() {\n        return (Map<String, SchemaConfig>) (schemas.isEmpty() ? Collections.emptyMap() : schemas);\n    }\n\n    private void load(String dtdFile, String xmlFile) {\n        InputStream dtd = null;\n        InputStream xml = null;\n        try {\n            dtd = XMLSchemaLoader.class.getResourceAsStream(dtdFile);\n            xml = XMLSchemaLoader.class.getResourceAsStream(xmlFile);\n            Element root = ConfigUtil.getDocument(dtd, xml).getDocumentElement();\n            //先加载所有的DataHost\n            loadDataHosts(root);\n            //再加载所有的DataNode\n            loadDataNodes(root);\n            //最后加载所有的Schema\n            loadSchemas(root);\n        } catch (ConfigException e) {\n            throw e;\n        } catch (Exception e) {\n            throw new ConfigException(e);\n        } finally {\n\n            if (dtd != null) {\n                try {\n                    dtd.close();\n                } catch (IOException e) {\n                }\n            }\n\n            if (xml != null) {\n                try {\n                    xml.close();\n                } catch (IOException e) {\n                }\n            }\n        }\n    }\n\n    private void loadSchemas(Element root) {\n        NodeList list = root.getElementsByTagName(\"schema\");\n        for (int i = 0, n = list.getLength(); i < n; i++) {\n            Element schemaElement = (Element) list.item(i);\n            //读取各个属性\n            String name = schemaElement.getAttribute(\"name\");\n            String dataNode = schemaElement.getAttribute(\"dataNode\");\n            String randomDataNode = schemaElement.getAttribute(\"randomDataNode\");\n            String checkSQLSchemaStr = schemaElement.getAttribute(\"checkSQLschema\");\n            String sqlMaxLimitStr = schemaElement.getAttribute(\"sqlMaxLimit\");\n            int sqlMaxLimit = -1;\n            //读取sql返回结果集限制\n            if (sqlMaxLimitStr != null && !sqlMaxLimitStr.isEmpty()) {\n                sqlMaxLimit = Integer.parseInt(sqlMaxLimitStr);\n            }\n\n            // check dataNode already exists or not,看schema标签中是否有datanode\n            String defaultDbType = null;\n            //校验检查并添加dataNode\n            if (dataNode != null && !dataNode.isEmpty()) {\n                List<String> dataNodeLst = new ArrayList<String>(1);\n                dataNodeLst.add(dataNode);\n                checkDataNodeExists(dataNodeLst);\n                String dataHost = dataNodes.get(dataNode).getDataHost();\n                defaultDbType = dataHosts.get(dataHost).getDbType();\n            } else {\n                dataNode = null;\n            }\n            //加载schema下所有tables\n            Map<String, TableConfig> tables = loadTables(schemaElement);\n            //判断schema是否重复\n            if (schemas.containsKey(name)) {\n                throw new ConfigException(\"schema \" + name + \" duplicated!\");\n            }\n\n            // 设置了table的不需要设置dataNode属性，没有设置table的必须设置dataNode属性\n            if (dataNode == null && tables.size() == 0) {\n                throw new ConfigException(\n                        \"schema \" + name + \" didn't config tables,so you must set dataNode property!\");\n            }\n\n            SchemaConfig schemaConfig = new SchemaConfig(name, dataNode,\n                    tables, sqlMaxLimit, \"true\".equalsIgnoreCase(checkSQLSchemaStr),randomDataNode);\n\n            //设定DB类型，这对之后的sql语句路由解析有帮助\n            if (defaultDbType != null) {\n                schemaConfig.setDefaultDataNodeDbType(defaultDbType);\n                if (!\"mysql\".equalsIgnoreCase(defaultDbType)) {\n                    schemaConfig.setNeedSupportMultiDBType(true);\n                }\n            }\n\n            // 判断是否有不是mysql的数据库类型，方便解析判断是否启用多数据库分页语法解析\n            for (TableConfig tableConfig : tables.values()) {\n                if (isHasMultiDbType(tableConfig)) {\n                    schemaConfig.setNeedSupportMultiDBType(true);\n                    break;\n                }\n            }\n            //记录每种dataNode的DB类型\n            Map<String, String> dataNodeDbTypeMap = new HashMap<>();\n            for (String dataNodeName : dataNodes.keySet()) {\n                DataNodeConfig dataNodeConfig = dataNodes.get(dataNodeName);\n                String dataHost = dataNodeConfig.getDataHost();\n                DataHostConfig dataHostConfig = dataHosts.get(dataHost);\n                if (dataHostConfig != null) {\n                    String dbType = dataHostConfig.getDbType();\n                    dataNodeDbTypeMap.put(dataNodeName, dbType);\n                }\n            }\n            schemaConfig.setDataNodeDbTypeMap(dataNodeDbTypeMap);\n            schemas.put(name, schemaConfig);\n        }\n    }\n\n\n    /**\n     * 处理动态日期表, 支持 YYYYMM、YYYYMMDD 两种格式\n     * <p>\n     * YYYYMM格式： \t  yyyymm,2015,01,60\n     * YYYYMMDD格式:  yyyymmdd,2015,01,10,50\n     *\n     * @param tableNameElement\n     * @param tableNameSuffixElement\n     * @return\n     */\n    private String doTableNameSuffix(String tableNameElement, String tableNameSuffixElement) {\n\n        String newTableName = tableNameElement;\n\n        String[] params = tableNameSuffixElement.split(\",\");\n        String suffixFormat = params[0].toUpperCase();\n        if (suffixFormat.equals(\"YYYYMM\")) {\n\n            //读取参数\n            int yyyy = Integer.parseInt(params[1]);\n            int mm = Integer.parseInt(params[2]);\n            int mmEndIdx = Integer.parseInt(params[3]);\n\n            //日期处理\n            SimpleDateFormat yyyyMMSDF = new SimpleDateFormat(\"yyyyMM\");\n\n            Calendar cal = Calendar.getInstance();\n            cal.set(Calendar.YEAR, yyyy);\n            cal.set(Calendar.MONTH, mm - 1);\n            cal.set(Calendar.DATE, 0);\n\n            //表名改写\n            StringBuffer tableNameBuffer = new StringBuffer();\n            for (int mmIdx = 0; mmIdx <= mmEndIdx; mmIdx++) {\n                tableNameBuffer.append(tableNameElement);\n                tableNameBuffer.append(yyyyMMSDF.format(cal.getTime()));\n                cal.add(Calendar.MONTH, 1);\n\n                if (mmIdx != mmEndIdx) {\n                    tableNameBuffer.append(\",\");\n                }\n            }\n            newTableName = tableNameBuffer.toString();\n\n        } else if (suffixFormat.equals(\"YYYYMMDD\")) {\n\n            //读取参数\n            int yyyy = Integer.parseInt(params[1]);\n            int mm = Integer.parseInt(params[2]);\n            int dd = Integer.parseInt(params[3]);\n            int ddEndIdx = Integer.parseInt(params[4]);\n\n            //日期处理\n            SimpleDateFormat yyyyMMddSDF = new SimpleDateFormat(\"yyyyMMdd\");\n\n            Calendar cal = Calendar.getInstance();\n            cal.set(Calendar.YEAR, yyyy);\n            cal.set(Calendar.MONTH, mm - 1);\n            cal.set(Calendar.DATE, dd);\n\n            //表名改写\n            StringBuffer tableNameBuffer = new StringBuffer();\n            for (int ddIdx = 0; ddIdx <= ddEndIdx; ddIdx++) {\n                tableNameBuffer.append(tableNameElement);\n                tableNameBuffer.append(yyyyMMddSDF.format(cal.getTime()));\n\n                cal.add(Calendar.DATE, 1);\n\n                if (ddIdx != ddEndIdx) {\n                    tableNameBuffer.append(\",\");\n                }\n            }\n            newTableName = tableNameBuffer.toString();\n        }\n        return newTableName;\n    }\n\n\n    private Map<String, TableConfig> loadTables(Element node) {\n\n        // Map<String, TableConfig> tables = new HashMap<String, TableConfig>();\n\n        // 支持表名中包含引号[`] BEN GONG\n        final String schemaName = node.getAttribute(\"name\");\n        Map<String, TableConfig> tables = new TableConfigMap();\n        NodeList nodeList = node.getElementsByTagName(\"table\");\n        List<Element> list = new ArrayList<>();\n        for (int i = 0; i < nodeList.getLength(); i++) {\n            Element tableElement = (Element) nodeList.item(i);\n            String tableNameElement = tableElement.getAttribute(\"name\").toUpperCase();\n            if(\"true\".equalsIgnoreCase(tableElement.getAttribute(\"splitTableNames\"))){\n                String[] split = tableNameElement.split(\",\");\n                for (String name : split) {\n                    Element node1 = (Element)tableElement.cloneNode(true);\n                    node1.setAttribute(\"name\",name);\n                    list.add(node1);\n                }\n            }else {\n                list.add(tableElement);\n            }\n        }\n        loadTable(schemaName, tables, list);\n        return tables;\n    }\n\n    private void loadTable(String schemaName, Map<String, TableConfig> tables,  List<Element>  nodeList) {\n        for (int i = 0; i < nodeList.size(); i++) {\n            Element tableElement = (Element) nodeList.get(i);\n            String tableNameElement = tableElement.getAttribute(\"name\").toUpperCase();\n\n            //TODO:路由, 增加对动态日期表的支持\n            String tableNameSuffixElement = tableElement.getAttribute(\"nameSuffix\").toUpperCase();\n            if (!\"\".equals(tableNameSuffixElement)) {\n\n                if (tableNameElement.split(\",\").length > 1) {\n                    throw new ConfigException(\"nameSuffix \" + tableNameSuffixElement + \", require name parameter cannot multiple breaks!\");\n                }\n                //前缀用来标明日期格式\n                tableNameElement = doTableNameSuffix(tableNameElement, tableNameSuffixElement);\n            }\n            //记录主键，用于之后路由分析，以及启用自增长主键\n            String[] tableNames = tableNameElement.split(\",\");\n            String primaryKey = tableElement.hasAttribute(\"primaryKey\") ? tableElement.getAttribute(\"primaryKey\").toUpperCase() : null;\n            //记录是否主键自增，默认不是，（启用全局sequence handler）\n            boolean autoIncrement = false;\n            if (tableElement.hasAttribute(\"autoIncrement\")) {\n                autoIncrement = Boolean.parseBoolean(tableElement.getAttribute(\"autoIncrement\"));\n            }\n\n            boolean fetchStoreNodeByJdbc = false;\n            if (tableElement.hasAttribute(\"fetchStoreNodeByJdbc\")) {\n                fetchStoreNodeByJdbc = Boolean.parseBoolean(tableElement.getAttribute(\"fetchStoreNodeByJdbc\"));\n            }\n            //记录是否需要加返回结果集限制，默认需要加\n            boolean needAddLimit = true;\n            if (tableElement.hasAttribute(\"needAddLimit\")) {\n                needAddLimit = Boolean.parseBoolean(tableElement.getAttribute(\"needAddLimit\"));\n            }\n            //记录type，是否为global\n            String tableTypeStr = tableElement.hasAttribute(\"type\") ? tableElement.getAttribute(\"type\") : null;\n            int tableType = TableConfig.TYPE_GLOBAL_DEFAULT;\n            if (\"global\".equalsIgnoreCase(tableTypeStr)) {\n                tableType = TableConfig.TYPE_GLOBAL_TABLE;\n            }\n            //记录dataNode，就是分布在哪些dataNode上\n            String dataNode = tableElement.getAttribute(\"dataNode\");\n            TableRuleConfig tableRule = null;\n            if (tableElement.hasAttribute(\"rule\")) {\n                String ruleName = tableElement.getAttribute(\"rule\");\n                tableRule = tableRules.get(ruleName);\n                if (tableRule == null) {\n                    throw new ConfigException(\"rule \" + ruleName + \" is not found!\");\n                }\n            }\n\n            boolean ruleRequired = false;\n            //记录是否绑定有分片规则\n            if (tableElement.hasAttribute(\"ruleRequired\")) {\n                ruleRequired = Boolean.parseBoolean(tableElement.getAttribute(\"ruleRequired\"));\n            }\n\n            if (tableNames == null) {\n                throw new ConfigException(\"table name is not found!\");\n            }\n            //distribute函数，重新编排dataNode\n            String distPrex = \"distribute(\";\n            boolean distTableDns = dataNode.startsWith(distPrex);\n            if (distTableDns) {\n                dataNode = dataNode.substring(distPrex.length(), dataNode.length() - 1);\n            }\n            //分表功能\n            String subTables = tableElement.getAttribute(\"subTables\");\n\n            for (int j = 0; j < tableNames.length; j++) {\n\n                String tableName = tableNames[j];\n                TableRuleConfig tableRuleConfig = tableRule;\n                if (tableRuleConfig != null) {\n                    //对于实现TableRuleAware的function进行特殊处理  根据每个表新建个实例\n                    RuleConfig rule = tableRuleConfig.getRule();\n                    if (rule.getRuleAlgorithm() instanceof TableRuleAware) {\n                        //因为ObjectUtil.copyObject是深拷贝,所以会把crc32的算法也拷贝一份状态,而不是公用一个分片数\n                        tableRuleConfig = (TableRuleConfig) ObjectUtil.copyObject(tableRuleConfig);\n                        String name = tableRuleConfig.getName();\n                        String newRuleName = getNewRuleName(schemaName, tableName, name);\n                        tableRuleConfig.setName(newRuleName);\n                        TableRuleAware tableRuleAware = (TableRuleAware) tableRuleConfig.getRule().getRuleAlgorithm();\n                        tableRuleAware.setRuleName(newRuleName);\n                        tableRules.put(newRuleName, tableRuleConfig);\n                    }\n                }\n\n                TableConfig table = new TableConfig(tableName, primaryKey,\n                        autoIncrement, needAddLimit, tableType, dataNode,\n                        getDbType(dataNode),\n                        (tableRuleConfig != null) ? tableRuleConfig.getRule() : null,\n                        ruleRequired, null, false, null, null, subTables, fetchStoreNodeByJdbc);\n                //因为需要等待TableConfig构造完毕才可以拿到dataNode节点数量,所以Rule构造延后到此处 @cjw\n                if ((tableRuleConfig != null) && (tableRuleConfig.getRule().getRuleAlgorithm() instanceof TableRuleAware)) {\n                    AbstractPartitionAlgorithm newRuleAlgorithm = tableRuleConfig.getRule().getRuleAlgorithm();\n                    ((TableRuleAware)newRuleAlgorithm).setTableConfig(table);\n                    newRuleAlgorithm.init();\n                }\n                checkDataNodeExists(table.getDataNodes());\n                // 检查分片表分片规则配置是否合法\n                if (table.getRule() != null) {\n                    checkRuleSuitTable(table);\n                }\n\n                if (distTableDns) {\n                    distributeDataNodes(table.getDataNodes());\n                }\n                //检查去重\n                if (tables.containsKey(table.getName())) {\n                    throw new ConfigException(\"table \" + tableName + \" duplicated!\");\n                }\n                //放入map\n                tables.put(table.getName(), table);\n            }\n            //只有tableName配置的是单个表（没有逗号）的时候才能有子表\n            if (tableNames.length == 1) {\n                TableConfig table = tables.get(tableNames[0]);\n                // process child tables\n                processChildTables(tables, table, dataNode, tableElement);\n            }\n        }\n    }\n\n    private String getNewRuleName(String schemaName, String tableName, String name) {\n        return name + \"_\" + schemaName + \"_\" + tableName;\n    }\n\n    /**\n     * distribute datanodes in multi hosts,means ,dn1 (host1),dn100\n     * (host2),dn300(host3),dn2(host1),dn101(host2),dn301(host3)...etc\n     * 将每个host上的datanode按照host重新排列。比如上面的例子host1拥有dn1,dn2，host2拥有dn100，dn101，host3拥有dn300，dn301,\n     * 按照host重新排列： 0->dn1 (host1),1->dn100(host2),2->dn300(host3),3->dn2(host1),4->dn101(host2),5->dn301(host3)\n     *\n     * @param theDataNodes\n     */\n    private void distributeDataNodes(ArrayList<String> theDataNodes) {\n        Map<String, ArrayList<String>> newDataNodeMap = new HashMap<String, ArrayList<String>>(dataHosts.size());\n        for (String dn : theDataNodes) {\n            DataNodeConfig dnConf = dataNodes.get(dn);\n            String host = dnConf.getDataHost();\n            ArrayList<String> hostDns = newDataNodeMap.get(host);\n            hostDns = (hostDns == null) ? new ArrayList<String>() : hostDns;\n            hostDns.add(dn);\n            newDataNodeMap.put(host, hostDns);\n        }\n\n        ArrayList<String> result = new ArrayList<String>(theDataNodes.size());\n        boolean hasData = true;\n        while (hasData) {\n            hasData = false;\n            for (ArrayList<String> dns : newDataNodeMap.values()) {\n                if (!dns.isEmpty()) {\n                    result.add(dns.remove(0));\n                    hasData = true;\n                }\n            }\n        }\n        theDataNodes.clear();\n        theDataNodes.addAll(result);\n    }\n\n    private Set<String> getDbType(String dataNode) {\n        Set<String> dbTypes = new HashSet<>();\n        String[] dataNodeArr = SplitUtil.split(dataNode, ',', '$', '-');\n        for (String node : dataNodeArr) {\n            DataNodeConfig datanode = dataNodes.get(node);\n            DataHostConfig datahost = dataHosts.get(datanode.getDataHost());\n            dbTypes.add(datahost.getDbType());\n        }\n\n        return dbTypes;\n    }\n\n    private Set<String> getDataNodeDbTypeMap(String dataNode) {\n        Set<String> dbTypes = new HashSet<>();\n        String[] dataNodeArr = SplitUtil.split(dataNode, ',', '$', '-');\n        for (String node : dataNodeArr) {\n            DataNodeConfig datanode = dataNodes.get(node);\n            DataHostConfig datahost = dataHosts.get(datanode.getDataHost());\n            dbTypes.add(datahost.getDbType());\n        }\n        return dbTypes;\n    }\n\n    private boolean isHasMultiDbType(TableConfig table) {\n        Set<String> dbTypes = table.getDbTypes();\n        for (String dbType : dbTypes) {\n            if (!\"mysql\".equalsIgnoreCase(dbType)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private void processChildTables(Map<String, TableConfig> tables,\n                                    TableConfig parentTable, String dataNodes, Element tableNode) {\n\n        // parse child tables\n        NodeList childNodeList = tableNode.getChildNodes();\n        for (int j = 0; j < childNodeList.getLength(); j++) {\n            Node theNode = childNodeList.item(j);\n            if (!theNode.getNodeName().equals(\"childTable\")) {\n                continue;\n            }\n            Element childTbElement = (Element) theNode;\n            //读取子表信息\n            String cdTbName = childTbElement.getAttribute(\"name\").toUpperCase();\n            String primaryKey = childTbElement.hasAttribute(\"primaryKey\") ? childTbElement.getAttribute(\"primaryKey\").toUpperCase() : null;\n\n            boolean autoIncrement = false;\n            if (childTbElement.hasAttribute(\"autoIncrement\")) {\n                autoIncrement = Boolean.parseBoolean(childTbElement.getAttribute(\"autoIncrement\"));\n            }\n            boolean needAddLimit = true;\n            if (childTbElement.hasAttribute(\"needAddLimit\")) {\n                needAddLimit = Boolean.parseBoolean(childTbElement.getAttribute(\"needAddLimit\"));\n            }\n\n            String subTables = childTbElement.getAttribute(\"subTables\");\n            //子表join键，和对应的parent的键，父子表通过这个关联\n            String joinKey = childTbElement.getAttribute(\"joinKey\").toUpperCase();\n            String parentKey = childTbElement.getAttribute(\"parentKey\").toUpperCase();\n            TableConfig table = new TableConfig(cdTbName, primaryKey,\n                    autoIncrement, needAddLimit,\n                    TableConfig.TYPE_GLOBAL_DEFAULT, dataNodes,\n                    getDbType(dataNodes), null, false, parentTable, true,\n                    joinKey, parentKey, subTables, false);\n\n            if (tables.containsKey(table.getName())) {\n                throw new ConfigException(\"table \" + table.getName() + \" duplicated!\");\n            }\n            tables.put(table.getName(), table);\n            //对于子表的子表，递归处理\n            processChildTables(tables, table, dataNodes, childTbElement);\n        }\n    }\n\n    private void checkDataNodeExists(Collection<String> nodes) {\n        if (nodes == null || nodes.size() < 1) {\n            return;\n        }\n        for (String node : nodes) {\n            if (!dataNodes.containsKey(node)) {\n                throw new ConfigException(\"dataNode '\" + node + \"' is not found!\");\n            }\n        }\n    }\n\n    /**\n     * 检查分片表分片规则配置, 目前主要检查分片表分片算法定义与分片dataNode是否匹配<br>\n     * 例如分片表定义如下:<br>\n     * {@code\n     * <table name=\"hotnews\" primaryKey=\"ID\" autoIncrement=\"true\" dataNode=\"dn1,dn2\"\n     * rule=\"mod-long\" />\n     * }\n     * <br>\n     * 分片算法如下:<br>\n     * {@code\n     * <function name=\"mod-long\" class=\"io.mycat.route.function.PartitionByMod\">\n     * <!-- how many data nodes -->\n     * <property name=\"count\">3</property>\n     * </function>\n     * }\n     * <br>\n     * shard table datanode(2) < function count(3) 此时检测为不匹配\n     */\n    private void checkRuleSuitTable(TableConfig tableConf) {\n        AbstractPartitionAlgorithm function = tableConf.getRule().getRuleAlgorithm();\n        int suitValue = function.suitableFor(tableConf);\n        switch (suitValue) {\n            case -1:\n                // 少节点,给提示并抛异常\n                throw new ConfigException(\"Illegal table conf : table [ \" + tableConf.getName() + \" ] rule function [ \"\n                        + tableConf.getRule().getFunctionName() + \" ] partition size : \" + tableConf.getRule().getRuleAlgorithm().getPartitionNum() + \" > table datanode size : \"\n                        + tableConf.getDataNodes().size() + \", please make sure table datanode size = function partition size\");\n            case 0:\n                // table datanode size == rule function partition size\n                break;\n            case 1:\n                // 有些节点是多余的,给出warn log\n                LOGGER.warn(\"table conf : table [ {} ] rule function [ {} ] partition size : {} < table datanode size : {} , this cause some datanode to be redundant\",\n                        new String[]{\n                                tableConf.getName(),\n                                tableConf.getRule().getFunctionName(),\n                                String.valueOf(tableConf.getRule().getRuleAlgorithm().getPartitionNum()),\n                                String.valueOf(tableConf.getDataNodes().size())\n                        });\n                break;\n        }\n    }\n\n    private void loadDataNodes(Element root) {\n        //读取DataNode分支\n        NodeList list = root.getElementsByTagName(\"dataNode\");\n        for (int i = 0, n = list.getLength(); i < n; i++) {\n            Element element = (Element) list.item(i);\n            String dnNamePre = element.getAttribute(\"name\");\n\n            String databaseStr = element.getAttribute(\"database\");\n            String host = element.getAttribute(\"dataHost\");\n            //字符串不为空\n            if (empty(dnNamePre) || empty(databaseStr) || empty(host)) {\n                throw new ConfigException(\"dataNode \" + dnNamePre + \" define error ,attribute can't be empty\");\n            }\n            //dnNames（name）,databases（database）,hostStrings（dataHost）都可以配置多个，以',', '$', '-'区分，但是需要保证database的个数*dataHost的个数=name的个数\n            //多个dataHost与多个database如果写在一个标签，则每个dataHost拥有所有database\n            //例如：<dataNode name=\"dn1$0-75\" dataHost=\"localhost$1-10\" database=\"db$0-759\" />\n            //则为：localhost1拥有dn1$0-75,localhost2也拥有dn1$0-75（对应db$76-151）\n            String[] dnNames = io.mycat.util.SplitUtil.split(dnNamePre, ',', '$', '-');\n            String[] databases = io.mycat.util.SplitUtil.split(databaseStr, ',', '$', '-');\n            String[] hostStrings = io.mycat.util.SplitUtil.split(host, ',', '$', '-');\n\n            if (dnNames.length > 1 && dnNames.length != databases.length * hostStrings.length) {\n                throw new ConfigException(\"dataNode \" + dnNamePre\n                        + \" define error ,dnNames.length must be=databases.length*hostStrings.length\");\n            }\n            if (dnNames.length > 1) {\n\n                List<String[]> mhdList = mergerHostDatabase(hostStrings, databases);\n                for (int k = 0; k < dnNames.length; k++) {\n                    String[] hd = mhdList.get(k);\n                    String dnName = dnNames[k];\n                    String databaseName = hd[1];\n                    String hostName = hd[0];\n                    createDataNode(dnName, databaseName, hostName);\n                }\n\n            } else {\n                createDataNode(dnNamePre, databaseStr, host);\n            }\n\n        }\n    }\n\n    /**\n     * 匹配DataHost和Database，每个DataHost拥有每个Database名字\n     *\n     * @param hostStrings\n     * @param databases\n     * @return\n     */\n    private List<String[]> mergerHostDatabase(String[] hostStrings, String[] databases) {\n        List<String[]> mhdList = new ArrayList<>();\n        for (int i = 0; i < hostStrings.length; i++) {\n            String hostString = hostStrings[i];\n            for (int i1 = 0; i1 < databases.length; i1++) {\n                String database = databases[i1];\n                String[] hd = new String[2];\n                hd[0] = hostString;\n                hd[1] = database;\n                mhdList.add(hd);\n            }\n        }\n        return mhdList;\n    }\n\n    private void createDataNode(String dnName, String database, String host) {\n\n        DataNodeConfig conf = new DataNodeConfig(dnName, database, host);\n        if (dataNodes.containsKey(conf.getName())) {\n            throw new ConfigException(\"dataNode \" + conf.getName() + \" duplicated!\");\n        }\n\n        if (!dataHosts.containsKey(host)) {\n            throw new ConfigException(\"dataNode \" + dnName + \" reference dataHost:\" + host + \" not exists!\");\n        }\n\n        dataHosts.get(host).addDataNode(conf.getName());\n        dataNodes.put(conf.getName(), conf);\n    }\n\n    private boolean empty(String dnName) {\n        return dnName == null || dnName.length() == 0;\n    }\n\n    private DBHostConfig createDBHostConf(String dataHost, Element node,\n                                          String dbType, String dbDriver, int maxCon, int minCon, String filters, long logTime) {\n\n        String nodeHost = node.getAttribute(\"host\");\n        String nodeUrl = node.getAttribute(\"url\");\n        String user = node.getAttribute(\"user\");\n        String password = node.getAttribute(\"password\");\n        String usingDecrypt = node.getAttribute(\"usingDecrypt\");\n        String checkAliveText = node.getAttribute(\"checkAlive\");\n        if (checkAliveText == null)checkAliveText = Boolean.TRUE.toString();\n        boolean checkAlive = Boolean.parseBoolean(checkAliveText);\n\n        String passwordEncryty = DecryptUtil.DBHostDecrypt(usingDecrypt, nodeHost, user, password);\n\n        String weightStr = node.getAttribute(\"weight\");\n        int weight = \"\".equals(weightStr) ? PhysicalDBPool.WEIGHT : Integer.parseInt(weightStr);\n\n        String ip = null;\n        int port = 0;\n        if (empty(nodeHost) || empty(nodeUrl) || empty(user)) {\n            throw new ConfigException(\n                    \"dataHost \"\n                            + dataHost\n                            + \" define error,some attributes of this element is empty: \"\n                            + nodeHost);\n        }\n        if (\"native\".equalsIgnoreCase(dbDriver)) {\n            int colonIndex = nodeUrl.indexOf(':');\n            ip = nodeUrl.substring(0, colonIndex).trim();\n            port = Integer.parseInt(nodeUrl.substring(colonIndex + 1).trim());\n        } else {\n            URI url;\n            try {\n                url = new URI(nodeUrl.substring(5));\n            } catch (Exception e) {\n                throw new ConfigException(\"invalid jdbc url \" + nodeUrl + \" of \" + dataHost);\n            }\n            ip = url.getHost();\n            port = url.getPort();\n        }\n\n        DBHostConfig conf = new DBHostConfig(nodeHost, ip, port, nodeUrl, user, passwordEncryty, password,checkAlive);\n        conf.setDbType(dbType);\n        conf.setMaxCon(maxCon);\n        conf.setMinCon(minCon);\n        conf.setFilters(filters);\n        conf.setLogTime(logTime);\n        conf.setWeight(weight);    //新增权重\n        return conf;\n    }\n\n    private void loadDataHosts(Element root) {\n        NodeList list = root.getElementsByTagName(\"dataHost\");\n        for (int i = 0, n = list.getLength(); i < n; ++i) {\n\n            Element element = (Element) list.item(i);\n            String name = element.getAttribute(\"name\");\n            //判断是否重复\n            if (dataHosts.containsKey(name)) {\n                throw new ConfigException(\"dataHost name \" + name + \"duplicated!\");\n            }\n            //读取最大连接数\n            int maxCon = Integer.parseInt(element.getAttribute(\"maxCon\"));\n            //读取最小连接数\n            int minCon = Integer.parseInt(element.getAttribute(\"minCon\"));\n            /**\n             * 读取负载均衡配置\n             * 1. balance=\"0\", 不开启分离机制，所有读操作都发送到当前可用的 writeHost 上。\n             * 2. balance=\"1\"，全部的 readHost 和 stand by writeHost 参不 select 的负载均衡\n             * 3. balance=\"2\"，所有读操作都随机的在 writeHost、readhost 上分发。\n             * 4. balance=\"3\"，所有读请求随机的分发到 wiriterHost 对应的 readhost 执行，writerHost 不负担读压力\n             */\n            int balance = Integer.parseInt(element.getAttribute(\"balance\"));\n            /**\n             * 负载均衡配置\n             * 1. balanceType=0, 随机\n             * 2. balanceType=1，加权轮询\n             * 3. balanceType=2，最少活跃\n             */\n            String balanceTypeStr = element.getAttribute(\"balanceType\");\n            int balanceType = balanceTypeStr.equals(\"\") ? 0 : Integer.parseInt(balanceTypeStr);\n            /**\n             * 读取切换类型\n             * -1 表示不自动切换\n             * 1 默认值，自动切换\n             * 2 基于MySQL主从同步的状态决定是否切换\n             * 心跳询句为 show slave status\n             * 3 基于 MySQL galary cluster 的切换机制\n             */\n            String switchTypeStr = element.getAttribute(\"switchType\");\n            int switchType = switchTypeStr.equals(\"\") ? -1 : Integer.parseInt(switchTypeStr);\n            //读取从延迟界限\n            String slaveThresholdStr = element.getAttribute(\"slaveThreshold\");\n            int slaveThreshold = slaveThresholdStr.equals(\"\") ? -1 : Integer.parseInt(slaveThresholdStr);\n\n            //如果 tempReadHostAvailable 设置大于 0 则表示写主机如果挂掉， 临时的读服务依然可用\n            String tempReadHostAvailableStr = element.getAttribute(\"tempReadHostAvailable\");\n            boolean tempReadHostAvailable = !tempReadHostAvailableStr.equals(\"\") && Integer.parseInt(tempReadHostAvailableStr) > 0;\n            /**\n             * 读取 写类型\n             * 这里只支持 0 - 所有写操作仅配置的第一个 writeHost\n             */\n            String writeTypStr = element.getAttribute(\"writeType\");\n            int writeType = \"\".equals(writeTypStr) ? PhysicalDBPool.WRITE_ONLYONE_NODE : Integer.parseInt(writeTypStr);\n\n\n            String dbDriver = element.getAttribute(\"dbDriver\");\n            String dbType = element.getAttribute(\"dbType\");\n            String filters = element.getAttribute(\"filters\");\n            String logTimeStr = element.getAttribute(\"logTime\");\n            String slaveIDs = element.getAttribute(\"slaveIDs\");\n            String maxRetryCountStr = element.getAttribute(\"maxRetryCount\");\n            int maxRetryCount;\n            if (StringUtil.isEmpty(maxRetryCountStr)) {\n                maxRetryCount = 3;\n            } else {\n                maxRetryCount = Integer.valueOf(maxRetryCountStr);\n            }\n            long logTime = \"\".equals(logTimeStr) ? PhysicalDBPool.LONG_TIME : Long.parseLong(logTimeStr);\n\t\t\t\n            String notSwitch =  element.getAttribute(\"notSwitch\");\n\t\t\tif(StringUtil.isEmpty(notSwitch)) {\n\t\t\t\tnotSwitch = DataHostConfig.CAN_SWITCH_DS;\n\t\t\t}\n            //读取心跳语句\n            String heartbeatSQL = element.getElementsByTagName(\"heartbeat\").item(0).getTextContent();\n            //读取 初始化sql配置,用于oracle\n            NodeList connectionInitSqlList = element.getElementsByTagName(\"connectionInitSql\");\n            String initConSQL = null;\n            if (connectionInitSqlList.getLength() > 0) {\n                initConSQL = connectionInitSqlList.item(0).getTextContent();\n            }\n            //读取writeHost\n            NodeList writeNodes = element.getElementsByTagName(\"writeHost\");\n            DBHostConfig[] writeDbConfs = new DBHostConfig[writeNodes.getLength()];\n            Map<Integer, DBHostConfig[]> readHostsMap = new HashMap<Integer, DBHostConfig[]>(2);\n            Set<String> writeHostNameSet = new HashSet<String>(writeNodes.getLength());\n            for (int w = 0; w < writeDbConfs.length; w++) {\n                Element writeNode = (Element) writeNodes.item(w);\n                writeDbConfs[w] = createDBHostConf(name, writeNode, dbType, dbDriver, maxCon, minCon, filters, logTime);\n                if (writeHostNameSet.contains(writeDbConfs[w].getHostName())) {\n                    throw new ConfigException(\"writeHost \" + writeDbConfs[w].getHostName() + \" duplicated!\");\n                } else {\n                    writeHostNameSet.add(writeDbConfs[w].getHostName());\n                }\n                NodeList readNodes = writeNode.getElementsByTagName(\"readHost\");\n                //读取对应的每一个readHost\n                if (readNodes.getLength() != 0) {\n                    DBHostConfig[] readDbConfs = new DBHostConfig[readNodes.getLength()];\n                    Set<String> readHostNameSet = new HashSet<String>(readNodes.getLength());\n                    for (int r = 0; r < readDbConfs.length; r++) {\n                        Element readNode = (Element) readNodes.item(r);\n                        readDbConfs[r] = createDBHostConf(name, readNode, dbType, dbDriver, maxCon, minCon, filters, logTime);\n                        if (readHostNameSet.contains(readDbConfs[r].getHostName())) {\n                            throw new ConfigException(\"readHost \" + readDbConfs[r].getHostName() + \" duplicated!\");\n                        } else {\n                            readHostNameSet.add(readDbConfs[r].getHostName());\n                        }\n                    }\n                    readHostsMap.put(w, readDbConfs);\n                }\n            }\n\n            DataHostConfig hostConf = new DataHostConfig(name, dbType, dbDriver,\n                    writeDbConfs, readHostsMap, switchType, slaveThreshold, tempReadHostAvailable);\n\n            hostConf.setMaxCon(maxCon);\n            hostConf.setMinCon(minCon);\n            hostConf.setBalance(balance);\n            hostConf.setBalanceType(balanceType);\n            hostConf.setWriteType(writeType);\n            hostConf.setHearbeatSQL(heartbeatSQL);\n            hostConf.setConnectionInitSql(initConSQL);\n            hostConf.setFilters(filters);\n            hostConf.setLogTime(logTime);\n            hostConf.setSlaveIDs(slaveIDs);\n\t\t\thostConf.setNotSwitch(notSwitch);\n            hostConf.setMaxRetryCount(maxRetryCount);\n            dataHosts.put(hostConf.getName(), hostConf);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/xml/XMLServerLoader.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.loader.xml;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\nimport io.mycat.config.Versions;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\nimport com.alibaba.druid.wall.WallConfig;\n\nimport io.mycat.config.model.ClusterConfig;\nimport io.mycat.config.model.FirewallConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.UserConfig;\nimport io.mycat.config.model.UserPrivilegesConfig;\nimport io.mycat.config.util.ConfigException;\nimport io.mycat.config.util.ConfigUtil;\nimport io.mycat.config.util.ParameterMapping;\nimport io.mycat.util.DecryptUtil;\nimport io.mycat.util.SplitUtil;\n\n/**\n * @author mycat\n */\n@SuppressWarnings(\"unchecked\")\npublic class XMLServerLoader {\n    private final SystemConfig system;\n    private final Map<String, UserConfig> users;\n    private final FirewallConfig firewall;\n    private ClusterConfig cluster;\n\n    public XMLServerLoader() {\n        this.system = new SystemConfig();\n        this.users = new HashMap<String, UserConfig>();\n        this.firewall = new FirewallConfig();\n        this.load();\n    }\n\n    public SystemConfig getSystem() {\n        return system;\n    }\n\n    public Map<String, UserConfig> getUsers() {\n        return (Map<String, UserConfig>) (users.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(users));\n    }\n\n    public FirewallConfig getFirewall() {\n        return firewall;\n    }\n\n    public ClusterConfig getCluster() {\n        return cluster;\n    }\n\n    private void load() {\n        //读取server.xml配置\n        InputStream dtd = null;\n        InputStream xml = null;\n        try {\n            dtd = XMLServerLoader.class.getResourceAsStream(\"/server.dtd\");\n            xml = XMLServerLoader.class.getResourceAsStream(\"/server.xml\");\n            Element root = ConfigUtil.getDocument(dtd, xml).getDocumentElement();\n\n            //加载System标签\n            loadSystem(root);\n\n            //加载User标签\n            loadUsers(root);\n\n            //加载集群配置\n            this.cluster = new ClusterConfig(root, system.getServerPort());\n\n            //加载全局SQL防火墙\n            loadFirewall(root);\n        } catch (ConfigException e) {\n            throw e;\n        } catch (Exception e) {\n            throw new ConfigException(e);\n        } finally {\n            if (dtd != null) {\n                try {\n                    dtd.close();\n                } catch (IOException e) {\n                }\n            }\n            if (xml != null) {\n                try {\n                    xml.close();\n                } catch (IOException e) {\n                }\n            }\n        }\n    }\n\n    /**\n     * 初始载入配置获取防火墙配置，配置防火墙方法之一，一共有两处，另一处:\n     * @see  FirewallConfig\n     *\n     * @modification 修改增加网段白名单\n     * @date 2016/12/8\n     * @modifiedBy Hash Zhang\n     */\n    private void loadFirewall(Element root) throws IllegalAccessException, InvocationTargetException {\n        NodeList list = root.getElementsByTagName(\"host\");\n        Map<String, List<UserConfig>> whitehost = new HashMap<>();\n        Map<Pattern, List<UserConfig>> whitehostMask = new HashMap<>();\n\n        for (int i = 0, n = list.getLength(); i < n; i++) {\n            Node node = list.item(i);\n            if (node instanceof Element) {\n                Element e = (Element) node;\n                String hostStr = e.getAttribute(\"host\").trim();\n                String userStr = e.getAttribute(\"user\").trim();\n                String []hosts = hostStr.split(\",\");\n                for (String host : hosts) {\n                    host = host.trim();\n                    if (this.firewall.existsHost(host)) {\n                        throw new ConfigException(\"host duplicated : \" + host);\n                    }\n                }\n                String []users = userStr.split(\",\");\n                List<UserConfig> userConfigs = new ArrayList<UserConfig>();\n                for(String user : users){\n                    user = user.trim();\n                \tUserConfig uc = this.users.get(user);\n                    if (null == uc) {\n                        throw new ConfigException(\"[user: \" + user + \"] doesn't exist in [host: \" + hostStr + \"]\");\n                    }\n                    if (uc.getSchemas() == null || uc.getSchemas().size() == 0) {\n                        throw new ConfigException(\"[host: \" + hostStr + \"] contains one root privileges user: \" + user);\n                    }\n                    userConfigs.add(uc);\n                }\n                for (String host : hosts) {\n                    host = host.trim();\n                    if (host.contains(\"*\") || host.contains(\"%\")) {\n                        whitehostMask.put(FirewallConfig.getMaskPattern(host), userConfigs);\n                    } else {\n                        whitehost.put(host, userConfigs);\n                    }\n                }\n            }\n        }\n\n        firewall.setWhitehost(whitehost);\n        firewall.setWhitehostMask(whitehostMask);\n\n        WallConfig wallConfig = new WallConfig();\n        NodeList blacklist = root.getElementsByTagName(\"blacklist\");\n        for (int i = 0, n = blacklist.getLength(); i < n; i++) {\n            Node node = blacklist.item(i);\n            if (node instanceof Element) {\n            \tElement e = (Element) node;\n             \tString check = e.getAttribute(\"check\");\n             \tif (null != check) {\n             \t\tfirewall.setCheck(Boolean.parseBoolean(check));\n\t\t\t\t}\n\n                Map<String, Object> props = ConfigUtil.loadElements((Element) node);\n                ParameterMapping.mapping(wallConfig, props);\n            }\n        }\n        firewall.setWallConfig(wallConfig);\n        firewall.init();\n\n    }\n\n    private void loadUsers(Element root) {\n        NodeList list = root.getElementsByTagName(\"user\");\n        for (int i = 0, n = list.getLength(); i < n; i++) {\n            Node node = list.item(i);\n            if (node instanceof Element) {\n                Element e = (Element) node;\n                String name = e.getAttribute(\"name\");\n                //huangyiming add  \n                String defaultAccount = e.getAttribute(\"defaultAccount\");\n\n                UserConfig user = new UserConfig();\n                Map<String, Object> props = ConfigUtil.loadElements(e);\n                String password = (String)props.get(\"password\");\n                String usingDecrypt = (String)props.get(\"usingDecrypt\");\n                String passwordDecrypt = DecryptUtil.mycatDecrypt(usingDecrypt,name,password);\n                user.setName(name);\n                user.setDefaultAccount(Boolean.parseBoolean(defaultAccount));\n                user.setDefaultSchema((String)props.get(\"defaultSchema\"));\n                user.setPassword(passwordDecrypt);\n                user.setEncryptPassword(password);\n\n\t\t\t\tString benchmark = (String) props.get(\"benchmark\");\n\t\t\t\tif(null != benchmark) {\n\t\t\t\t\tuser.setBenchmark( Integer.parseInt(benchmark) );\n\t\t\t\t}\n\n\t\t\t\tString readOnly = (String) props.get(\"readOnly\");\n\t\t\t\tif (null != readOnly) {\n\t\t\t\t\tuser.setReadOnly(Boolean.parseBoolean(readOnly));\n\t\t\t\t}\n\n\n\t\t\t\tString schemas = (String) props.get(\"schemas\");\n                if (schemas != null) {\n                    String[] strArray = SplitUtil.split(schemas, ',', true);\n                    user.setSchemas(new HashSet<String>(Arrays.asList(strArray)));\n                }\n\n                //加载用户 DML 权限\n                loadPrivileges(user, e);\n\n                if (users.containsKey(name)) {\n                    throw new ConfigException(\"user \" + name + \" duplicated!\");\n                }\n                users.put(name, user);\n            }\n        }\n    }\n\n    private void loadPrivileges(UserConfig userConfig, Element node) {\n\n    \tUserPrivilegesConfig privilegesConfig = new UserPrivilegesConfig();\n\n    \tNodeList privilegesNodes = node.getElementsByTagName(\"privileges\");\n    \tint privilegesNodesLength = privilegesNodes.getLength();\n\t\tfor (int i = 0; i < privilegesNodesLength; ++i) {\n\t\t\tElement privilegesNode = (Element) privilegesNodes.item(i);\n\t\t\tString check = privilegesNode.getAttribute(\"check\");\n         \tif (null != check) {\n         \t\tprivilegesConfig.setCheck(Boolean.valueOf(check));\n\t\t\t}\n\n\n\t\t\tNodeList schemaNodes = privilegesNode.getElementsByTagName(\"schema\");\n\t\t\tint schemaNodeLength = schemaNodes.getLength();\n\n\t\t\tfor (int j = 0; j < schemaNodeLength; j++ ) {\n\t\t\t\tElement schemaNode = (Element) schemaNodes.item(j);\n\t\t\t\tString name1 = schemaNode.getAttribute(\"name\");\n\t\t\t\tString dml1 = schemaNode.getAttribute(\"dml\");\n\n\t\t\t\tint[] dml1Array = new int[ dml1.length() ];\n\t\t\t\tfor(int offset1 = 0; offset1 < dml1.length(); offset1++ ) {\n\t\t\t\t\tdml1Array[offset1] =  Character.getNumericValue( dml1.charAt( offset1 ) );\n\t\t\t\t}\n\n\t\t\t\tUserPrivilegesConfig.SchemaPrivilege schemaPrivilege = new UserPrivilegesConfig.SchemaPrivilege();\n\t\t\t\tschemaPrivilege.setName( name1 );\n\t\t\t\tschemaPrivilege.setDml( dml1Array );\n\n\t\t\t\tNodeList tableNodes = schemaNode.getElementsByTagName(\"table\");\n\t\t\t\tint tableNodeLength = tableNodes.getLength();\n\t\t\t\tfor (int z = 0; z < tableNodeLength; z++) {\n\n\t\t\t\t\tUserPrivilegesConfig.TablePrivilege tablePrivilege = new UserPrivilegesConfig.TablePrivilege();\n\n\t\t\t\t\tElement tableNode = (Element) tableNodes.item(z);\n\t\t\t\t\tString name2 = tableNode.getAttribute(\"name\");\n\t\t\t\t\tString dml2 = tableNode.getAttribute(\"dml\");\n\n\t\t\t\t\tint[] dml2Array = new int[ dml2.length() ];\n\t\t\t\t\tfor(int offset2 = 0; offset2 < dml2.length(); offset2++ ) {\n\t\t\t\t\t\tdml2Array[offset2] =  Character.getNumericValue( dml2.charAt( offset2 ) );\n\t\t\t\t\t}\n\n\t\t\t\t\ttablePrivilege.setName( name2 );\n\t\t\t\t\ttablePrivilege.setDml( dml2Array );\n\n\t\t\t\t\tschemaPrivilege.addTablePrivilege(name2, tablePrivilege);\n\t\t\t\t}\n\n\t\t\t\tprivilegesConfig.addSchemaPrivilege(name1, schemaPrivilege);\n\t\t\t}\n\n            // 获取 dataNode 权限\n            NodeList dataNodes = privilegesNode.getElementsByTagName(\"dataNode\");\n            int dataNodeLength = dataNodes.getLength();\n\n            for(int k = 0; k < dataNodeLength; k++){\n                UserPrivilegesConfig.DataNodePrivilege dataNodePrivilege = new UserPrivilegesConfig.DataNodePrivilege();\n\n                Element dataNode = (Element) dataNodes.item(k);\n                String dataNodeName = dataNode.getAttribute(\"name\");\n                String dataNodeDml = dataNode.getAttribute(\"dml\");\n\n                int[] dataNodeDmlArray = new int[ dataNodeDml.length() ];\n                for(int offset2 = 0; offset2 < dataNodeDml.length(); offset2++ ) {\n                    dataNodeDmlArray[offset2] =  Character.getNumericValue( dataNodeDml.charAt( offset2 ) );\n                }\n\n                dataNodePrivilege.setName( dataNodeName );\n                dataNodePrivilege.setDml( dataNodeDmlArray );\n\n                privilegesConfig.addDataNodePrivileges(dataNodeName, dataNodePrivilege);\n            }\n\t\t}\n\n\t\tuserConfig.setPrivilegesConfig(privilegesConfig);\n    }\n\n    private void loadSystem(Element root) throws IllegalAccessException, InvocationTargetException {\n        NodeList list = root.getElementsByTagName(\"system\");\n        for (int i = 0, n = list.getLength(); i < n; i++) {\n            Node node = list.item(i);\n            if (node instanceof Element) {\n                Map<String, Object> props = ConfigUtil.loadElements((Element) node);\n                ParameterMapping.mapping(system, props);\n            }\n        }\n\n        if (system.getFakeMySQLVersion() != null) {\n            boolean validVersion = true;\n            String majorMySQLVersion = system.getFakeMySQLVersion();\n            /*\n             * 注意！！！ 目前MySQL官方主版本号仍然是5.x, 以后万一前面的大版本号变成2位数字，\n             * 比如 10.x...,下面获取主版本的代码要做修改\n             */\n//            majorMySQLVersion = majorMySQLVersion.substring(0, majorMySQLVersion.indexOf(\".\", 2));\n//            for (String ver : SystemConfig.MySQLVersions) {\n//                // 这里只是比较mysql前面的大版本号\n//                if (majorMySQLVersion.equals(ver)) {\n//                    validVersion = true;\n//                }\n//            }\n\n            if (validVersion) {\n                Versions.setServerVersion(system.getFakeMySQLVersion());\n            } else {\n                throw new ConfigException(\"The specified MySQL Version (\" + system.getFakeMySQLVersion()\n                        + \") is not valid.\");\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/comm/NotiflyService.java",
    "content": "package io.mycat.config.loader.zkprocess.comm;\n\n/**\n * 通过接口\n * @author liujun\n *\n * @date 2015年2月4日\n * @vsersion 0.0.1\n */\npublic interface NotiflyService {\n\n    /**\n     * 进行通知接口\n     * @throws Exception 异常操作\n     * @return true 通知更新成功，false ，更新失败\n     */\n    public boolean notiflyProcess() throws Exception;\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/comm/ZkConfig.java",
    "content": "package io.mycat.config.loader.zkprocess.comm;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Properties;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.base.Strings;\n\nimport io.mycat.config.loader.zkprocess.zktoxml.ZktoXmlMain;\n\n\n/**\n * 进行zk的配制信息\n* 源文件名：ZkConfig.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class ZkConfig {\n    /**\n     * 日志信息\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(ZkConfig.class);\n\n    private static final String ZK_CONFIG_FILE_NAME = \"/myid.properties\";\n\n    private ZkConfig() {\n    }\n\n    /**\n     * 实例对象信息\n    * @字段说明 ZKCFGINSTANCE\n    */\n    private static ZkConfig ZKCFGINSTANCE = new ZkConfig();\n\n\n    /**\n     * myid的属性文件信息\n    * @字段说明 ZKPROPERTIES\n    */\n    private static Properties ZKPROPERTIES = null;\n\n    static {\n        ZKPROPERTIES = LoadMyidPropersites();\n    }\n\n\n    public String getZkURL()\n    {\n        return ZKPROPERTIES==null?null:ZKPROPERTIES.getProperty(ZkParamCfg.ZK_CFG_URL.getKey())  ;\n    }\n    public void initZk()\n    {\n        try {\n            if (Boolean.parseBoolean(ZKPROPERTIES.getProperty(ZkParamCfg.ZK_CFG_FLAG.getKey()))) {\n                ZktoXmlMain.loadZktoFile();\n            }\n        } catch (Exception e) {\n            LOGGER.error(\"error:\",e);\n        }\n    }\n\n    /**\n     * 获得实例对象信息\n    * 方法描述\n    * @return\n    * @创建日期 2016年9月15日\n    */\n    public  static ZkConfig getInstance() {\n\n        return ZKCFGINSTANCE;\n    }\n\n    /**\n     * 获取myid属性文件中的属性值 \n    * 方法描述\n    * @param param 参数信息\n    * @return\n    * @创建日期 2016年9月15日\n    */\n    public String getValue(ZkParamCfg param) {\n        if (null != param) {\n            return ZKPROPERTIES.getProperty(param.getKey());\n        }\n\n        return null;\n    }\n\n    /**\n     * 加载myid配制文件信息\n    * 方法描述\n    * @return\n    * @创建日期 2016年9月15日\n    */\n    private static Properties LoadMyidPropersites() {\n        Properties pros = new Properties();\n\n        try (InputStream configIS = ZkConfig.class.getResourceAsStream(ZK_CONFIG_FILE_NAME)) {\n            if (configIS == null) {\n                return null;\n            }\n\n            pros.load(configIS);\n        } catch (IOException e) {\n            LOGGER.error(\"ZkConfig LoadMyidPropersites error:\", e);\n            throw new RuntimeException(\"can't find myid properties file : \" + ZK_CONFIG_FILE_NAME);\n        }\n\n        // validate\n        String zkURL = pros.getProperty(ZkParamCfg.ZK_CFG_URL.getKey());\n        String myid = pros.getProperty(ZkParamCfg.ZK_CFG_MYID.getKey());\n\n        String clusterId = pros.getProperty(ZkParamCfg.ZK_CFG_CLUSTERID.getKey());\n\n        if (Strings.isNullOrEmpty(clusterId) ||Strings.isNullOrEmpty(zkURL) || Strings.isNullOrEmpty(myid)) {\n            throw new RuntimeException(\"clusterId and zkURL and myid must not be null or empty!\");\n        }\n        return pros;\n\n    }\n\n    public static void main(String[] args) {\n        String zk = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTERID);\n        System.out.println(zk);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/comm/ZkParamCfg.java",
    "content": "package io.mycat.config.loader.zkprocess.comm;\n\n/**\n * 当前zk的配制参数信息\n* 源文件名：ZkParamCfg.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月17日\n* 修改作者：liujun\n* 修改日期：2016年9月17日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic enum ZkParamCfg {\n\n    /**\n     * zk是否启用标识\n    * @字段说明 ZK_CFG_OPEN\n    */\n    ZK_CFG_FLAG(\"loadZk\"),\n\n    /**\n     * zk配制的url地址信息\n    * @字段说明 ZK_CFG_URL\n    */\n    ZK_CFG_URL(\"zkURL\"),\n\n    /**\n     * 集群的id\n    * @字段说明 ZK_CFG_CLUSTERID\n    */\n    ZK_CFG_CLUSTERID(\"clusterId\"),\n\n    ZK_CFG_CLUSTER_SIZE(\"clusterSize\"),\n\n    /**\n     * 当前mycat节点的id\n    * @字段说明 zk_CFG_MYID\n    */\n    ZK_CFG_MYID(\"myid\"),\n\n\n    MYCAT_SERVER_TYPE(\"type\"),\n\n    MYCAT_BOOSTER_DATAHOSTS(\"boosterDataHosts\"),\n\n    /**\n     * 集群中所有节点的名称信息\n    * @字段说明 ZK_CFG_CLUSTER_NODES\n    */\n    ZK_CFG_CLUSTER_NODES(\"clusterNodes\"),\n\n    /**\n     * 集群中所有节点的名称信息的分隔符\n    * @字段说明 ZK_CFG_CLUSTER_NODES\n    */\n    ZK_CFG_CLUSTER_NODES_SEPARATE(\",\"),\n\n    ;\n\n    private ZkParamCfg(String key) {\n        this.key = key;\n    }\n\n    private String key;\n\n    public String getKey() {\n        return key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/comm/ZookeeperProcessListen.java",
    "content": "package io.mycat.config.loader.zkprocess.comm;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.console.ZkNofiflyCfg;\n\n/**\n * 进行zookeeper操作的监控器器父类信息\n * \n * @author liujun\n * \n * @date 2015年2月4日\n * @vsersion 0.0.1\n */\npublic class ZookeeperProcessListen {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger lOG = LoggerFactory.getLogger(ZookeeperProcessListen.class);\n\n    /**\n     * 所有更新缓存操作的集合\n     */\n    private Map<String, NotiflyService> listenCache = new HashMap<String, NotiflyService>();\n\n    /**\n     * 监控的路径信息\n    * @字段说明 watchPath\n    */\n    private Map<String, Set<String>> watchPathMap = new HashMap<>();\n\n    /**\n     * 监控路径对应的缓存key的对应表\n    * @字段说明 watchToListen\n    */\n    private Map<String, String> watchToListenMap = new HashMap<>();\n\n    /**\n     * 基本路径信息\n    * @字段说明 basePath\n    */\n    private String basePath;\n\n    public String getBasePath() {\n        return basePath;\n    }\n\n    public void setBasePath(String basePath) {\n        this.basePath = basePath;\n    }\n\n    /**\n     * 添加缓存更新操作\n     * \n     * @param key\n     * @param cacheNotiflySercie\n     */\n    public void addListen(String key, NotiflyService cacheNotiflySercie) {\n        listenCache.put(key, cacheNotiflySercie);\n    }\n\n    /**\n     * 专门针对zk设置的监控路径\n    * 方法描述\n    * @param key\n    * @param path\n    * @param cacheNotiflySercie\n    * @创建日期 2016年9月19日\n    */\n    public void watchPath(String key, String path) {\n        Set<String> watchPaths = watchPathMap.get(key);\n\n        if (null == watchPaths) {\n            watchPaths = new HashSet<>();\n        }\n\n        watchPaths.add(path);\n        watchPathMap.put(key, watchPaths);\n    }\n\n    /**\n     * 进行监控路径的转换\n    * 方法描述\n    * @创建日期 2016年9月20日\n    */\n    public void watchToParse() {\n        if (null != watchPathMap && !watchPathMap.isEmpty()) {\n            for (Entry<String, Set<String>> watchPathEntry : watchPathMap.entrySet()) {\n                for (String path : watchPathEntry.getValue()) {\n                    watchToListenMap.put(watchPathEntry.getKey() + ZookeeperPath.ZK_SEPARATOR.getKey() + path,\n                            watchPathEntry.getKey());\n                }\n            }\n        }\n    }\n\n    /**\n     * 返回路径集合\n    * 方法描述\n    * @return\n    * @创建日期 2016年9月19日\n    */\n    public Set<String> getWatchPath() {\n\n        if (watchToListenMap.isEmpty()) {\n            this.watchToParse();\n        }\n\n        return watchToListenMap.keySet();\n    }\n\n    /**\n     * 进行缓存更新通知\n     * \n     * @param key\n     *            缓存模块的key\n     * @return true 当前缓存模块数据更新成功，false，当前缓存数据更新失败\n     */\n    public boolean notifly(String key) {\n        boolean result = false;\n\n        if (null != key && !\"\".equals(key)) {\n\n            // 进行配制加载所有\n            if (ZkNofiflyCfg.ZK_NOTIFLY_LOAD_ALL.getKey().equals(key)) {\n                this.notiflyAll();\n            }\n            // 如果是具体的单独更新，则进行单业务的业务刷新\n            else {\n                String watchListen = watchToListenMap.get(key);\n\n                if (null != watchListen) {\n                    // 取得具体的业务监听信息\n                    NotiflyService cacheService = listenCache.get(watchListen);\n\n                    if (null != cacheService) {\n                        try {\n                            result = cacheService.notiflyProcess();\n                        } catch (Exception e) {\n                            lOG.error(\"ZookeeperProcessListen notifly key :\" + key + \" error:Exception info:\", e);\n                        }\n                    }\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * 进行通知所有缓存进行更新操作\n     */\n    private void notiflyAll() {\n\n        Iterator<Entry<String, NotiflyService>> notiflyIter = listenCache.entrySet().iterator();\n\n        Entry<String, NotiflyService> item = null;\n\n        while (notiflyIter.hasNext()) {\n            item = notiflyIter.next();\n\n            // 进行缓存更新通知操作\n            if (null != item.getValue()) {\n                try {\n                    item.getValue().notiflyProcess();\n                } catch (Exception e) {\n                    lOG.error(\"ZookeeperProcessListen notiflyAll key :\" + item.getKey() + \";value \" + item.getValue()\n                            + \";error:Exception info:\", e);\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/console/ParseParamEnum.java",
    "content": "package io.mycat.config.loader.zkprocess.console;\n\n/**\n * 转换的流程参数配制信息\n* 源文件名：ParseParamEnum.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月18日\n* 修改作者：liujun\n* 修改日期：2016年9月18日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic enum ParseParamEnum {\n\n    /**\n     * mapfile配制的参数名\n    * @字段说明 ZK_PATH_RULE_MAPFILE_NAME\n    */\n    ZK_PATH_RULE_MAPFILE_NAME(\"mapFile\"),\n\n    ;\n\n    /**\n     * 配制的key的信息\n    * @字段说明 key\n    */\n    private String key;\n\n    private ParseParamEnum(String key) {\n        this.key = key;\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/console/ZkNofiflyCfg.java",
    "content": "package io.mycat.config.loader.zkprocess.console;\n\n/**\n * 进行zk通知的参数配制信息\n* 源文件名：ZkNofiflyCfg.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic enum ZkNofiflyCfg {\n\n    /**\n     * 通知更新所有节点的信息\n    * @字段说明 ZK_NOTIFLY_LOAD_ALL\n    */\n    ZK_NOTIFLY_LOAD_ALL(\"all\"),\n    \n    ;\n\n    /**\n     * 配制的key的信息\n    * @字段说明 key\n    */\n    private String key;\n\n    private ZkNofiflyCfg(String key) {\n        this.key = key;\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/Named.java",
    "content": "package io.mycat.config.loader.zkprocess.entity;\n\n/**\n * presentation a object have a filed name.\n */\npublic interface Named {\n    /**\n     * 获得属性的名称\n    * 方法描述\n    * @return\n    * @创建日期 2016年9月15日\n    */\n    String getName();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/Propertied.java",
    "content": "package io.mycat.config.loader.zkprocess.entity;\n\n/**\n * Created by lion on 12/8/15.\n */\npublic interface Propertied {\n    void addProperty(Property property);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/Property.java",
    "content": "package io.mycat.config.loader.zkprocess.entity;\n\nimport java.util.Objects;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlType;\nimport javax.xml.bind.annotation.XmlValue;\n\n/**\n * 键值对信息\n* 源文件名：Property.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月16日\n* 修改作者：liujun\n* 修改日期：2016年9月16日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"Property\")\npublic class Property implements Named {\n\n    @XmlValue\n    protected String value;\n    @XmlAttribute(name = \"name\")\n    protected String name;\n\n    public String getValue() {\n        return value;\n    }\n\n    public Property setValue(String value) {\n        this.value = value;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public Property setName(String value) {\n        this.name = value;\n        return this;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n        Property property = (Property) o;\n        return value.equals(property.value) && name.equals(property.name);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(value, name);\n    }\n\n    @Override\n    public String toString() {\n        return \"Property{\" + \"value='\" + value + '\\'' + \", name='\" + name + '\\'' + '}';\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/Rules.java",
    "content": "package io.mycat.config.loader.zkprocess.entity;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlRootElement;\n\nimport io.mycat.config.loader.zkprocess.entity.rule.function.Function;\nimport io.mycat.config.loader.zkprocess.entity.rule.tablerule.TableRule;\n\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlRootElement(namespace = \"http://io.mycat/\", name = \"rule\")\npublic class Rules {\n\n    /**\n     * 表的路由配制信息\n    * @字段说明 tableRule\n    */\n    protected List<TableRule> tableRule;\n\n    /**\n     * 指定的方法信息\n    * @字段说明 function\n    */\n    protected List<Function> function;\n\n    public List<TableRule> getTableRule() {\n        if (this.tableRule == null) {\n            tableRule = new ArrayList<>();\n        }\n        return tableRule;\n    }\n\n    public void setTableRule(List<TableRule> tableRule) {\n        this.tableRule = tableRule;\n    }\n\n    public List<Function> getFunction() {\n        if (this.function == null) {\n            function = new ArrayList<>();\n        }\n        return function;\n    }\n\n    public void setFunction(List<Function> function) {\n        this.function = function;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"Rules [tableRule=\");\n        builder.append(tableRule);\n        builder.append(\", function=\");\n        builder.append(function);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/Schemas.java",
    "content": "package io.mycat.config.loader.zkprocess.entity;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlRootElement;\n\nimport io.mycat.config.loader.zkprocess.entity.schema.datahost.DataHost;\nimport io.mycat.config.loader.zkprocess.entity.schema.datanode.DataNode;\nimport io.mycat.config.loader.zkprocess.entity.schema.schema.Schema;\n\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlRootElement(namespace = \"http://io.mycat/\", name = \"schema\")\npublic class Schemas {\n    /**\n     * 配制的逻辑表信息\n    * @字段说明 schema\n    */\n    private List<Schema> schema;\n\n    /**\n     * 配制的表对应的数据库信息\n    * @字段说明 dataNode\n    */\n    private List<DataNode> dataNode;\n\n    /**\n     * 用于指定数据信息\n    * @字段说明 dataHost\n    */\n    private List<DataHost> dataHost;\n\n    public List<Schema> getSchema() {\n        if (this.schema == null) {\n            schema = new ArrayList<>();\n        }\n        return schema;\n    }\n\n    public void setSchema(List<Schema> schema) {\n        this.schema = schema;\n    }\n\n    public List<DataNode> getDataNode() {\n        if (this.dataNode == null) {\n            dataNode = new ArrayList<>();\n        }\n        return dataNode;\n    }\n\n    public void setDataNode(List<DataNode> dataNode) {\n        this.dataNode = dataNode;\n    }\n\n    public List<DataHost> getDataHost() {\n        if (this.dataHost == null) {\n            dataHost = new ArrayList<>();\n        }\n        return dataHost;\n    }\n\n    public void setDataHost(List<DataHost> dataHost) {\n        this.dataHost = dataHost;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"Schemas [schema=\");\n        builder.append(schema);\n        builder.append(\", dataNode=\");\n        builder.append(dataNode);\n        builder.append(\", dataHost=\");\n        builder.append(dataHost);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/Server.java",
    "content": "package io.mycat.config.loader.zkprocess.entity;\n\nimport java.util.List;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlElement;\nimport javax.xml.bind.annotation.XmlRootElement;\n\nimport io.mycat.config.loader.zkprocess.entity.server.System;\nimport io.mycat.config.loader.zkprocess.entity.server.user.User;\n\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlRootElement(namespace = \"http://io.mycat/\", name = \"server\")\npublic class Server {\n\n    @XmlElement(required = true)\n    protected System system;\n\n    @XmlElement(required = true)\n    protected List<User> user;\n\n    public System getSystem() {\n        return system;\n    }\n\n    public void setSystem(System value) {\n        this.system = value;\n    }\n\n    public List<User> getUser() {\n        return user;\n    }\n\n    public void setUser(List<User> user) {\n        this.user = user;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"Server [system=\");\n        builder.append(system);\n        builder.append(\", user=\");\n        builder.append(user);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/cache/CacheInfo.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.cache;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlRootElement;\n\n/**\n * 缓存配制信息\n* 源文件名：CacheInfo.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月19日\n* 修改作者：liujun\n* 修改日期：2016年9月19日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.NONE)\n@XmlRootElement(name = \"defaultCache\")\npublic class CacheInfo {\n\n    /**\n     * maxElementsInMemory:在内存中最大的对象数量 \n    * @字段说明 maxEntriesLocalHeap\n    */\n    @XmlAttribute\n    private int maxElementsInMemory;\n\n    /**\n     *   eternal：设置元素是否永久的，如果为永久，则timeout忽略 \n    * @字段说明 maxBytesLocalDisk\n    */\n    @XmlAttribute\n    private boolean eternal;\n\n    /**\n     *   overflowToDisk：是否当memory中的数量达到限制后，保存到Disk \n    * @字段说明 updateCheck\n    */\n    @XmlAttribute\n    private boolean overflowToDisk;\n\n    /**\n     * diskSpoolBufferSizeMB：这个参数设置DiskStore（磁盘缓存）的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 \n    * @字段说明 diskSpoolBufferSizeMB\n    */\n    @XmlAttribute\n    private int diskSpoolBufferSizeMB;\n\n    /**\n     *  maxElementsOnDisk：硬盘最大缓存个数。\n    * @字段说明 maxElementsOnDisk\n    */\n    @XmlAttribute\n    private int maxElementsOnDisk;\n\n    /**\n     *  diskPersistent：是否缓存虚拟机重启期数据 \n    * @字段说明 diskPersistent\n    */\n    @XmlAttribute\n    private boolean diskPersistent;\n\n    /**\n     * diskExpiryThreadIntervalSeconds：磁盘失效线程运行时间间隔，默认是120秒。 \n    * @字段说明 diskExpiryThreadIntervalSeconds\n    */\n    @XmlAttribute\n    private int diskExpiryThreadIntervalSeconds;\n\n    /**\n     *  memoryStoreEvictionPolicy：当达到maxElementsInMemory限制时，\n     *  Ehcache将会根据指定的策略去清理内存。默认策略是LRU（最近最少使用）。\n     *  你可以设置为FIFO（先进先出）或是LFU（较少使用）。    \n    * @字段说明 memoryStoreEvictionPolicy\n    */\n    @XmlAttribute\n    private String memoryStoreEvictionPolicy;\n\n    public int getMaxElementsInMemory() {\n        return maxElementsInMemory;\n    }\n\n    public void setMaxElementsInMemory(int maxElementsInMemory) {\n        this.maxElementsInMemory = maxElementsInMemory;\n    }\n\n    public boolean isEternal() {\n        return eternal;\n    }\n\n    public void setEternal(boolean eternal) {\n        this.eternal = eternal;\n    }\n\n    public boolean isOverflowToDisk() {\n        return overflowToDisk;\n    }\n\n    public void setOverflowToDisk(boolean overflowToDisk) {\n        this.overflowToDisk = overflowToDisk;\n    }\n\n    public int getDiskSpoolBufferSizeMB() {\n        return diskSpoolBufferSizeMB;\n    }\n\n    public void setDiskSpoolBufferSizeMB(int diskSpoolBufferSizeMB) {\n        this.diskSpoolBufferSizeMB = diskSpoolBufferSizeMB;\n    }\n\n    public int getMaxElementsOnDisk() {\n        return maxElementsOnDisk;\n    }\n\n    public void setMaxElementsOnDisk(int maxElementsOnDisk) {\n        this.maxElementsOnDisk = maxElementsOnDisk;\n    }\n\n    public boolean isDiskPersistent() {\n        return diskPersistent;\n    }\n\n    public void setDiskPersistent(boolean diskPersistent) {\n        this.diskPersistent = diskPersistent;\n    }\n\n    public int getDiskExpiryThreadIntervalSeconds() {\n        return diskExpiryThreadIntervalSeconds;\n    }\n\n    public void setDiskExpiryThreadIntervalSeconds(int diskExpiryThreadIntervalSeconds) {\n        this.diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;\n    }\n\n    public String getMemoryStoreEvictionPolicy() {\n        return memoryStoreEvictionPolicy;\n    }\n\n    public void setMemoryStoreEvictionPolicy(String memoryStoreEvictionPolicy) {\n        this.memoryStoreEvictionPolicy = memoryStoreEvictionPolicy;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"CacheInfo [maxElementsInMemory=\");\n        builder.append(maxElementsInMemory);\n        builder.append(\", eternal=\");\n        builder.append(eternal);\n        builder.append(\", overflowToDisk=\");\n        builder.append(overflowToDisk);\n        builder.append(\", diskSpoolBufferSizeMB=\");\n        builder.append(diskSpoolBufferSizeMB);\n        builder.append(\", maxElementsOnDisk=\");\n        builder.append(maxElementsOnDisk);\n        builder.append(\", diskPersistent=\");\n        builder.append(diskPersistent);\n        builder.append(\", diskExpiryThreadIntervalSeconds=\");\n        builder.append(diskExpiryThreadIntervalSeconds);\n        builder.append(\", memoryStoreEvictionPolicy=\");\n        builder.append(memoryStoreEvictionPolicy);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/cache/Ehcache.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.cache;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlElement;\nimport javax.xml.bind.annotation.XmlRootElement;\n\n/**\n * ehcache配制信息\n* 源文件名：Ehcache.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月19日\n* 修改作者：liujun\n* 修改日期：2016年9月19日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlRootElement(name = \"ehcache\")\npublic class Ehcache {\n\n    /**\n     * \n    * @字段说明 maxEntriesLocalHeap\n    */\n    @XmlAttribute\n    private int maxEntriesLocalHeap;\n\n    /**\n    * @字段说明 maxBytesLocalDisk\n    */\n    @XmlAttribute\n    private String maxBytesLocalDisk;\n\n    /**\n    * @字段说明 updateCheck\n    */\n    @XmlAttribute\n    private boolean updateCheck;\n\n    /**\n     * 缓存信息\n    * @字段说明 defaultCache\n    */\n    @XmlElement\n    private CacheInfo defaultCache;\n\n    public int getMaxEntriesLocalHeap() {\n        return maxEntriesLocalHeap;\n    }\n\n    public void setMaxEntriesLocalHeap(int maxEntriesLocalHeap) {\n        this.maxEntriesLocalHeap = maxEntriesLocalHeap;\n    }\n\n    public String getMaxBytesLocalDisk() {\n        return maxBytesLocalDisk;\n    }\n\n    public void setMaxBytesLocalDisk(String maxBytesLocalDisk) {\n        this.maxBytesLocalDisk = maxBytesLocalDisk;\n    }\n\n    public boolean isUpdateCheck() {\n        return updateCheck;\n    }\n\n    public void setUpdateCheck(boolean updateCheck) {\n        this.updateCheck = updateCheck;\n    }\n\n    public CacheInfo getDefaultCache() {\n        return defaultCache;\n    }\n\n    public void setDefaultCache(CacheInfo defaultCache) {\n        this.defaultCache = defaultCache;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"Ehcache [maxEntriesLocalHeap=\");\n        builder.append(maxEntriesLocalHeap);\n        builder.append(\", maxBytesLocalDisk=\");\n        builder.append(maxBytesLocalDisk);\n        builder.append(\", updateCheck=\");\n        builder.append(updateCheck);\n        builder.append(\", defaultCache=\");\n        builder.append(defaultCache);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/package-info.java",
    "content": "@XmlSchema(xmlns = @XmlNs(prefix = \"mycat\", namespaceURI = \"http://io.mycat/\") , elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)\n\npackage io.mycat.config.loader.zkprocess.entity;\n\nimport javax.xml.bind.annotation.XmlNs;\nimport javax.xml.bind.annotation.XmlSchema;\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/rule/function/Function.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.rule.function;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlType;\n\nimport io.mycat.config.loader.zkprocess.entity.Named;\nimport io.mycat.config.loader.zkprocess.entity.Propertied;\nimport io.mycat.config.loader.zkprocess.entity.Property;\n\n/**\n *<function name=\"mod-long\" class=\"io.mycat.route.function.PartitionByMod\">\n * * <property name=\"count\">3</property>\n *</function>\n* 源文件名：Function.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月18日\n* 修改作者：liujun\n* 修改日期：2016年9月18日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"function\")\npublic class Function  implements Propertied, Named  {\n    \n\n    @XmlAttribute(required = true)\n    protected String name;\n\n    @XmlAttribute(required = true, name = \"class\")\n    protected String clazz;\n\n    protected List<Property> property;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getClazz() {\n        return clazz;\n    }\n\n    public void setClazz(String clazz) {\n        this.clazz = clazz;\n    }\n\n    public List<Property> getProperty() {\n        if (this.property == null) {\n            property = new ArrayList<>();\n        }\n        return property;\n    }\n\n    public void setProperty(List<Property> property) {\n        this.property = property;\n    }\n\n    @Override\n    public void addProperty(Property property) {\n        this.getProperty().add(property);\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"Function [name=\");\n        builder.append(name);\n        builder.append(\", clazz=\");\n        builder.append(clazz);\n        builder.append(\", property=\");\n        builder.append(property);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n    \n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/rule/tablerule/Rule.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.rule.tablerule;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlType;\n\n\n/**\n * *<rule>\n * * *<columns>id</columns>\n * * *<algorithm>func1</algorithm>\n * *</rule>\n* 源文件名：Rule.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月18日\n* 修改作者：liujun\n* 修改日期：2016年9月18日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"rule\", propOrder = { \"columns\", \"algorithm\" })\npublic class Rule {\n\n    protected String columns;\n    protected String algorithm;\n\n    public String getColumns() {\n        return columns;\n    }\n\n    public Rule setColumns(String columns) {\n        this.columns = columns;\n        return this;\n    }\n\n    public String getAlgorithm() {\n        return algorithm;\n    }\n\n    public Rule setAlgorithm(String algorithm) {\n        this.algorithm = algorithm;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"Rule [columns=\");\n        builder.append(columns);\n        builder.append(\", algorithm=\");\n        builder.append(algorithm);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n    \n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/rule/tablerule/TableRule.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.rule.tablerule;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlElement;\nimport javax.xml.bind.annotation.XmlType;\n\nimport io.mycat.config.loader.zkprocess.entity.Named;\n\n/**\n * * <tableRule name=\"rule1\">\n * * *<rule>\n * * * *<columns>id</columns>\n * * * *<algorithm>func1</algorithm>\n * * </rule>\n * </tableRule>\n* 源文件名：TableRule.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月18日\n* 修改作者：liujun\n* 修改日期：2016年9月18日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"tableRule\")\npublic class TableRule implements Named {\n\n    @XmlElement(required = true, name = \"rule\")\n    protected Rule rule;\n    \n    @XmlAttribute(required = true)\n    protected String name;\n\n    public Rule getRule() {\n        return rule;\n    }\n\n    public TableRule setRule(Rule rule) {\n        this.rule = rule;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public TableRule setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"TableRule [rule=\");\n        builder.append(rule);\n        builder.append(\", name=\");\n        builder.append(name);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n    \n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/schema/datahost/DataHost.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.schema.datahost;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlType;\n\nimport io.mycat.config.loader.zkprocess.entity.Named;\n\n/**\n * <dataHost name=\"localhost1\" maxCon=\"1000\" minCon=\"10\" balance=\"0\"\n     * writeType=\"0\" dbType=\"mysql\" dbDriver=\"native\" switchType=\"1\"  slaveThreshold=\"100\">\n     * </dataHost>\n* 源文件名：DataHost.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"dataHost\")\npublic class DataHost implements Named {\n\n    @XmlAttribute(required = true)\n    protected Integer balance;\n    @XmlAttribute(required = true)\n    protected Integer balanceType;\n    @XmlAttribute(required = true)\n    protected Integer maxCon;\n    @XmlAttribute(required = true)\n    protected Integer minCon;\n    @XmlAttribute(required = true)\n    protected String name;\n    @XmlAttribute\n    protected Integer writeType;\n    @XmlAttribute\n    protected Integer switchType;\n    @XmlAttribute\n    protected Integer slaveThreshold;\n    @XmlAttribute(required = true)\n    protected String dbType;\n    @XmlAttribute(required = true)\n    protected String dbDriver;\n\n    @XmlAttribute()\n    protected String slaveIDs;\n\n    @XmlAttribute(required = true)\n    protected String maxRetryCount;\n    \n    protected String heartbeat;\n    protected String connectionInitSql;\n\n    protected List<WriteHost> writeHost;\n\n    public String getMaxRetryCount() {\n\t\treturn maxRetryCount;\n\t}\n\n\tpublic void setMaxRetryCount(String maxRetryCount) {\n\t\tthis.maxRetryCount = maxRetryCount;\n\t}\n\n\tpublic String getHeartbeat() {\n        return heartbeat;\n    }\n\n    public void setHeartbeat(String heartbeat) {\n        this.heartbeat = heartbeat;\n    }\n\n    public String getConnectionInitSql() {\n        return connectionInitSql;\n    }\n\n    public void setConnectionInitSql(String connectionInitSql) {\n        this.connectionInitSql = connectionInitSql;\n    }\n\n    public List<WriteHost> getWriteHost() {\n        if (this.writeHost == null) {\n            writeHost = new ArrayList<>();\n        }\n        return writeHost;\n    }\n\n    public String getSlaveIDs() {\n        return slaveIDs;\n    }\n\n    public void setSlaveIDs(String slaveIDs) {\n        this.slaveIDs = slaveIDs;\n    }\n\n    public void setWriteHost(List<WriteHost> writeHost) {\n        this.writeHost = writeHost;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Integer getMaxCon() {\n        return maxCon;\n    }\n\n    public void setMaxCon(Integer maxCon) {\n        this.maxCon = maxCon;\n    }\n\n    public Integer getMinCon() {\n        return minCon;\n    }\n\n    public void setMinCon(Integer minCon) {\n        this.minCon = minCon;\n    }\n\n    public Integer getBalance() {\n        return balance;\n    }\n\n    public void setBalance(Integer balance) {\n        this.balance = balance;\n    }\n\n    public Integer getBalanceType() {\n        return balanceType;\n    }\n\n    public void setBalanceType(Integer balanceType) {\n        this.balanceType = balanceType;\n    }\n\n    public String getDbType() {\n        return dbType;\n    }\n\n    public void setDbType(String dbType) {\n        this.dbType = dbType;\n    }\n\n    public String getDbDriver() {\n        return dbDriver;\n    }\n\n    public void setDbDriver(String dbDriver) {\n        this.dbDriver = dbDriver;\n    }\n\n    public Integer getWriteType() {\n        return writeType;\n    }\n\n    public void setWriteType(Integer writeType) {\n        this.writeType = writeType;\n    }\n\n    public Integer getSwitchType() {\n        return switchType;\n    }\n\n    public void setSwitchType(Integer switchType) {\n        this.switchType = switchType;\n    }\n\n    public Integer getSlaveThreshold() {\n        return slaveThreshold;\n    }\n\n    public void setSlaveThreshold(Integer slaveThreshold) {\n        this.slaveThreshold = slaveThreshold;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"DataHost [balance=\");\n        builder.append(balance);\n        builder.append(\", balanceType=\");\n        builder.append(balanceType);\n        builder.append(\", maxCon=\");\n        builder.append(maxCon);\n        builder.append(\", minCon=\");\n        builder.append(minCon);\n        builder.append(\", name=\");\n        builder.append(name);\n        builder.append(\", writeType=\");\n        builder.append(writeType);\n        builder.append(\", switchType=\");\n        builder.append(switchType);\n        builder.append(\", slaveThreshold=\");\n        builder.append(slaveThreshold);\n        builder.append(\", dbType=\");\n        builder.append(dbType);\n        builder.append(\", dbDriver=\");\n        builder.append(dbDriver);\n        builder.append(\", heartbeat=\");\n        builder.append(heartbeat);\n        builder.append(\", connectionInitSql=\");\n        builder.append(connectionInitSql);\n        builder.append(\", writeHost=\");\n        builder.append(writeHost);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/schema/datahost/ReadHost.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.schema.datahost;\n\nimport java.util.List;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlTransient;\nimport javax.xml.bind.annotation.XmlType;\n\n/**\n * <readHost host=\"\" url=\"\" password=\"\" user=\"\"></readHost>\n* 源文件名：ReadHost.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"readHost\")\npublic class ReadHost extends WriteHost {\n\n    @XmlAttribute\n    protected String weight;\n\n    public String getWeight() {\n        return weight;\n    }\n\n    public void setWeight(String weight) {\n        this.weight = weight;\n    }\n\n    @XmlTransient\n    @Override\n    public List<ReadHost> getReadHost() {\n        return super.getReadHost();\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"ReadHost [weight=\");\n        builder.append(weight);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/schema/datahost/WriteHost.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.schema.datahost;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlType;\n\n/**\n * <readHost host=\"\" url=\"\" password=\"\" user=\"\"></readHost>\n* 源文件名：WriteHost.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"writeHost\")\npublic class WriteHost {\n\n    @XmlAttribute(required = true)\n    protected String host;\n    @XmlAttribute(required = true)\n    protected String url;\n    @XmlAttribute(required = true)\n    protected String password;\n    @XmlAttribute(required = true)\n    protected String user;\n    @XmlAttribute\n    protected Boolean usingDecrypt;\n\n    @XmlAttribute\n    protected Boolean checkAlive;\n\n    private List<ReadHost> readHost;\n\n    public String getHost() {\n        return host;\n    }\n\n    public void setHost(String host) {\n        this.host = host;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public String getUser() {\n        return user;\n    }\n\n    public void setUser(String user) {\n        this.user = user;\n    }\n\n    public Boolean isUsingDecrypt() {\n        return usingDecrypt;\n    }\n\n    public void setUsingDecrypt(Boolean usingDecrypt) {\n        this.usingDecrypt = usingDecrypt;\n    }\n\n    public List<ReadHost> getReadHost() {\n        if (this.readHost == null) {\n            readHost = new ArrayList<>();\n        }\n        return readHost;\n    }\n\n    public void setReadHost(List<ReadHost> readHost) {\n        this.readHost = readHost;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"WriteHost [host=\");\n        builder.append(host);\n        builder.append(\", url=\");\n        builder.append(url);\n        builder.append(\", password=\");\n        builder.append(password);\n        builder.append(\", user=\");\n        builder.append(user);\n        builder.append(\", usingDecrypt=\");\n        builder.append(usingDecrypt);\n        builder.append(\", readHost=\");\n        builder.append(readHost);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/schema/datanode/DataNode.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.schema.datanode;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlType;\n\nimport io.mycat.config.loader.zkprocess.entity.Named;\n\n/**\n * <dataNode name=\"dn1\" dataHost=\"localhost1\" database=\"db1\" />\n* 源文件名：DataNode.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"dataNode\")\npublic class DataNode implements Named {\n\n    @XmlAttribute(required = true)\n    private String name;\n\n    @XmlAttribute(required = true)\n    private String dataHost;\n\n    @XmlAttribute(required = true)\n    private String database;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getDataHost() {\n        return dataHost;\n    }\n\n    public void setDataHost(String dataHost) {\n        this.dataHost = dataHost;\n    }\n\n    public String getDatabase() {\n        return database;\n    }\n\n    public void setDatabase(String database) {\n        this.database = database;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"DataNode [name=\");\n        builder.append(name);\n        builder.append(\", dataHost=\");\n        builder.append(dataHost);\n        builder.append(\", database=\");\n        builder.append(database);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/schema/schema/ChildTable.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.schema.schema;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlType;\n\nimport io.mycat.config.loader.zkprocess.entity.Named;\n\n/**\n * \n * <childTable name=\"order_items\" joinKey=\"order_id\" parentKey=\"id\" />\n * 配制子表信息\n* 源文件名：ChildTable.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"childTable\")\npublic class ChildTable implements Named {\n\n    @XmlAttribute(required = true)\n    protected String name;\n    @XmlAttribute(required = true)\n    protected String joinKey;\n    @XmlAttribute(required = true)\n    protected String parentKey;\n    @XmlAttribute\n    protected String primaryKey;\n    @XmlAttribute\n    protected Boolean autoIncrement;\n\n    protected List<ChildTable> childTable;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getJoinKey() {\n        return joinKey;\n    }\n\n    public void setJoinKey(String joinKey) {\n        this.joinKey = joinKey;\n    }\n\n    public String getParentKey() {\n        return parentKey;\n    }\n\n    public void setParentKey(String parentKey) {\n        this.parentKey = parentKey;\n    }\n\n    public String getPrimaryKey() {\n        return primaryKey;\n    }\n\n    public void setPrimaryKey(String primaryKey) {\n        this.primaryKey = primaryKey;\n    }\n\n    public Boolean isAutoIncrement() {\n        return autoIncrement;\n    }\n\n    public void setAutoIncrement(Boolean autoIncrement) {\n        this.autoIncrement = autoIncrement;\n    }\n\n    public List<ChildTable> getChildTable() {\n        if (this.childTable == null) {\n            childTable = new ArrayList<>();\n        }\n        return childTable;\n    }\n\n    public void setChildTable(List<ChildTable> childTable) {\n        this.childTable = childTable;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"ChildTable [name=\");\n        builder.append(name);\n        builder.append(\", joinKey=\");\n        builder.append(joinKey);\n        builder.append(\", parentKey=\");\n        builder.append(parentKey);\n        builder.append(\", primaryKey=\");\n        builder.append(primaryKey);\n        builder.append(\", autoIncrement=\");\n        builder.append(autoIncrement);\n        builder.append(\", childTable=\");\n        builder.append(childTable);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/schema/schema/Schema.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.schema.schema;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlType;\n\nimport io.mycat.config.loader.zkprocess.entity.Named;\n\n/**\n * <schema name=\"TESTDB\" checkSQLschema=\"false\" sqlMaxLimit=\"100\">\n * * <table name=\"travelrecord\" dataNode=\"dn1,dn2,dn3\" rule=\"auto-sharding-long\" />\n * *\n * </schema>\n * \n* 源文件名：Schema.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"schema\")\npublic class Schema implements Named {\n\n    /**\n     * schema的名称\n    * @字段说明 name\n    */\n    @XmlAttribute(required = true)\n    protected String name;\n\n    /**\n     * 当诠值讴置为 true 时，\n     * 如果我们执行询句**select * from TESTDB.travelrecord;\n     * **则MyCat会把询句修改为**select * from travelrecord;**\n    * @字段说明 checkSQLschema\n    */\n    @XmlAttribute\n    protected Boolean checkSQLschema;\n\n    /**\n     * 当诠值设置为某个数值时。每条执行癿SQL询句，如果没有加上limit询句，MyCat也会自劢癿加上所对应癿\n    * @字段说明 sqlMaxLimit\n    */\n    @XmlAttribute\n    protected Integer sqlMaxLimit;\n\n    /**\n     * 诠属性用二绊定逡辑库刡某个具体癿database上，\n     * 1.3版本如果配置了dataNode，则不可以配置分片表，\n     * 1.4可以配置默讣分片，叧雹要配置需要分片的表即可\n    * @字段说明 dataNode\n    */\n    @XmlAttribute\n    protected String dataNode;\n\n    /**\n     * 配制表信息\n    * @字段说明 table\n    */\n    protected List<Table> table;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Boolean isCheckSQLschema() {\n        return checkSQLschema;\n    }\n\n    public void setCheckSQLschema(Boolean checkSQLschema) {\n        this.checkSQLschema = checkSQLschema;\n    }\n\n    public Integer getSqlMaxLimit() {\n        return sqlMaxLimit;\n    }\n\n    public void setSqlMaxLimit(Integer sqlMaxLimit) {\n        this.sqlMaxLimit = sqlMaxLimit;\n    }\n\n    public String getDataNode() {\n        return dataNode;\n    }\n\n    public void setDataNode(String dataNode) {\n        this.dataNode = dataNode;\n    }\n\n    public List<Table> getTable() {\n        if (this.table == null) {\n            table = new ArrayList<>();\n        }\n        return table;\n    }\n\n    public void setTable(List<Table> table) {\n        this.table = table;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"Schema [name=\");\n        builder.append(name);\n        builder.append(\", checkSQLschema=\");\n        builder.append(checkSQLschema);\n        builder.append(\", sqlMaxLimit=\");\n        builder.append(sqlMaxLimit);\n        builder.append(\", dataNode=\");\n        builder.append(dataNode);\n        builder.append(\", table=\");\n        builder.append(table);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/schema/schema/Table.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.schema.schema;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlType;\n\nimport io.mycat.config.loader.zkprocess.entity.Named;\n\n/**\n * <table name=\"travelrecord\" dataNode=\"dn1,dn2,dn3\" rule=\"auto-sharding-long\" />\n * 用于具体的表信息\n* 源文件名：Table.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"table\")\npublic class Table implements Named {\n\n    @XmlAttribute(required = true)\n    protected String name;\n    @XmlAttribute\n    protected String nameSuffix;\n    @XmlAttribute(required = true)\n    protected String dataNode;\n    @XmlAttribute\n    protected String rule;\n    @XmlAttribute\n    protected Boolean ruleRequired;\n    @XmlAttribute\n    protected String primaryKey;\n    @XmlAttribute\n    protected Boolean autoIncrement;\n    @XmlAttribute\n    protected Boolean needAddLimit;\n    @XmlAttribute\n    protected String type;\n\n    @XmlAttribute\n    protected String subTables;\n    /**\n     * 子节点信息\n    * @字段说明 childTable\n    */\n    protected List<ChildTable> childTable;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getDataNode() {\n        return dataNode;\n    }\n\n    public void setDataNode(String dataNode) {\n        this.dataNode = dataNode;\n    }\n\n    public String getRule() {\n        return rule;\n    }\n\n    public void setRule(String rule) {\n        this.rule = rule;\n    }\n\n    public List<ChildTable> getChildTable() {\n        if (this.childTable == null) {\n            childTable = new ArrayList<>();\n        }\n        return childTable;\n    }\n\n    public void setChildTable(List<ChildTable> childTable) {\n        this.childTable = childTable;\n    }\n\n    public String getNameSuffix() {\n        return nameSuffix;\n    }\n\n    public void setNameSuffix(String nameSuffix) {\n        this.nameSuffix = nameSuffix;\n    }\n\n    public Boolean isRuleRequired() {\n        return ruleRequired;\n    }\n\n    public void setRuleRequired(Boolean ruleRequired) {\n        this.ruleRequired = ruleRequired;\n    }\n\n    public String getPrimaryKey() {\n        return primaryKey;\n    }\n\n    public void setPrimaryKey(String primaryKey) {\n        this.primaryKey = primaryKey;\n    }\n\n    public Boolean isAutoIncrement() {\n        return autoIncrement;\n    }\n\n    public void setAutoIncrement(Boolean autoIncrement) {\n        this.autoIncrement = autoIncrement;\n    }\n\n    public Boolean isNeedAddLimit() {\n        return needAddLimit;\n    }\n\n    public void setNeedAddLimit(Boolean needAddLimit) {\n        this.needAddLimit = needAddLimit;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n    \n    \n    public String getSubTables() {\n\t\treturn subTables;\n\t}\n\n\tpublic void setSubTables(String subTables) {\n\t\tthis.subTables = subTables;\n\t}\n\n\t@Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"Table [name=\");\n        builder.append(name);\n        builder.append(\", nameSuffix=\");\n        builder.append(nameSuffix);\n        builder.append(\", dataNode=\");\n        builder.append(dataNode);\n        builder.append(\", rule=\");\n        builder.append(rule);\n        builder.append(\", ruleRequired=\");\n        builder.append(ruleRequired);\n        builder.append(\", primaryKey=\");\n        builder.append(primaryKey);\n        builder.append(\", autoIncrement=\");\n        builder.append(autoIncrement);\n        builder.append(\", needAddLimit=\");\n        builder.append(needAddLimit);\n        builder.append(\", type=\");\n        builder.append(type);\n        builder.append(\", childTable=\");\n        builder.append(childTable);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/server/System.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.server;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlType;\n\nimport io.mycat.config.loader.zkprocess.entity.Propertied;\nimport io.mycat.config.loader.zkprocess.entity.Property;\n\n/**\n * 系统信息\n* 源文件名：System.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月16日\n* 修改作者：liujun\n* 修改日期：2016年9月16日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"system\")\npublic class System implements Propertied {\n\n    protected List<Property> property;\n\n    public List<Property> getProperty() {\n        if (this.property == null) {\n            property = new ArrayList<>();\n        }\n        return property;\n    }\n\n    public void setProperty(List<Property> property) {\n        this.property = property;\n    }\n\n    @Override\n    public void addProperty(Property property) {\n        this.getProperty().add(property);\n    }\n\n    /**\n     * 设置最新的方法值\n    * 方法描述\n    * @param newSet\n    * @创建日期 2016年9月17日\n    */\n    public void setNewValue(System newSet) {\n        if (null != newSet) {\n            List<Property> valuePro = newSet.getProperty();\n            // 最新设置的属性值\n            for (Property netsetProper : valuePro) {\n                // 当前已经设置的属性值\n                for (Property property : this.getProperty()) {\n                    // 如果新设置的属性名称与当前的已经存在的名称相同，则设置为新值\n                    if (netsetProper.getName().equals(property.getName())) {\n                        property.setValue(netsetProper.getValue());\n                    }\n                }\n            }\n        }\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"System [property=\");\n        builder.append(property);\n        builder.append(\"]\");\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/entity/server/user/User.java",
    "content": "package io.mycat.config.loader.zkprocess.entity.server.user;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.xml.bind.annotation.XmlAccessType;\nimport javax.xml.bind.annotation.XmlAccessorType;\nimport javax.xml.bind.annotation.XmlAttribute;\nimport javax.xml.bind.annotation.XmlType;\n\nimport io.mycat.config.loader.zkprocess.entity.Named;\nimport io.mycat.config.loader.zkprocess.entity.Propertied;\nimport io.mycat.config.loader.zkprocess.entity.Property;\n\n@XmlAccessorType(XmlAccessType.FIELD)\n@XmlType(name = \"user\")\npublic class User implements Propertied, Named {\n\n    @XmlAttribute(required = true)\n    protected String name;\n\n    protected List<Property> property;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public List<Property> getProperty() {\n        if (this.property == null) {\n            property = new ArrayList<>();\n        }\n        return property;\n    }\n\n    public void setProperty(List<Property> property) {\n        this.property = property;\n    }\n\n    @Override\n    public void addProperty(Property property) {\n        this.getProperty().add(property);\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" + \"name='\" + name + '\\'' + \", property=\" + property + '}';\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/JsonProcessBase.java",
    "content": "package io.mycat.config.loader.zkprocess.parse;\n\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.google.gson.Gson;\nimport com.google.gson.reflect.TypeToken;\n\nimport io.mycat.config.loader.zkprocess.entity.Schemas;\nimport io.mycat.config.loader.zkprocess.entity.schema.datanode.DataNode;\n\n/**\n * json数据与实体类的类的信息 \n* 源文件名：XmlProcessBase.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class JsonProcessBase {\n\n    /**\n     * 进行消息转换的类的信息\n    * @字段说明 gson\n    */\n    private Gson gson = new Gson();\n\n    /**\n     * 进行json字符串化\n    * 方法描述\n    * @param obj\n    * @return\n    * @创建日期 2016年9月17日\n    */\n    public String toJsonFromBean(Object obj) {\n        if (null != obj) {\n            return gson.toJson(obj);\n        }\n\n        return null;\n    }\n\n    /**\n     * 将json字符串至类，根据指定的类型信息,一般用于集合的转换\n    * 方法描述\n    * @param json\n    * @param typeSchema\n    * @return\n    * @创建日期 2016年9月17日\n    */\n    public <T> T toBeanformJson(String json, Type typeSchema) {\n        T result = this.gson.fromJson(json, typeSchema);\n\n        return result;\n    }\n\n    /**\n     * 将json字符串至类，根据指定的类型信息,用于转换单对象实体\n     * 方法描述\n     * @param <T>\n     * @param json\n     * @param typeSchema\n     * @return\n     * @创建日期 2016年9月17日\n     */\n    public <T> T toBeanformJson(String json, Class<T> classinfo) {\n        T result = this.gson.fromJson(json, classinfo);\n\n        return result;\n    }\n\n    public static void main(String[] args) {\n\n        DataNode datanode = new DataNode();\n\n        datanode.setDatabase(\"db1\");\n        datanode.setDataHost(\"os1\");\n        datanode.setName(\"dn1\");\n\n        JsonProcessBase jsonParse = new JsonProcessBase();\n\n        String jsonStr = jsonParse.toJsonFromBean(datanode);\n\n        System.out.println(\"单对象当前的json:\" + jsonStr);\n\n        // 转换实体\n        DataNode node = jsonParse.toBeanformJson(jsonStr, DataNode.class);\n\n        System.out.println(\"单对象:\" + node);\n\n        List<DataNode> listNode = new ArrayList<>();\n\n        listNode.add(datanode);\n        listNode.add(datanode);\n\n        String listJson = jsonParse.toJsonFromBean(listNode);\n\n        System.out.println(\"当前集合的json:\" + listJson);\n\n        // 转换为集合的bean\n        Type parseType = new TypeToken<List<DataNode>>() {\n        }.getType();\n        List<DataNode> list = jsonParse.toBeanformJson(listJson, parseType);\n\n        System.out.println(\"集合对象:\" + list);\n\n        // 复杂对象的转换\n        Schemas schema = new Schemas();\n        schema.setDataNode(listNode);\n\n        String jsonMultStr = jsonParse.toJsonFromBean(schema);\n\n        System.out.println(\"复杂单对象当前的json:\" + jsonMultStr);\n\n        // 转换实体\n        Schemas nodeMult = jsonParse.toBeanformJson(jsonMultStr, Schemas.class);\n\n        System.out.println(\"复杂单对象:\" + nodeMult);\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/ParseJsonServiceInf.java",
    "content": "package io.mycat.config.loader.zkprocess.parse;\n\n/**\n * json转化服务 \n* 源文件名：JsonParseServiceInf.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月16日\n* 修改作者：liujun\n* 修改日期：2016年9月16日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic interface ParseJsonServiceInf<T> {\n\n    /**\n     * 将对象T转换为json字符串\n    * 方法描述\n    * @param data\n    * @return\n    * @创建日期 2016年9月16日\n    */\n    public String parseBeanToJson(T t);\n\n    /**\n     * 将json字符串转换为javabean对象\n    * 方法描述\n    * @param json\n    * @return\n    * @创建日期 2016年9月16日\n    */\n    public T parseJsonToBean(String json);\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/ParseXmlServiceInf.java",
    "content": "package io.mycat.config.loader.zkprocess.parse;\n\n/**\n *xml转化服务 \n* 源文件名：JsonParseServiceInf.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月16日\n* 修改作者：liujun\n* 修改日期：2016年9月16日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic interface ParseXmlServiceInf<T> {\n\n    /**\n     * 将对象T写入xml文件\n    * 方法描述\n    * @param data\n    * @return\n    * @创建日期 2016年9月16日\n    */\n    public void parseToXmlWrite(T data, String outputPath, String dataName);\n\n    /**\n     * 将指定的xml转换为javabean对象\n    * 方法描述\n    * @param path xml文件路径信息\n    * @return\n    * @创建日期 2016年9月16日\n    */\n    public T parseXmlToBean(String path);\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/XmlProcessBase.java",
    "content": "package io.mycat.config.loader.zkprocess.parse;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.StandardOpenOption;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport javax.xml.bind.JAXBContext;\nimport javax.xml.bind.JAXBException;\nimport javax.xml.bind.Marshaller;\nimport javax.xml.bind.Unmarshaller;\nimport javax.xml.stream.XMLInputFactory;\nimport javax.xml.stream.XMLStreamException;\nimport javax.xml.stream.XMLStreamReader;\nimport javax.xml.transform.stream.StreamSource;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * xml文件操作转换的类的信息 \n* 源文件名：XmlProcessBase.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class XmlProcessBase {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger lOG = LoggerFactory.getLogger(XmlProcessBase.class);\n\n    /**\n     * 转换对象\n    * @字段说明 jaxContext\n    */\n    private JAXBContext jaxContext;\n\n    /**\n     * 反序列化xml文件的对象\n    * @字段说明 unmarshaller\n    */\n    private Unmarshaller unmarshaller;\n\n    /**\n     * 转换的实体对象的class信息\n    * @字段说明 parseXmlClass\n    */\n    @SuppressWarnings(\"rawtypes\")\n    public List<Class> parseXmlClass = new ArrayList<Class>();\n\n    /**\n     * 添加转换的class信息\n    * 方法描述\n    * @param parseClass\n    * @创建日期 2016年9月15日\n    */\n    @SuppressWarnings(\"rawtypes\")\n    public void addParseClass(Class parseClass) {\n        this.parseXmlClass.add(parseClass);\n    }\n\n    /**\n     * 进行jaxb对象的初始化\n    * 方法描述\n    * @throws JAXBException\n    * @创建日期 2016年9月15日\n    */\n    @SuppressWarnings(\"rawtypes\")\n    public void initJaxbClass() throws JAXBException {\n\n        // 将集合转换为数组\n        Class[] classArray = new Class[parseXmlClass.size()];\n        parseXmlClass.toArray(classArray);\n\n        try {\n            this.jaxContext = JAXBContext.newInstance(classArray, Collections.<String, Object> emptyMap());\n        } catch (JAXBException e) {\n            lOG.error(\"ZookeeperProcessListen initJaxbClass  error:Exception info:\", e);\n            throw e;\n        }\n\n        // 创建解反序化对象\n        unmarshaller = jaxContext.createUnmarshaller();\n    }\n\n    /**\n     * 默认将bean序列化为xml对象信息并写入文件\n    * 方法描述\n    * @param user 用户对象\n    * @param inputPath\n    * @param name 当前的转换xml的dtd文件的信息\n    * @创建日期 2016年9月15日\n    */\n    public void baseParseAndWriteToXml(Object user, String inputPath, String name) throws IOException {\n        try {\n            Marshaller marshaller = this.jaxContext.createMarshaller();\n            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);\n            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);\n\n            if (null != name) {\n                marshaller.setProperty(\"com.sun.xml.internal.bind.xmlHeaders\",\n                        String.format(\"<!DOCTYPE mycat:%1$s SYSTEM \\\"%1$s.dtd\\\">\", name));\n            }\n\n            Path path = Paths.get(inputPath);\n\n            OutputStream out = Files.newOutputStream(path, StandardOpenOption.CREATE,\n                    StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);\n\n            marshaller.marshal(user, out);\n\n        } catch (JAXBException e) {\n            lOG.error(\"ZookeeperProcessListen parseToXml  error:Exception info:\", e);\n        } catch (IOException e) {\n            lOG.error(\"ZookeeperProcessListen parseToXml  error:Exception info:\", e);\n        }\n    }\n\n    /**\n     * 默认将bean序列化为xml对象信息并写入文件\n     * 方法描述\n     * @param user 用户对象\n     * @param inputPath\n     * @param name 当前的转换xml的dtd文件的信息\n     * @创建日期 2016年9月15日\n     */\n    @SuppressWarnings(\"restriction\")\n    public void baseParseAndWriteToXml(Object user, String inputPath, String name, Map<String, Object> map)\n            throws IOException {\n        try {\n            Marshaller marshaller = this.jaxContext.createMarshaller();\n            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);\n            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);\n\n            if (null != name) {\n                marshaller.setProperty(\"com.sun.xml.internal.bind.xmlHeaders\",\n                        String.format(\"<!DOCTYPE mycat:%1$s SYSTEM \\\"%1$s.dtd\\\">\", name));\n            }\n\n            if (null != map && !map.isEmpty()) {\n                for (Entry<String, Object> entry : map.entrySet()) {\n                    marshaller.setProperty(entry.getKey(), entry.getValue());\n                }\n            }\n\n            Path path = Paths.get(inputPath);\n\n            OutputStream out = Files.newOutputStream(path, StandardOpenOption.CREATE,\n                    StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);\n\n            marshaller.marshal(user, out);\n\n        } catch (JAXBException e) {\n            lOG.error(\"ZookeeperProcessListen parseToXml  error:Exception info:\", e);\n        } catch (IOException e) {\n            lOG.error(\"ZookeeperProcessListen parseToXml  error:Exception info:\", e);\n        }\n    }\n\n    /**\n     * 默认转换将指定的xml转化为\n    * 方法描述\n    * @param inputStream\n    * @param fileName\n    * @return\n    * @throws JAXBException\n    * @throws XMLStreamException\n    * @创建日期 2016年9月16日\n    */\n    public Object baseParseXmlToBean(String fileName) throws JAXBException, XMLStreamException {\n        // 搜索当前转化的文件\n        InputStream inputStream = XmlProcessBase.class.getResourceAsStream(fileName);\n\n        // 如果能够搜索到文件\n        if (inputStream != null) {\n            // 进行文件反序列化信息\n            XMLInputFactory xif = XMLInputFactory.newFactory();\n            xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);\n            XMLStreamReader xmlRead = xif.createXMLStreamReader(new StreamSource(inputStream));\n\n            return unmarshaller.unmarshal(xmlRead);\n        }\n\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/entryparse/cache/json/EhcacheJsonParse.java",
    "content": "package io.mycat.config.loader.zkprocess.parse.entryparse.cache.json;\n\nimport io.mycat.config.loader.zkprocess.entity.cache.Ehcache;\nimport io.mycat.config.loader.zkprocess.parse.JsonProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\n\n/**\n * 进行Function节点的转换\n* 源文件名：FunctionJsonParse.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月17日\n* 修改作者：liujun\n* 修改日期：2016年9月17日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class EhcacheJsonParse extends JsonProcessBase implements ParseJsonServiceInf<Ehcache> {\n\n    @Override\n    public String parseBeanToJson(Ehcache t) {\n        return this.toJsonFromBean(t);\n    }\n\n    @Override\n    public Ehcache parseJsonToBean(String json) {\n        return this.toBeanformJson(json, Ehcache.class);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/entryparse/cache/xml/EhcacheParseXmlImpl.java",
    "content": "package io.mycat.config.loader.zkprocess.parse.entryparse.cache.xml;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.xml.bind.JAXBException;\nimport javax.xml.bind.Marshaller;\nimport javax.xml.stream.XMLStreamException;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.loader.zkprocess.entity.cache.Ehcache;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\n\n/**\n * rule.xml与javabean之间的转化\n* 源文件名：SchemasParseXmlImpl.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月16日\n* 修改作者：liujun\n* 修改日期：2016年9月16日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class EhcacheParseXmlImpl implements ParseXmlServiceInf<Ehcache> {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger lOG = LoggerFactory.getLogger(EhcacheParseXmlImpl.class);\n\n    /**\n     * 基本的转换类的信息\n    * @字段说明 parseBean\n    */\n    private XmlProcessBase parseBean;\n\n    /**\n     * 转换的类的信息\n    * 构造方法\n    * @param parseBase\n    */\n    public EhcacheParseXmlImpl(XmlProcessBase parseBase) {\n\n        this.parseBean = parseBase;\n        // 添加xml的转换的实体类信息\n        parseBean.addParseClass(Ehcache.class);\n    }\n\n    @Override\n    public Ehcache parseXmlToBean(String path) {\n\n        Ehcache schema = null;\n\n        try {\n            schema = (Ehcache) this.parseBean.baseParseXmlToBean(path);\n        } catch (JAXBException e) {\n            e.printStackTrace();\n            lOG.error(\"EhcacheParseXmlImpl parseXmlToBean JAXBException\", e);\n        } catch (XMLStreamException e) {\n            e.printStackTrace();\n            lOG.error(\"EhcacheParseXmlImpl parseXmlToBean XMLStreamException\", e);\n        }\n\n        return schema;\n    }\n\n    @Override\n    public void parseToXmlWrite(Ehcache data, String outputFile, String dataName) {\n        try {\n            // 设置\n            Map<String, Object> paramMap = new HashMap<>();\n            paramMap.put(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, \"ehcache.xsd\");\n            \n            this.parseBean.baseParseAndWriteToXml(data, outputFile, dataName, paramMap);\n        } catch (IOException e) {\n            e.printStackTrace();\n            lOG.error(\"EhcacheParseXmlImpl parseToXmlWrite IOException\", e);\n        }\n    }\n\n}\n\n\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/entryparse/rule/json/FunctionJsonParse.java",
    "content": "package io.mycat.config.loader.zkprocess.parse.entryparse.rule.json;\n\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport com.google.gson.reflect.TypeToken;\n\nimport io.mycat.config.loader.zkprocess.entity.rule.function.Function;\nimport io.mycat.config.loader.zkprocess.parse.JsonProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\n\n/**\n * 进行Function节点的转换\n* 源文件名：FunctionJsonParse.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月17日\n* 修改作者：liujun\n* 修改日期：2016年9月17日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class FunctionJsonParse extends JsonProcessBase implements ParseJsonServiceInf<List<Function>> {\n\n    @Override\n    public String parseBeanToJson(List<Function> t) {\n        return this.toJsonFromBean(t);\n    }\n\n    @Override\n    public List<Function> parseJsonToBean(String json) {\n\n        // 转换为集合的bean\n        Type parseType = new TypeToken<List<Function>>() {\n        }.getType();\n\n        return this.toBeanformJson(json, parseType);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/entryparse/rule/json/TableRuleJsonParse.java",
    "content": "package io.mycat.config.loader.zkprocess.parse.entryparse.rule.json;\n\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport com.google.gson.reflect.TypeToken;\n\nimport io.mycat.config.loader.zkprocess.entity.rule.tablerule.TableRule;\nimport io.mycat.config.loader.zkprocess.parse.JsonProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\n\n/**\n * 进行TableRule节点的转换\n* 源文件名：TableRuleJsonParse.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月17日\n* 修改作者：liujun\n* 修改日期：2016年9月17日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class TableRuleJsonParse extends JsonProcessBase implements ParseJsonServiceInf<List<TableRule>> {\n\n    @Override\n    public String parseBeanToJson(List<TableRule> t) {\n        return this.toJsonFromBean(t);\n    }\n\n    @Override\n    public List<TableRule> parseJsonToBean(String json) {\n\n        // 转换为集合的bean\n        Type parseType = new TypeToken<List<TableRule>>() {\n        }.getType();\n\n        return this.toBeanformJson(json, parseType);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/entryparse/rule/xml/RuleParseXmlImpl.java",
    "content": "package io.mycat.config.loader.zkprocess.parse.entryparse.rule.xml;\n\nimport java.io.IOException;\n\nimport javax.xml.bind.JAXBException;\nimport javax.xml.stream.XMLStreamException;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.loader.zkprocess.entity.Rules;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\n\n/**\n * rule.xml与javabean之间的转化\n* 源文件名：SchemasParseXmlImpl.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月16日\n* 修改作者：liujun\n* 修改日期：2016年9月16日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class RuleParseXmlImpl implements ParseXmlServiceInf<Rules> {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger lOG = LoggerFactory.getLogger(RuleParseXmlImpl.class);\n\n    /**\n     * 基本的转换类的信息\n    * @字段说明 parseBean\n    */\n    private XmlProcessBase parseBean;\n\n    /**\n     * 转换的类的信息\n    * 构造方法\n    * @param parseBase\n    */\n    public RuleParseXmlImpl(XmlProcessBase parseBase) {\n\n        this.parseBean = parseBase;\n        // 添加xml的转换的实体类信息\n        parseBean.addParseClass(Rules.class);\n    }\n\n    @Override\n    public Rules parseXmlToBean(String path) {\n\n        Rules schema = null;\n\n        try {\n            schema = (Rules) this.parseBean.baseParseXmlToBean(path);\n        } catch (JAXBException e) {\n            e.printStackTrace();\n            lOG.error(\"RulesParseXmlImpl parseXmlToBean JAXBException\", e);\n        } catch (XMLStreamException e) {\n            e.printStackTrace();\n            lOG.error(\"RulesParseXmlImpl parseXmlToBean XMLStreamException\", e);\n        }\n\n        return schema;\n    }\n\n    @Override\n    public void parseToXmlWrite(Rules data, String outputFile, String dataName) {\n        try {\n            this.parseBean.baseParseAndWriteToXml(data, outputFile, dataName);\n        } catch (IOException e) {\n            e.printStackTrace();\n            lOG.error(\"RulesParseXmlImpl parseToXmlWrite IOException\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/entryparse/schema/json/DataHostJsonParse.java",
    "content": "package io.mycat.config.loader.zkprocess.parse.entryparse.schema.json;\n\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport com.google.gson.reflect.TypeToken;\n\nimport io.mycat.config.loader.zkprocess.entity.schema.datahost.DataHost;\nimport io.mycat.config.loader.zkprocess.parse.JsonProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\n\n/**\n * 进行datahost节点的转换\n* 源文件名：DataHostJsonParse.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月17日\n* 修改作者：liujun\n* 修改日期：2016年9月17日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class DataHostJsonParse extends JsonProcessBase implements ParseJsonServiceInf<List<DataHost>> {\n\n    @Override\n    public String parseBeanToJson(List<DataHost> t) {\n        return this.toJsonFromBean(t);\n    }\n\n    @Override\n    public List<DataHost> parseJsonToBean(String json) {\n\n        // 转换为集合的bean\n        Type parseType = new TypeToken<List<DataHost>>() {\n        }.getType();\n\n        return this.toBeanformJson(json, parseType);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/entryparse/schema/json/DataNodeJsonParse.java",
    "content": "package io.mycat.config.loader.zkprocess.parse.entryparse.schema.json;\n\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport com.google.gson.reflect.TypeToken;\n\nimport io.mycat.config.loader.zkprocess.entity.schema.datanode.DataNode;\nimport io.mycat.config.loader.zkprocess.parse.JsonProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\n\n/**\n * 进行将datanode数据与json的转化\n* 源文件名：DataNodeJsonParse.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月17日\n* 修改作者：liujun\n* 修改日期：2016年9月17日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class DataNodeJsonParse extends JsonProcessBase implements ParseJsonServiceInf<List<DataNode>> {\n\n    @Override\n    public String parseBeanToJson(List<DataNode> t) {\n        return this.toJsonFromBean(t);\n    }\n\n    @Override\n    public List<DataNode> parseJsonToBean(String json) {\n        // 转换为集合的bean\n        Type parseType = new TypeToken<List<DataNode>>() {\n        }.getType();\n\n        return this.toBeanformJson(json, parseType);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/entryparse/schema/json/SchemaJsonParse.java",
    "content": "package io.mycat.config.loader.zkprocess.parse.entryparse.schema.json;\n\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport com.google.gson.reflect.TypeToken;\n\nimport io.mycat.config.loader.zkprocess.entity.schema.schema.Schema;\nimport io.mycat.config.loader.zkprocess.parse.JsonProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\n\n/**\n * 进行schema部分的转换\n* 源文件名：SchemaJsonParse.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月17日\n* 修改作者：liujun\n* 修改日期：2016年9月17日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class SchemaJsonParse extends JsonProcessBase implements ParseJsonServiceInf<List<Schema>> {\n\n    @Override\n    public String parseBeanToJson(List<Schema> t) {\n        return this.toJsonFromBean(t);\n    }\n\n    @Override\n    public List<Schema> parseJsonToBean(String json) {\n        // 转换为集合的bean\n        Type parseType = new TypeToken<List<Schema>>() {\n        }.getType();\n\n        return this.toBeanformJson(json, parseType);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/entryparse/schema/xml/SchemasParseXmlImpl.java",
    "content": "package io.mycat.config.loader.zkprocess.parse.entryparse.schema.xml;\n\nimport java.io.IOException;\n\nimport javax.xml.bind.JAXBException;\nimport javax.xml.stream.XMLStreamException;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.loader.zkprocess.entity.Schemas;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\n\n/**\n * schema.xml与javabean之间的转化\n* 源文件名：SchemasParseXmlImpl.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月16日\n* 修改作者：liujun\n* 修改日期：2016年9月16日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class SchemasParseXmlImpl implements ParseXmlServiceInf<Schemas> {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger lOG = LoggerFactory.getLogger(SchemasParseXmlImpl.class);\n\n    /**\n     * 基本的转换类的信息\n    * @字段说明 parseBean\n    */\n    private XmlProcessBase parseBean;\n\n    /**\n     * 转换的类的信息\n    * 构造方法\n    * @param parseBase\n    */\n    public SchemasParseXmlImpl(XmlProcessBase parseBase) {\n\n        this.parseBean = parseBase;\n        // 添加xml的转换的实体类信息\n        parseBean.addParseClass(Schemas.class);\n    }\n\n    @Override\n    public Schemas parseXmlToBean(String path) {\n\n        Schemas schema = null;\n\n        try {\n            schema = (Schemas) this.parseBean.baseParseXmlToBean(path);\n        } catch (JAXBException e) {\n            e.printStackTrace();\n            lOG.error(\"SchemasParseXmlImpl parseXmlToBean JAXBException\", e);\n        } catch (XMLStreamException e) {\n            e.printStackTrace();\n            lOG.error(\"SchemasParseXmlImpl parseXmlToBean XMLStreamException\", e);\n        }\n\n        return schema;\n    }\n\n    @Override\n    public void parseToXmlWrite(Schemas data, String outputFile, String dataName) {\n        try {\n            this.parseBean.baseParseAndWriteToXml(data, outputFile, dataName);\n        } catch (IOException e) {\n            e.printStackTrace();\n            lOG.error(\"SchemasParseXmlImpl parseToXmlWrite IOException\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/entryparse/server/json/SystemJsonParse.java",
    "content": "package io.mycat.config.loader.zkprocess.parse.entryparse.server.json;\n\nimport io.mycat.config.loader.zkprocess.entity.server.System;\nimport io.mycat.config.loader.zkprocess.parse.JsonProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\n\n/**\n * 进行datahost节点的转换\n* 源文件名：DataHostJsonParse.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月17日\n* 修改作者：liujun\n* 修改日期：2016年9月17日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class SystemJsonParse extends JsonProcessBase implements ParseJsonServiceInf<System> {\n\n    @Override\n    public String parseBeanToJson(System t) {\n        return this.toJsonFromBean(t);\n    }\n\n    @Override\n    public System parseJsonToBean(String json) {\n\n        return this.toBeanformJson(json, System.class);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/entryparse/server/json/UserJsonParse.java",
    "content": "package io.mycat.config.loader.zkprocess.parse.entryparse.server.json;\n\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport com.google.gson.reflect.TypeToken;\n\nimport io.mycat.config.loader.zkprocess.entity.server.user.User;\nimport io.mycat.config.loader.zkprocess.parse.JsonProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\n\n/**\n * 进行datahost节点的转换\n* 源文件名：DataHostJsonParse.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月17日\n* 修改作者：liujun\n* 修改日期：2016年9月17日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class UserJsonParse extends JsonProcessBase implements ParseJsonServiceInf<List<User>> {\n\n    @Override\n    public String parseBeanToJson(List<User> t) {\n        return this.toJsonFromBean(t);\n    }\n\n    @Override\n    public List<User> parseJsonToBean(String json) {\n\n        // 转换为集合的bean\n        Type parseType = new TypeToken<List<User>>() {\n        }.getType();\n\n        return this.toBeanformJson(json, parseType);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/parse/entryparse/server/xml/ServerParseXmlImpl.java",
    "content": "package io.mycat.config.loader.zkprocess.parse.entryparse.server.xml;\n\nimport java.io.IOException;\n\nimport javax.xml.bind.JAXBException;\nimport javax.xml.stream.XMLStreamException;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.loader.zkprocess.entity.Server;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\n\n/**\n * schema.xml与javabean之间的转化\n* 源文件名：ServerParseXmlImpl.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月16日\n* 修改作者：liujun\n* 修改日期：2016年9月16日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class ServerParseXmlImpl implements ParseXmlServiceInf<Server> {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger lOG = LoggerFactory.getLogger(ServerParseXmlImpl.class);\n\n    /**\n     * 基本的转换类的信息\n    * @字段说明 parseBean\n    */\n    private XmlProcessBase parseBean;\n\n    /**\n     * 转换的类的信息\n    * 构造方法\n    * @param parseBase\n    */\n    public ServerParseXmlImpl(XmlProcessBase parseBase) {\n\n        this.parseBean = parseBase;\n        // 添加xml的转换的实体类信息\n        parseBean.addParseClass(Server.class);\n    }\n\n    @Override\n    public Server parseXmlToBean(String path) {\n\n        Server server = null;\n\n        try {\n            server = (Server) this.parseBean.baseParseXmlToBean(path);\n        } catch (JAXBException e) {\n            e.printStackTrace();\n            lOG.error(\"ServerParseXmlImpl parseXmlToBean JAXBException\", e);\n        } catch (XMLStreamException e) {\n            e.printStackTrace();\n            lOG.error(\"ServerParseXmlImpl parseXmlToBean XMLStreamException\", e);\n        }\n\n        return server;\n    }\n\n    @Override\n    public void parseToXmlWrite(Server data, String outputFile, String dataName) {\n        try {\n            this.parseBean.baseParseAndWriteToXml(data, outputFile, dataName);\n        } catch (IOException e) {\n            e.printStackTrace();\n            lOG.error(\"ServerParseXmlImpl parseToXmlWrite IOException\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/xmltozk/BindataToZK.java",
    "content": "package io.mycat.config.loader.zkprocess.xmltozk;\n\nimport com.google.common.io.Files;\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.zktoxml.listen.RuleszkToxmlLoader;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.util.ZKUtils;\nimport org.apache.curator.framework.CuratorFramework;\n\nimport java.io.File;\nimport java.nio.file.Paths;\n\n/**\n * Created by magicdoom on 2016/10/26.\n *  only for test\n */\npublic class BindataToZK {\n    public static void main(String[] args) {\n        File file = new File(SystemConfig.getHomePath()+ \"/conf\",\"ruledata\" );\n        if(file.exists()&&file.isDirectory())\n        {\n            File[] binFiles=file.listFiles();\n            for (File binFile : binFiles) {\n\n           String path=     ZKUtils.getZKBasePath()+\"ruledata/\"+binFile.getName();\n                CuratorFramework zk= ZKUtils.getConnection();\n                try {\n                    zk.create().creatingParentsIfNeeded().forPath(path)  ;\n                    zk.setData().forPath(path, Files.toByteArray(binFile)) ;\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/xmltozk/XmltoZkMain.java",
    "content": "package io.mycat.config.loader.zkprocess.xmltozk;\n\nimport javax.xml.bind.JAXBException;\n\nimport com.alibaba.fastjson.JSON;\nimport io.mycat.config.loader.zkprocess.zookeeper.ClusterInfo;\nimport org.apache.curator.framework.CuratorFramework;\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.console.ZkNofiflyCfg;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.xmltozk.listen.EcachesxmlTozkLoader;\nimport io.mycat.config.loader.zkprocess.xmltozk.listen.OthermsgTozkLoader;\nimport io.mycat.config.loader.zkprocess.xmltozk.listen.RulesxmlTozkLoader;\nimport io.mycat.config.loader.zkprocess.xmltozk.listen.SchemasxmlTozkLoader;\nimport io.mycat.config.loader.zkprocess.xmltozk.listen.SequenceTozkLoader;\nimport io.mycat.config.loader.zkprocess.xmltozk.listen.ServerxmlTozkLoader;\nimport io.mycat.util.ZKUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class XmltoZkMain {\n    private static final Logger LOGGER = LoggerFactory.getLogger(XmltoZkMain.class);\n    public static void main(String[] args) throws JAXBException, InterruptedException {\n        // 加载zk总服务\n        ZookeeperProcessListen zkListen = new ZookeeperProcessListen();\n\n        // 得到集群名称\n        String custerName = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTERID);\n        // 得到基本路径\n        String basePath = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_BASE.getKey();\n        basePath = basePath + ZookeeperPath.ZK_SEPARATOR.getKey() + custerName;\n        zkListen.setBasePath(basePath);\n\n        // 获得zk的连接信息\n        CuratorFramework zkConn = buildConnection(ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_URL));\n\n        // 获得公共的xml转换器对象\n        XmlProcessBase xmlProcess = new XmlProcessBase();\n\n        // 进行xmltozk的schema文件的操作\n        new SchemasxmlTozkLoader(zkListen, zkConn, xmlProcess);\n\n        // 进行xmltozk的server文件的操作\n        new ServerxmlTozkLoader(zkListen, zkConn, xmlProcess);\n\n        // 进行rule文件到zk的操作\n        new RulesxmlTozkLoader(zkListen, zkConn, xmlProcess);\n\n        // 进行序列信息入zk中\n        new SequenceTozkLoader(zkListen, zkConn, xmlProcess);\n\n        // 缓存配制信息\n        new EcachesxmlTozkLoader(zkListen, zkConn, xmlProcess);\n\n        // 将其他信息加载的zk中\n        new OthermsgTozkLoader(zkListen, zkConn, xmlProcess);\n\n        // 初始化xml转换操作\n        xmlProcess.initJaxbClass();\n\n\n        // 加载通知进程\n        zkListen.notifly(ZkNofiflyCfg.ZK_NOTIFLY_LOAD_ALL.getKey());\n\n\n\n        String clusterNodes=    ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTER_NODES);\n        String clusterSize=    ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTER_SIZE);\n        ClusterInfo info=new ClusterInfo();\n        info.setClusterNodes(clusterNodes);\n        info.setClusterSize(Integer.parseInt(clusterSize));\n        try {\n            zkConn.setData().forPath(basePath, JSON.toJSONBytes(info));\n        } catch (Exception e) {\n            LOGGER.error(\"error\",e);\n        }\n\n    }\n\n    private static CuratorFramework buildConnection(String url) {\n\n        return ZKUtils.getConnection();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/xmltozk/listen/EcachesxmlTozkLoader.java",
    "content": "package io.mycat.config.loader.zkprocess.xmltozk.listen;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.fastjson.util.IOUtils;\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.NotiflyService;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.entity.cache.Ehcache;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.cache.json.EhcacheJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.cache.xml.EhcacheParseXmlImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkMultLoader;\n\n/**\n * 进行从ecache.xml加载到zk中加载\n* 源文件名：SchemasLoader.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class EcachesxmlTozkLoader extends ZkMultLoader implements NotiflyService {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(EcachesxmlTozkLoader.class);\n\n    /**\n     * 当前文件中的zkpath信息 \n    * @字段说明 currZkPath\n    */\n    private final String currZkPath;\n\n    /**\n     * Ehcache文件的路径信息\n    * @字段说明 SCHEMA_PATH\n    */\n    private static final String EHCACHE_PATH = ZookeeperPath.ZK_LOCAL_CFG_PATH.getKey() + \"ehcache.xml\";\n\n    /**\n     * 缓存文件名称\n    * @字段说明 CACHESERVER_NAME\n    */\n    private static final String CACHESERVER_NAME = \"cacheservice.properties\";\n\n    /**\n     * 缓存的xml文件配制信息\n    * @字段说明 EHCACHE_NAME\n    */\n    private static final String EHCACHE_NAME = \"ehcache.xml\";\n\n    /**\n     * ehcache的xml的转换信息\n    * @字段说明 parseEhcacheXMl\n    */\n    private final ParseXmlServiceInf<Ehcache> parseEcacheXMl;\n\n    /**\n     * 表的路由信息\n    * @字段说明 parseJsonService\n    */\n    private ParseJsonServiceInf<Ehcache> parseJsonEhcacheService = new EhcacheJsonParse();\n\n    public EcachesxmlTozkLoader(ZookeeperProcessListen zookeeperListen, CuratorFramework curator,\n            XmlProcessBase xmlParseBase) {\n\n        this.setCurator(curator);\n\n        // 获得当前集群的名称\n        String schemaPath = zookeeperListen.getBasePath();\n        schemaPath = schemaPath + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_CACHE.getKey();\n\n        currZkPath = schemaPath;\n        // 将当前自己注册为事件接收对象\n        zookeeperListen.addListen(schemaPath, this);\n\n        // 生成xml与类的转换信息\n        parseEcacheXMl = new EhcacheParseXmlImpl(xmlParseBase);\n    }\n\n    @Override\n    public boolean notiflyProcess() throws Exception {\n        // 1,读取本地的xml文件\n        Ehcache Ehcache = this.parseEcacheXMl.parseXmlToBean(EHCACHE_PATH);\n        LOGGER.info(\"EhcachexmlTozkLoader notiflyProcessxml to zk Ehcache Object  :\" + Ehcache);\n        // 将实体信息写入至zk中\n        this.xmlTozkEhcacheJson(currZkPath, Ehcache);\n\n        LOGGER.info(\"EhcachexmlTozkLoader notiflyProcess xml to zk is success\");\n\n        return true;\n    }\n\n    /**\n     * 将xml文件的信息写入到zk中\n    * 方法描述\n    * @param basePath 基本路径\n    * @param schema schema文件的信息\n    * @throws Exception 异常信息\n    * @创建日期 2016年9月17日\n    */\n    private void xmlTozkEhcacheJson(String basePath, Ehcache ehcache) throws Exception {\n        // ehcache节点信息\n        String ehcacheFile = ZookeeperPath.ZK_SEPARATOR.getKey() + EHCACHE_NAME;\n        String ehcacheJson = this.parseJsonEhcacheService.parseBeanToJson(ehcache);\n        this.checkAndwriteString(basePath, ehcacheFile, ehcacheJson);\n\n        // 读取文件信息\n        String cacheServicePath = ZookeeperPath.ZK_SEPARATOR.getKey() + CACHESERVER_NAME;\n        String serviceValue = this.readSeqFile(CACHESERVER_NAME);\n        this.checkAndwriteString(basePath, cacheServicePath, serviceValue);\n    }\n\n    /**\n     * 读取 mapFile文件的信息\n    * 方法描述\n    * @param name 名称信息\n    * @return\n    * @创建日期 2016年9月18日\n    */\n    private String readSeqFile(String name) {\n\n        StringBuilder mapFileStr = new StringBuilder();\n\n        String path = ZookeeperPath.ZK_LOCAL_CFG_PATH.getKey() + name;\n        // 加载数据\n        InputStream input = EcachesxmlTozkLoader.class.getResourceAsStream(path);\n\n        checkNotNull(input, \"read SeqFile file curr Path :\" + path + \" is null! must is not null\");\n\n        byte[] buffers = new byte[256];\n\n        try {\n            int readIndex = -1;\n\n            while ((readIndex = input.read(buffers)) != -1) {\n                mapFileStr.append(new String(buffers, 0, readIndex));\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n            LOGGER.error(\"EhcachexmlTozkLoader readMapFile IOException\", e);\n        } finally {\n            IOUtils.close(input);\n        }\n\n        return mapFileStr.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/xmltozk/listen/OthermsgTozkLoader.java",
    "content": "package io.mycat.config.loader.zkprocess.xmltozk.listen;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.utils.ZKPaths;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.NotiflyService;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkMultLoader;\n\n/**\n * 其他一些信息加载到zk中\n* 源文件名：SchemasLoader.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class OthermsgTozkLoader extends ZkMultLoader implements NotiflyService {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(OthermsgTozkLoader.class);\n\n    /**\n     * 当前文件中的zkpath信息 \n    * @字段说明 currZkPath\n    */\n    private final String currZkPath;\n\n    public OthermsgTozkLoader(ZookeeperProcessListen zookeeperListen, CuratorFramework curator,\n            XmlProcessBase xmlParseBase) {\n\n        this.setCurator(curator);\n\n        // 获得当前集群的名称\n        String schemaPath = zookeeperListen.getBasePath();\n\n        currZkPath = schemaPath;\n        // 将当前自己注册为事件接收对象\n        zookeeperListen.addListen(schemaPath, this);\n\n    }\n\n    @Override\n    public boolean notiflyProcess() throws Exception {\n        // 添加line目录，用作集群中节点，在线的基本目录信息\n        String line = currZkPath + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_LINE.getKey();\n        ZKPaths.mkdirs(this.getCurator().getZookeeperClient().getZooKeeper(), line);\n        LOGGER.info(\"OthermsgTozkLoader zookeeper mkdir \" + line + \" success\");\n\n        // 添加序列目录信息\n        String seqLine = currZkPath + ZookeeperPath.ZK_SEPARATOR.getKey()\n                + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE.getKey();\n        seqLine = seqLine + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_INSTANCE.getKey();\n        ZKPaths.mkdirs(this.getCurator().getZookeeperClient().getZooKeeper(), seqLine);\n\n        String seqLeader = currZkPath + ZookeeperPath.ZK_SEPARATOR.getKey()\n                + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE.getKey();\n        seqLeader = seqLeader + ZookeeperPath.ZK_SEPARATOR.getKey()\n                + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_LEADER.getKey();\n        ZKPaths.mkdirs(this.getCurator().getZookeeperClient().getZooKeeper(), seqLeader);\n\n        String incrSeq = currZkPath + ZookeeperPath.ZK_SEPARATOR.getKey()\n                + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE.getKey();\n        incrSeq = incrSeq + ZookeeperPath.ZK_SEPARATOR.getKey()\n                + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_INCREMENT_SEQ.getKey();\n        ZKPaths.mkdirs(this.getCurator().getZookeeperClient().getZooKeeper(), incrSeq);\n\n        LOGGER.info(\"OthermsgTozkLoader zookeeper mkdir \" + seqLine + \" success\");\n        LOGGER.info(\"OthermsgTozkLoader zookeeper mkdir \" + seqLeader + \" success\");\n        LOGGER.info(\"OthermsgTozkLoader zookeeper mkdir \" + incrSeq + \" success\");\n\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/xmltozk/listen/RulesxmlTozkLoader.java",
    "content": "package io.mycat.config.loader.zkprocess.xmltozk.listen;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.fastjson.util.IOUtils;\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.NotiflyService;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.console.ParseParamEnum;\nimport io.mycat.config.loader.zkprocess.entity.Property;\nimport io.mycat.config.loader.zkprocess.entity.Rules;\nimport io.mycat.config.loader.zkprocess.entity.rule.function.Function;\nimport io.mycat.config.loader.zkprocess.entity.rule.tablerule.TableRule;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.rule.json.FunctionJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.rule.json.TableRuleJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.rule.xml.RuleParseXmlImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkMultLoader;\n\n/**\n * 进行从rule.xml加载到zk中加载\n* 源文件名：SchemasLoader.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class RulesxmlTozkLoader extends ZkMultLoader implements NotiflyService {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(RulesxmlTozkLoader.class);\n\n    /**\n     * 当前文件中的zkpath信息 \n    * @字段说明 currZkPath\n    */\n    private final String currZkPath;\n\n    /**\n     * Rules文件的路径信息\n    * @字段说明 SCHEMA_PATH\n    */\n    private static final String RULE_PATH = ZookeeperPath.ZK_LOCAL_CFG_PATH.getKey() + \"rule.xml\";\n\n    /**\n     * Rules的xml的转换信息\n    * @字段说明 parseRulesXMl\n    */\n    private ParseXmlServiceInf<Rules> parseRulesXMl;\n\n    /**\n     * 表的路由信息\n    * @字段说明 parseJsonService\n    */\n    private ParseJsonServiceInf<List<TableRule>> parseJsonTableRuleService = new TableRuleJsonParse();\n\n    /**\n     * 表对应的字段信息\n    * @字段说明 parseJsonFunctionService\n    */\n    private ParseJsonServiceInf<List<Function>> parseJsonFunctionService = new FunctionJsonParse();\n\n    public RulesxmlTozkLoader(ZookeeperProcessListen zookeeperListen, CuratorFramework curator,\n            XmlProcessBase xmlParseBase) {\n\n        this.setCurator(curator);\n\n        // 获得当前集群的名称\n        String schemaPath = zookeeperListen.getBasePath();\n        schemaPath = schemaPath + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_RULE.getKey();\n\n        currZkPath = schemaPath;\n        // 将当前自己注册为事件接收对象\n        zookeeperListen.addListen(schemaPath, this);\n\n        // 生成xml与类的转换信息\n        parseRulesXMl = new RuleParseXmlImpl(xmlParseBase);\n    }\n\n    @Override\n    public boolean notiflyProcess() throws Exception {\n        // 1,读取本地的xml文件\n        Rules Rules = this.parseRulesXMl.parseXmlToBean(RULE_PATH);\n        LOGGER.info(\"RulesxmlTozkLoader notiflyProcessxml to zk Rules Object  :\" + Rules);\n        // 将实体信息写入至zk中\n        this.xmlTozkRulesJson(currZkPath, Rules);\n\n        LOGGER.info(\"RulesxmlTozkLoader notiflyProcess xml to zk is success\");\n\n        return true;\n    }\n\n    /**\n     * 将xml文件的信息写入到zk中\n    * 方法描述\n    * @param basePath 基本路径\n    * @param schema schema文件的信息\n    * @throws Exception 异常信息\n    * @创建日期 2016年9月17日\n    */\n    private void xmlTozkRulesJson(String basePath, Rules Rules) throws Exception {\n        // tablerune节点信息\n        String tableRulePath = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_RULE_TABLERULE.getKey();\n        String tableRuleJson = this.parseJsonTableRuleService.parseBeanToJson(Rules.getTableRule());\n        this.checkAndwriteString(basePath, tableRulePath, tableRuleJson);\n\n        // 读取mapFile文件,并加入到function中\n        this.readMapFileAddFunction(Rules.getFunction());\n\n        // 方法设置信息\n        String functionPath = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_RULE_FUNCTION.getKey();\n        String functionJson = this.parseJsonFunctionService.parseBeanToJson(Rules.getFunction());\n        this.checkAndwriteString(basePath, functionPath, functionJson);\n    }\n\n    /**\n     *  读取序列配制文件便利店  \n    * 方法描述\n    * @param functionList\n    * @创建日期 2016年9月18日\n    */\n    private void readMapFileAddFunction(List<Function> functionList) {\n\n        List<Property> tempData = new ArrayList<>();\n\n        for (Function function : functionList) {\n            List<Property> proList = function.getProperty();\n            if (null != proList && !proList.isEmpty()) {\n                // 进行数据遍历\n                for (Property property : proList) {\n                    // 如果为mapfile，则需要去读取数据信息，并存到json中\n                    if (ParseParamEnum.ZK_PATH_RULE_MAPFILE_NAME.getKey().equals(property.getName())) {\n                        Property mapFilePro = new Property();\n                        mapFilePro.setName(property.getValue());\n                        // 加载属性的值信息\n                        mapFilePro.setValue(this.readMapFile(property.getValue()));\n                        tempData.add(mapFilePro);\n                    }\n                }\n                // 将数据添加的集合中\n                proList.addAll(tempData);\n                // 清空，以进行下一次的添加\n                tempData.clear();\n            }\n        }\n    }\n\n    /**\n     * 读取 mapFile文件的信息\n    * 方法描述\n    * @param name 名称信息\n    * @return\n    * @创建日期 2016年9月18日\n    */\n    private String readMapFile(String name) {\n\n        StringBuilder mapFileStr = new StringBuilder();\n\n        String path = ZookeeperPath.ZK_LOCAL_CFG_PATH.getKey() + name;\n        // 加载数据\n        InputStream input = RulesxmlTozkLoader.class.getResourceAsStream(path);\n\n        checkNotNull(input, \"read Map file curr Path :\" + path + \" is null! must is not null\");\n\n        byte[] buffers = new byte[256];\n\n        try {\n            int readIndex = -1;\n\n            while ((readIndex = input.read(buffers)) != -1) {\n                mapFileStr.append(new String(buffers, 0, readIndex));\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n            LOGGER.error(\"RulesxmlTozkLoader readMapFile IOException\", e);\n\n        } finally {\n            IOUtils.close(input);\n        }\n\n        return mapFileStr.toString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/xmltozk/listen/SchemasxmlTozkLoader.java",
    "content": "package io.mycat.config.loader.zkprocess.xmltozk.listen;\n\nimport java.util.List;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.entity.Schemas;\nimport io.mycat.config.loader.zkprocess.entity.schema.datahost.DataHost;\nimport io.mycat.config.loader.zkprocess.entity.schema.datanode.DataNode;\nimport io.mycat.config.loader.zkprocess.entity.schema.schema.Schema;\nimport io.mycat.config.loader.zkprocess.comm.NotiflyService;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.schema.json.DataHostJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.schema.json.DataNodeJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.schema.json.SchemaJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.schema.xml.SchemasParseXmlImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkMultLoader;\n\n/**\n * 进行从xml加载到zk中加载\n* 源文件名：SchemasLoader.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class SchemasxmlTozkLoader extends ZkMultLoader implements NotiflyService {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(SchemasxmlTozkLoader.class);\n\n    /**\n     * 当前文件中的zkpath信息 \n    * @字段说明 currZkPath\n    */\n    private final String currZkPath;\n\n    /**\n     * schema文件的路径信息\n    * @字段说明 SCHEMA_PATH\n    */\n    private static final String SCHEMA_PATH = ZookeeperPath.ZK_LOCAL_CFG_PATH.getKey() + \"schema.xml\";\n\n    /**\n     * schema类与xml转换服务 \n    * @字段说明 parseSchemaService\n    */\n    private ParseXmlServiceInf<Schemas> parseSchemaXmlService;\n\n    /**\n     * 进行将schema\n    * @字段说明 parseJsonSchema\n    */\n    private ParseJsonServiceInf<List<Schema>> parseJsonSchema = new SchemaJsonParse();\n\n    /**\n     * 进行将dataNode\n     * @字段说明 parseJsonSchema\n     */\n    private ParseJsonServiceInf<List<DataNode>> parseJsonDataNode = new DataNodeJsonParse();\n\n    /**\n     * 进行将dataNode\n     * @字段说明 parseJsonSchema\n     */\n    private ParseJsonServiceInf<List<DataHost>> parseJsonDataHost = new DataHostJsonParse();\n\n    public SchemasxmlTozkLoader(ZookeeperProcessListen zookeeperListen, CuratorFramework curator,\n            XmlProcessBase xmlParseBase) {\n\n        this.setCurator(curator);\n\n        // 获得当前集群的名称\n        String schemaPath = zookeeperListen.getBasePath();\n        schemaPath = schemaPath + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FOW_ZK_PATH_SCHEMA.getKey();\n\n        currZkPath = schemaPath;\n        // 将当前自己注册为事件接收对象\n        zookeeperListen.addListen(schemaPath, this);\n\n        // 生成xml与类的转换信息\n        this.parseSchemaXmlService = new SchemasParseXmlImpl(xmlParseBase);\n    }\n\n    @Override\n    public boolean notiflyProcess() throws Exception {\n        // 1,读取本地的xml文件\n        Schemas schema = this.parseSchemaXmlService.parseXmlToBean(SCHEMA_PATH);\n\n        LOGGER.info(\"SchemasxmlTozkLoader notiflyProcessxml to zk schema Object  :\" + schema);\n\n        // 将实体信息写入至zk中\n        this.xmlTozkSchemasJson(currZkPath, schema);\n\n        LOGGER.info(\"SchemasxmlTozkLoader notiflyProcess xml to zk is success\");\n\n        return true;\n    }\n\n    /**\n     * 将xml文件的信息写入到zk中\n    * 方法描述\n    * @param basePath 基本路径\n    * @param schema schema文件的信息\n    * @throws Exception 异常信息\n    * @创建日期 2016年9月17日\n    */\n    private void xmlTozkSchemasJson(String basePath, Schemas schema) throws Exception {\n\n        // 设置schema目录的值\n        String schemaStr = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SCHEMA_SCHEMA.getKey();\n\n        String schemaValueStr = this.parseJsonSchema.parseBeanToJson(schema.getSchema());\n\n        this.checkAndwriteString(basePath, schemaStr, schemaValueStr);\n        // 设置datanode\n        String dataNodeStr = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SCHEMA_DATANODE.getKey();\n\n        String dataNodeValueStr = this.parseJsonDataNode.parseBeanToJson(schema.getDataNode());\n\n        this.checkAndwriteString(basePath, dataNodeStr, dataNodeValueStr);\n\n        // 设置dataHost\n        String dataHostStr = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SCHEMA_DATAHOST.getKey();\n\n        String dataHostValueStr = this.parseJsonDataHost.parseBeanToJson(schema.getDataHost());\n\n        this.checkAndwriteString(basePath, dataHostStr, dataHostValueStr);\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/xmltozk/listen/SequenceTozkLoader.java",
    "content": "package io.mycat.config.loader.zkprocess.xmltozk.listen;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.fastjson.util.IOUtils;\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.NotiflyService;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkMultLoader;\n\n/**\n * 进行从sequence加载到zk中加载\n* 源文件名：SchemasLoader.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class SequenceTozkLoader extends ZkMultLoader implements NotiflyService {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(SequenceTozkLoader.class);\n\n    /**\n     * 当前文件中的zkpath信息 \n    * @字段说明 currZkPath\n    */\n    private final String currZkPath;\n\n    /**\n     * 后缀名\n    * @字段说明 PROPERTIES_SUFFIX\n    */\n    private static final String PROPERTIES_SUFFIX = \".properties\";\n\n    /**\n     * 序列配制信息\n    * @字段说明 PROPERTIES_SEQUENCE_CONF\n    */\n    private static final String PROPERTIES_SEQUENCE_CONF = \"sequence_conf\";\n\n    /**\n     * db序列配制信息\n     * @字段说明 PROPERTIES_SEQUENCE_CONF\n     */\n    private static final String PROPERTIES_SEQUENCE_DB_CONF = \"sequence_db_conf\";\n\n    /**\n     * 分布式的序列配制\n     * @字段说明 PROPERTIES_SEQUENCE_CONF\n     */\n    private static final String PROPERTIES_SEQUENCE_DISTRIBUTED_CONF = \"sequence_distributed_conf\";\n\n    /**\n     * 时间的序列配制\n     * @字段说明 PROPERTIES_SEQUENCE_CONF\n     */\n    private static final String PROPERTIES_SEQUENCE_TIME_CONF = \"sequence_time_conf\";\n\n    public SequenceTozkLoader(ZookeeperProcessListen zookeeperListen, CuratorFramework curator,\n            XmlProcessBase xmlParseBase) {\n\n        this.setCurator(curator);\n\n        // 获得当前集群的名称\n        String schemaPath = zookeeperListen.getBasePath();\n        schemaPath = schemaPath + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE.getKey();\n\n        currZkPath = schemaPath;\n        // 将当前自己注册为事件接收对象\n        zookeeperListen.addListen(schemaPath, this);\n\n    }\n\n    @Override\n    public boolean notiflyProcess() throws Exception {\n\n        // 将zk序列配配制信息入zk\n        this.sequenceTozk(currZkPath, PROPERTIES_SEQUENCE_CONF);\n\n        LOGGER.info(\"SequenceTozkLoader notiflyProcess sequence_conf to zk success\");\n\n        // 将zk的db方式信息入zk\n        this.sequenceTozk(currZkPath, PROPERTIES_SEQUENCE_DB_CONF);\n\n        LOGGER.info(\"SequenceTozkLoader notiflyProcess sequence_db_conf to zk success\");\n\n        // 将zk的分布式信息入zk\n        this.sequenceTozk(currZkPath, PROPERTIES_SEQUENCE_DISTRIBUTED_CONF);\n\n        LOGGER.info(\"SequenceTozkLoader notiflyProcess sequence_distributed_conf to zk success\");\n\n        // 将时间序列入zk\n        this.sequenceTozk(currZkPath, PROPERTIES_SEQUENCE_TIME_CONF);\n\n        LOGGER.info(\"SequenceTozkLoader notiflyProcess sequence_time_conf to zk success\");\n\n        LOGGER.info(\"SequenceTozkLoader notiflyProcess xml to zk is success\");\n\n        return true;\n    }\n\n    /**\n     * 将xml文件的信息写入到zk中\n    * 方法描述\n    * @param basePath 基本路径\n    * @param schema schema文件的信息\n    * @throws Exception 异常信息\n    * @创建日期 2016年9月17日\n    */\n    private void sequenceTozk(String basePath, String name) throws Exception {\n        // 读取当前节的信息\n        String commPath = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_COMMON.getKey()\n                + ZookeeperPath.ZK_SEPARATOR.getKey();\n\n        String readFile = name + PROPERTIES_SUFFIX;\n        // 读取公共节点的信息\n        String commSequence = this.readSequenceCfg(readFile);\n        String sequenceZkPath = commPath + readFile;\n        this.checkAndwriteString(basePath, sequenceZkPath, commSequence);\n\n        // 集群中特有的节点的配制信息\n        String culsterPath = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_CLUSTER.getKey()\n                + ZookeeperPath.ZK_SEPARATOR.getKey();\n\n        String[] clusters = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTER_NODES)\n                .split(ZkParamCfg.ZK_CFG_CLUSTER_NODES_SEPARATE.getKey());\n\n        if (null != clusters) {\n            String nodeName = null;\n            for (String clusterName : clusters) {\n                nodeName = name + \"-\" + clusterName + PROPERTIES_SUFFIX;\n                // 读取当前集群中特有的节点的信息\n                String clusterSequence = this.readSequenceCfg(nodeName);\n\n                // 如果配制了特定节点的信息,则将往上入zk中\n                if (null != clusterSequence) {\n                    String seqclusterZkPath = culsterPath + nodeName;\n                    this.checkAndwriteString(basePath, seqclusterZkPath, clusterSequence);\n                }\n            }\n\n        }\n    }\n\n    /**\n     * 读取 sequence配制文件的信息\n    * 方法描述\n    * @param name 名称信息\n    * @return\n    * @创建日期 2016年9月18日\n    */\n    private String readSequenceCfg(String name) {\n\n        String path = ZookeeperPath.ZK_LOCAL_CFG_PATH.getKey() + name;\n        // 加载数据\n        InputStream input = SequenceTozkLoader.class.getResourceAsStream(path);\n\n        if (null != input) {\n\n            StringBuilder mapFileStr = new StringBuilder();\n\n            byte[] buffers = new byte[256];\n\n            try {\n                int readIndex = -1;\n\n                while ((readIndex = input.read(buffers)) != -1) {\n                    mapFileStr.append(new String(buffers, 0, readIndex));\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n                LOGGER.error(\"SequenceTozkLoader readMapFile IOException\", e);\n\n            } finally {\n                IOUtils.close(input);\n            }\n\n            return mapFileStr.toString();\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/xmltozk/listen/ServerxmlTozkLoader.java",
    "content": "package io.mycat.config.loader.zkprocess.xmltozk.listen;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.fastjson.util.IOUtils;\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.NotiflyService;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.entity.Server;\nimport io.mycat.config.loader.zkprocess.entity.server.System;\nimport io.mycat.config.loader.zkprocess.entity.server.user.User;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.server.json.SystemJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.server.json.UserJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.server.xml.ServerParseXmlImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkMultLoader;\n\n/**\n * 进行从server.xml加载到zk中加载\n* 源文件名：SchemasLoader.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class ServerxmlTozkLoader extends ZkMultLoader implements NotiflyService {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(ServerxmlTozkLoader.class);\n\n    /**\n     * 当前文件中的zkpath信息 \n    * @字段说明 currZkPath\n    */\n    private final String currZkPath;\n\n    /**\n     * server文件的路径信息\n    * @字段说明 SCHEMA_PATH\n    */\n    private static final String SERVER_PATH = ZookeeperPath.ZK_LOCAL_CFG_PATH.getKey() + \"server.xml\";\n\n    /**\n     * index_to_charset文件的路径信息\n     * @字段说明 SCHEMA_PATH\n     */\n    private static final String INDEX_TOCHARSET_PATH = \"index_to_charset.properties\";\n\n    /**\n     * server的xml的转换信息\n    * @字段说明 parseServerXMl\n    */\n    private ParseXmlServiceInf<Server> parseServerXMl;\n\n    /**\n     * system信息\n    * @字段说明 parseJsonSchema\n    */\n    private ParseJsonServiceInf<System> parseJsonSystem = new SystemJsonParse();\n\n    /**\n     * system信息\n     * @字段说明 parseJsonSchema\n     */\n    private ParseJsonServiceInf<List<User>> parseJsonUser = new UserJsonParse();\n\n    public ServerxmlTozkLoader(ZookeeperProcessListen zookeeperListen, CuratorFramework curator,\n            XmlProcessBase xmlParseBase) {\n\n        this.setCurator(curator);\n\n        // 获得当前集群的名称\n        String schemaPath = zookeeperListen.getBasePath();\n        schemaPath = schemaPath + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SERVER.getKey();\n\n        currZkPath = schemaPath;\n        // 将当前自己注册为事件接收对象\n        zookeeperListen.addListen(schemaPath, this);\n\n        // 生成xml与类的转换信息\n        parseServerXMl = new ServerParseXmlImpl(xmlParseBase);\n    }\n\n    @Override\n    public boolean notiflyProcess() throws Exception {\n        // 1,读取本地的xml文件\n        Server server = this.parseServerXMl.parseXmlToBean(SERVER_PATH);\n        LOGGER.info(\"ServerxmlTozkLoader notiflyProcessxml to zk server Object  :\" + server);\n        // 将实体信息写入至zk中\n        this.xmlTozkServerJson(currZkPath, server);\n\n        // 2,读取集群中的节点信息\n        this.writeClusterNode(currZkPath);\n\n        // 读取properties\n        String charSetValue = readProperties(INDEX_TOCHARSET_PATH);\n        // 将文件上传\n        this.checkAndwriteString(currZkPath, INDEX_TOCHARSET_PATH, charSetValue);\n\n        LOGGER.info(\"ServerxmlTozkLoader notiflyProcess xml to zk is success\");\n\n        return true;\n    }\n\n    /**\n     * 写入集群节点的信息\n    * 方法描述\n    * @throws Exception\n    * @创建日期 2016年9月17日\n    */\n    private void writeClusterNode(String basePath) throws Exception {\n        // 1，读取集群节点信息\n        String[] zkNodes = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTER_NODES)\n                .split(ZkParamCfg.ZK_CFG_CLUSTER_NODES_SEPARATE.getKey());\n\n        if (null != zkNodes && zkNodes.length > 0) {\n            for (String node : zkNodes) {\n                String nodePath = ZookeeperPath.ZK_LOCAL_CFG_PATH.getKey() + \"server-\" + node + \".xml\";\n                // 将当前的xml文件写入到zk中\n                Server serverNode = this.parseServerXMl.parseXmlToBean(nodePath);\n\n                LOGGER.info(\"ServerxmlTozkLoader writeClusterNode to zk server Object  :\" + serverNode);\n\n                // 如果当前不存在此配制文件则不写入\n                if (null != serverNode) {\n                    // 以集群的节点的名称写入\n                    this.xmlTozkClusterNodeJson(basePath, node, serverNode);\n\n                    LOGGER.info(\"ServerxmlTozkLoader writeClusterNode xml to zk is success\");\n                }\n            }\n        }\n    }\n\n    /**\n     * 将xml文件的信息写入到zk中\n    * 方法描述\n    * @param basePath 基本路径\n    * @param schema schema文件的信息\n    * @throws Exception 异常信息\n    * @创建日期 2016年9月17日\n    */\n    private void xmlTozkServerJson(String basePath, Server server) throws Exception {\n        // 设置默认的节点信息\n        String defaultSystem = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SERVER_DEFAULT.getKey();\n        String defaultSystemValue = this.parseJsonSystem.parseBeanToJson(server.getSystem());\n        this.checkAndwriteString(basePath, defaultSystem, defaultSystemValue);\n\n        // 设置用户信息\n        String userStr = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SERVER_USER.getKey();\n        String userValueStr = this.parseJsonUser.parseBeanToJson(server.getUser());\n        this.checkAndwriteString(basePath, userStr, userValueStr);\n    }\n\n    /**\n     * 将xml文件的信息写入到zk中\n    * 方法描述\n    * @param basePath 基本路径\n    * @param schema schema文件的信息\n    * @throws Exception 异常信息\n    * @创建日期 2016年9月17日\n    */\n    private void xmlTozkClusterNodeJson(String basePath, String node, Server server) throws Exception {\n        // 设置集群中的节点信息\n        basePath = basePath + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SERVER_CLUSTER.getKey();\n        String clusterSystemValue = this.parseJsonSystem.parseBeanToJson(server.getSystem());\n        this.checkAndwriteString(basePath, node, clusterSystemValue);\n    }\n\n    /**\n     * 读取 properties配制文件的信息\n    * 方法描述\n    * @param name 名称信息\n    * @return\n    * @创建日期 2016年9月18日\n    */\n    private String readProperties(String name) {\n\n        String path = ZookeeperPath.ZK_LOCAL_CFG_PATH.getKey() + name;\n        // 加载数据\n        InputStream input = SequenceTozkLoader.class.getResourceAsStream(path);\n\n        if (null != input) {\n\n            StringBuilder mapFileStr = new StringBuilder();\n\n            byte[] buffers = new byte[256];\n\n            try {\n                int readIndex = -1;\n\n                while ((readIndex = input.read(buffers)) != -1) {\n                    mapFileStr.append(new String(buffers, 0, readIndex));\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n                LOGGER.error(\"SequenceTozkLoader readMapFile IOException\", e);\n\n            } finally {\n                IOUtils.close(input);\n            }\n\n            return mapFileStr.toString();\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zktoxml/ZktoXmlMain.java",
    "content": "package io.mycat.config.loader.zkprocess.zktoxml;\n\nimport java.util.Set;\n\nimport io.mycat.config.loader.zkprocess.zktoxml.command.CommandPathListener;\nimport io.mycat.config.loader.zkprocess.zktoxml.listen.*;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.recipes.cache.NodeCache;\nimport org.apache.curator.framework.recipes.cache.NodeCacheListener;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCache;\nimport org.apache.curator.utils.ZKPaths;\nimport org.apache.zookeeper.CreateMode;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.console.ZkNofiflyCfg;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkMultLoader;\nimport io.mycat.manager.handler.ZKHandler;\nimport io.mycat.migrate.MigrateTaskWatch;\nimport io.mycat.util.ZKUtils;\n\n/**\n * 将xk的信息转换为xml文件的操作\n* 源文件名：ZktoxmlMain.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月20日\n* 修改作者：liujun\n* 修改日期：2016年9月20日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class ZktoXmlMain {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(ZkMultLoader.class);\n\n    /**\n     * 加载zk监听服务\n     */\n    public static final ZookeeperProcessListen ZKLISTENER = new ZookeeperProcessListen();\n\n    public static void main(String[] args) throws Exception {\n        loadZktoFile();\n        System.out.println(Long.MAX_VALUE);\n    }\n\n    /**\n     * 将zk数据放到到本地\n    * 方法描述\n     * @throws Exception \n     * @创建日期 2016年9月21日\n    */\n    public static void loadZktoFile() throws Exception {\n\n        // 得到集群名称\n        String custerName = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTERID);\n        // 得到基本路径\n        String basePath = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_BASE.getKey();\n        basePath = basePath + ZookeeperPath.ZK_SEPARATOR.getKey() + custerName;\n        ZKLISTENER.setBasePath(basePath);\n\n        // 获得zk的连接信息\n        CuratorFramework zkConn = buildConnection(ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_URL));\n\n        // 获得公共的xml转换器对象\n        XmlProcessBase xmlProcess = new XmlProcessBase();\n\n        // 加载以接收者\n        new SchemaszkToxmlLoader(ZKLISTENER, zkConn, xmlProcess);\n\n        // server加载\n        new ServerzkToxmlLoader(ZKLISTENER, zkConn, xmlProcess);\n\n        // rule文件加载\n        // new RuleszkToxmlLoader(zkListen, zkConn, xmlProcess);\n        ZKUtils.addChildPathCache(ZKUtils.getZKBasePath() + \"rules\", new RuleFunctionCacheListener());\n        // 将序列配制信息加载\n        new SequenceTopropertiesLoader(ZKLISTENER, zkConn, xmlProcess);\n\n        // 进行ehcache转换\n        new EcacheszkToxmlLoader(ZKLISTENER, zkConn, xmlProcess);\n\n        // 将bindata目录的数据进行转换到本地文件\n        ZKUtils.addChildPathCache(ZKUtils.getZKBasePath() + \"bindata\", new BinDataPathChildrenCacheListener());\n\n        // ruledata\n        ZKUtils.addChildPathCache(ZKUtils.getZKBasePath() + \"ruledata\", new RuleDataPathChildrenCacheListener());\n\n        // 初始化xml转换操作\n        xmlProcess.initJaxbClass();\n\n        // 通知所有人\n        ZKLISTENER.notifly(ZkNofiflyCfg.ZK_NOTIFLY_LOAD_ALL.getKey());\n\n        // 加载watch\n        loadZkWatch(ZKLISTENER.getWatchPath(), zkConn, ZKLISTENER);\n\n        // 创建临时节点\n        createTempNode(ZKUtils.getZKBasePath() + \"line\", ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID),\n                zkConn, ZkConfig.getInstance().getValue(ZkParamCfg.MYCAT_SERVER_TYPE));\n\n        // 接收zk发送过来的命令\n        runCommandWatch(zkConn, ZKUtils.getZKBasePath() + ZKHandler.ZK_NODE_PATH);\n\n        MigrateTaskWatch.start();\n    }\n\n    private static void loadZkWatch(Set<String> setPaths, final CuratorFramework zkConn,\n            final ZookeeperProcessListen zkListen) throws Exception {\n\n        if (null != setPaths && !setPaths.isEmpty()) {\n            for (String path : setPaths) {\n                // 进行本地节点的监控操作\n                NodeCache node = runWatch(zkConn, path, zkListen);\n                node.start();\n\n                LOGGER.info(\"ZktoxmlMain loadZkWatch path:\" + path + \" regist success\");\n            }\n        }\n    }\n\n    /**\n     * 进行命令的监听操作\n    * 方法描述\n    * @param zkConn zk的连接信息\n    * @param path 路径信息\n    * @param ZKLISTENER 监控路径信息\n    * @throws Exception\n    * @创建日期 2016年9月20日\n    */\n    @SuppressWarnings(\"resource\")\n    private static void runCommandWatch(final CuratorFramework zkConn, final String path) throws Exception {\n\n        PathChildrenCache children = new PathChildrenCache(zkConn, path, true);\n\n        CommandPathListener commandListener = new CommandPathListener();\n\n        // 移除原来的监听再进行添加\n        children.getListenable().addListener(commandListener);\n\n        children.start();\n    }\n\n    /**\n     * 创建临时节点测试\n    * 方法描述\n    * @param parent\n    * @param node\n    * @param zkConn\n    * @throws Exception\n    * @创建日期 2016年9月20日\n    */\n    private static void createTempNode(String parent, String node, final CuratorFramework zkConn, String type)\n            throws Exception {\n\n        String path = ZKPaths.makePath(parent, node);\n\n        zkConn.create().withMode(CreateMode.EPHEMERAL).inBackground().forPath(path, type.getBytes(\"UTF-8\"));\n\n    }\n\n    /**\n     * 进行zk的watch操作\n    * 方法描述\n    * @param zkConn zk的连接信息\n    * @param path 路径信息\n    * @param zkListen 监控路径信息\n    * @throws Exception\n    * @创建日期 2016年9月20日\n    */\n    private static NodeCache runWatch(final CuratorFramework zkConn, final String path,\n            final ZookeeperProcessListen zkListen) throws Exception {\n        final NodeCache cache = new NodeCache(zkConn, path);\n\n        NodeCacheListener listen = new NodeCacheListener() {\n            @Override\n            public void nodeChanged() throws Exception {\n                LOGGER.info(\"ZktoxmlMain runWatch  process path  event start \");\n                LOGGER.info(\"NodeCache changed, path is: \" + cache.getCurrentData().getPath());\n                String notPath = cache.getCurrentData().getPath();\n                // 进行通知更新\n                zkListen.notifly(notPath);\n                LOGGER.info(\"ZktoxmlMain runWatch  process path  event over\");\n            }\n        };\n\n        // 添加监听\n        cache.getListenable().addListener(listen);\n\n        return cache;\n    }\n\n    private static CuratorFramework buildConnection(String url) {\n\n        return ZKUtils.getConnection();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zktoxml/command/CommandPathListener.java",
    "content": "package io.mycat.config.loader.zkprocess.zktoxml.command;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.util.concurrent.FutureCallback;\nimport com.google.common.util.concurrent.Futures;\nimport com.google.common.util.concurrent.ListenableFuture;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.loader.zkprocess.console.ZkNofiflyCfg;\nimport io.mycat.config.loader.zkprocess.zktoxml.ZktoXmlMain;\nimport io.mycat.manager.handler.ZKHandler;\nimport io.mycat.manager.response.ReloadConfig;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.util.ZKUtils;\n\n/**\n * zk命令监听器\n * @author kk\n * @date 2017年1月18日\n * @version 0.0.1\n */\npublic class CommandPathListener implements PathChildrenCacheListener {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(CommandPathListener.class);\n\n    @Override\n    public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {\n        switch (event.getType()) {\n        case CHILD_ADDED:\n            // 在发生节点添加的时候，则执行接收命令并执行\n            // 1,首先检查\n            String path = event.getData().getPath();\n            String basePath = ZKUtils.getZKBasePath() + ZKHandler.ZK_NODE_PATH + \"/\";\n\n            // 检查节点与当前的节点是否一致\n            String node = path.substring(basePath.length());\n\n            if (node.equals(ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID))) {\n                // 检查命令内容是否为\n                if (ZKHandler.RELOAD_FROM_ZK.equals(new String(client.getData().forPath(path)))) {\n                    // 从服务器上下载最新的配制文件信息\n                    ZktoXmlMain.ZKLISTENER.notifly(ZkNofiflyCfg.ZK_NOTIFLY_LOAD_ALL.getKey());\n                    // 重新加载配制信息\n                    reload(path);\n                    // 完成之后，删除命令信息， 以供下次读取\n                    client.delete().forPath(event.getData().getPath());\n                    LOGGER.info(\"CommandPathListener path:\" + path + \" reload success\");\n                }\n            }\n\n            break;\n        case CHILD_UPDATED:\n            break;\n        case CHILD_REMOVED:\n            break;\n        default:\n            break;\n        }\n\n    }\n\n    public void reload(final String path) {\n        // reload @@config_all 校验前一次的事务完成情况\n        if (!NIOProcessor.backends_old.isEmpty()) {\n            return;\n        }\n\n        final ReentrantLock lock = MycatServer.getInstance().getConfig().getLock();\n        lock.lock();\n        try {\n            ListenableFuture<Boolean> listenableFuture = MycatServer.getInstance().getListeningExecutorService()\n                    .submit(new Callable<Boolean>() {\n                        @Override\n                        public Boolean call() throws Exception {\n                            return ReloadConfig.reload_all();\n                        }\n                    });\n            Futures.addCallback(listenableFuture, new FutureCallback<Boolean>() {\n                @Override\n                public void onSuccess(Boolean result) {\n                    LOGGER.info(\"CommandPathListener path:\" + path + \" reload success\");\n                }\n\n                @Override\n                public void onFailure(Throwable t) {\n                    LOGGER.error(\"CommandPathListener path:\" + path + \" reload error\", t);\n                }\n\n            }, MycatServer.getInstance().getListeningExecutorService());\n        } finally {\n            lock.unlock();\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zktoxml/listen/BinDataPathChildrenCacheListener.java",
    "content": "package io.mycat.config.loader.zkprocess.zktoxml.listen;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.recipes.cache.ChildData;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;\n\nimport com.google.common.io.Files;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.model.SystemConfig;\n\n/**\n * Created by magicdoom on 2016/10/27.\n */\npublic class BinDataPathChildrenCacheListener implements PathChildrenCacheListener {\n    @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {\n        ChildData data = event.getData();\n        switch (event.getType()) {\n\n            case CHILD_ADDED:\n\n                add(data.getPath().substring(data.getPath().lastIndexOf(\"/\")+1),event.getData().getData()) ;\n                break;\n            case CHILD_REMOVED:\n                delete(data.getPath().substring(data.getPath().lastIndexOf(\"/\")+1),event.getData().getData()); ;\n                break;\n            case CHILD_UPDATED:\n                add(data.getPath().substring(data.getPath().lastIndexOf(\"/\")+1),event.getData().getData()) ;\n                break;\n            default:\n                break;\n        }\n    }\n\n    private void add(String name,byte[] data) throws IOException {\n        File file = new File(\n                SystemConfig.getHomePath() + File.separator + \"conf\" ,\n                name);\n        Files.write(data,file);\n        //try to reload dnindex\n        if(\"dnindex.properties\".equals(name)) {\n            MycatServer.getInstance().reloadDnIndex();\n        }\n    }\n\n    private void delete(String name,byte[] data) throws IOException {\n        File file = new File(\n                SystemConfig.getHomePath() + File.separator + \"conf\" ,\n                name);\n        if(file.exists())\n         file.delete();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zktoxml/listen/EcacheszkToxmlLoader.java",
    "content": "package io.mycat.config.loader.zkprocess.zktoxml.listen;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.io.Files;\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.NotiflyService;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.entity.cache.Ehcache;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.cache.json.EhcacheJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.cache.xml.EhcacheParseXmlImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.DataInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.DiretoryInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkDataImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkDirectoryImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkMultLoader;\n\n/**\n * 进行从ecache.xml加载到zk中加载\n* 源文件名：SchemasLoader.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class EcacheszkToxmlLoader extends ZkMultLoader implements NotiflyService {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(EcacheszkToxmlLoader.class);\n\n    /**\n     * 当前文件中的zkpath信息 \n    * @字段说明 currZkPath\n    */\n    private final String currZkPath;\n\n    /**\n     * 缓存文件名称\n    * @字段说明 CACHESERVER_NAME\n    */\n    private static final String CACHESERVER_NAME = \"cacheservice.properties\";\n\n    /**\n     * 缓存的xml文件配制信息\n    * @字段说明 EHCACHE_NAME\n    */\n    private static final String EHCACHE_NAME = \"ehcache.xml\";\n\n    /**\n     * ehcache的xml的转换信息\n    * @字段说明 parseEhcacheXMl\n    */\n    private final ParseXmlServiceInf<Ehcache> parseEcacheXMl;\n\n    /**\n     * 表的路由信息\n    * @字段说明 parseJsonService\n    */\n    private ParseJsonServiceInf<Ehcache> parseJsonEhcacheService = new EhcacheJsonParse();\n\n    /**\n     * 监控类信息\n    * @字段说明 zookeeperListen\n    */\n    private ZookeeperProcessListen zookeeperListen;\n\n    public EcacheszkToxmlLoader(ZookeeperProcessListen zookeeperListen, CuratorFramework curator,\n            XmlProcessBase xmlParseBase) {\n\n        this.setCurator(curator);\n\n        this.zookeeperListen = zookeeperListen;\n\n        // 获得当前集群的名称\n        String schemaPath = zookeeperListen.getBasePath();\n        schemaPath = schemaPath + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_CACHE.getKey();\n\n        currZkPath = schemaPath;\n        // 将当前自己注册为事件接收对象\n        this.zookeeperListen.addListen(schemaPath, this);\n\n        // 生成xml与类的转换信息\n        parseEcacheXMl = new EhcacheParseXmlImpl(xmlParseBase);\n    }\n\n    @Override\n    public boolean notiflyProcess() throws Exception {\n\n        // 通过组合模式进行zk目录树的加载\n        DiretoryInf RulesDirectory = new ZkDirectoryImpl(currZkPath, null);\n        // 进行递归的数据获取\n        this.getTreeDirectory(currZkPath, ZookeeperPath.FLOW_ZK_PATH_CACHE.getKey(), RulesDirectory);\n\n        // 从当前的下一级开始进行遍历,获得到\n        ZkDirectoryImpl zkDirectory = (ZkDirectoryImpl) RulesDirectory.getSubordinateInfo().get(0);\n\n        // 进行写入操作\n        zktoEhcacheWrite(zkDirectory);\n\n        LOGGER.info(\"EcacheszkToxmlLoader notiflyProcess   zk ehcache write success \");\n\n        return true;\n    }\n\n    /**\n     * 将zk上面的信息转换为javabean对象\n    * 方法描述\n    * @param zkDirectory\n    * @return\n    * @创建日期 2016年9月17日\n    */\n    private void zktoEhcacheWrite(ZkDirectoryImpl zkDirectory) {\n\n        // 得到schema对象的目录信息\n        DataInf ehcacheZkDirectory = this.getZkData(zkDirectory, EHCACHE_NAME);\n\n        Ehcache ehcache = parseJsonEhcacheService.parseJsonToBean(ehcacheZkDirectory.getDataValue());\n\n        String outputPath = EcacheszkToxmlLoader.class.getClassLoader()\n                .getResource(ZookeeperPath.ZK_LOCAL_WRITE_PATH.getKey()).getPath();\n        outputPath = new File(outputPath).getPath() + File.separator;\n        outputPath += EHCACHE_NAME;\n\n        parseEcacheXMl.parseToXmlWrite(ehcache, outputPath, null);\n\n        // 设置zk监控的路径信息\n        String watchPath = zkDirectory.getName();\n        watchPath = watchPath + ZookeeperPath.ZK_SEPARATOR.getKey() + EHCACHE_NAME;\n        this.zookeeperListen.watchPath(currZkPath, watchPath);\n\n        // 写入cacheservice.properties的信息\n        DataInf cacheserZkDirectory = this.getZkData(zkDirectory, CACHESERVER_NAME);\n\n        if (null != cacheserZkDirectory) {\n            ZkDataImpl cacheData = (ZkDataImpl) cacheserZkDirectory;\n\n            // 写入文件cacheservice.properties\n            this.writeCacheservice(cacheData.getName(), cacheData.getValue());\n\n            String watchServerPath = zkDirectory.getName();\n            watchServerPath = watchPath + ZookeeperPath.ZK_SEPARATOR.getKey() + CACHESERVER_NAME;\n            this.zookeeperListen.watchPath(currZkPath, watchServerPath);\n        }\n\n    }\n\n    /**\n     * 读取 mapFile文件的信息\n    * 方法描述\n    * @param name 名称信息\n    * @return\n    * @创建日期 2016年9月18日\n    */\n    private void writeCacheservice(String name, String value) {\n\n        // 加载数据\n        String path = RuleszkToxmlLoader.class.getClassLoader().getResource(ZookeeperPath.ZK_LOCAL_WRITE_PATH.getKey())\n                .getPath();\n\n        checkNotNull(path, \"write ecache file curr Path :\" + path + \" is null! must is not null\");\n        path = new File(path).getPath() + File.separator;\n        path += name;\n\n        // 进行数据写入\n        try {\n            Files.write(value.getBytes(), new File(path));\n        } catch (IOException e1) {\n            e1.printStackTrace();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zktoxml/listen/RuleDataPathChildrenCacheListener.java",
    "content": "package io.mycat.config.loader.zkprocess.zktoxml.listen;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.config.model.rule.RuleConfig;\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\nimport io.mycat.route.function.ReloadFunction;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.recipes.cache.ChildData;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.StandardOpenOption;\nimport java.util.Map;\n\n/**\n * Created by magicdoom on 2016/10/27.\n */\npublic class RuleDataPathChildrenCacheListener implements PathChildrenCacheListener {\n    @Override\n    public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {\n        ChildData data = event.getData();\n        switch (event.getType()) {\n\n            case CHILD_ADDED:\n\n                add(data.getPath().substring(data.getPath().lastIndexOf(\"/\") + 1), event.getData().getData());\n                break;\n            case CHILD_REMOVED:\n                delete(data.getPath().substring(data.getPath().lastIndexOf(\"/\") + 1), event.getData().getData());\n                ;\n                break;\n            case CHILD_UPDATED:\n                add(data.getPath().substring(data.getPath().lastIndexOf(\"/\") + 1), event.getData().getData());\n                break;\n            default:\n                break;\n        }\n    }\n\n\n    private void reloadRuleData(String name) {\n        String tableName = name.substring(name.lastIndexOf(\"_\") + 1, name.indexOf(\".\"));\n        String ruleName = name.substring(0, name.indexOf(\".\"));\n        Map<String, SchemaConfig> schemaConfigMap = MycatServer.getInstance().getConfig().getSchemas();\n        for (SchemaConfig schemaConfig : schemaConfigMap.values()) {\n            TableConfig tableConfig = schemaConfig.getTables().get(tableName.toUpperCase());\n            if (tableConfig == null) continue;\n            RuleConfig rule = tableConfig.getRule();\n            AbstractPartitionAlgorithm function = rule.getRuleAlgorithm();\n            if (function instanceof ReloadFunction) {\n                ((ReloadFunction) function).reload();\n            }\n        }\n    }\n\n    private void add(String name, byte[] data) throws IOException {\n        Path ruledataPath = Paths.get(SystemConfig.getHomePath(), \"conf\", \"ruledata\");\n        if (!Files.exists(ruledataPath)) {\n            Files.createDirectory(ruledataPath);\n        }\n        Path file = ruledataPath.resolve(name);\n        Files.write(file, data, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);\n        reloadRuleData(name);\n    }\n\n    private void delete(String name, byte[] data) throws IOException {\n        File file = new File(\n                SystemConfig.getHomePath() + File.separator + \"conf\" + File.separator + \"ruledata\",\n                name);\n        if (file.exists())\n            file.delete();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zktoxml/listen/RuleFunctionCacheListener.java",
    "content": "package io.mycat.config.loader.zkprocess.zktoxml.listen;\n\nimport com.google.common.io.Files;\nimport io.mycat.MycatServer;\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.console.ParseParamEnum;\nimport io.mycat.config.loader.zkprocess.entity.Property;\nimport io.mycat.config.loader.zkprocess.entity.Rules;\nimport io.mycat.config.loader.zkprocess.entity.rule.function.Function;\nimport io.mycat.config.loader.zkprocess.entity.rule.tablerule.TableRule;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.rule.json.FunctionJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.rule.json.TableRuleJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.rule.xml.RuleParseXmlImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.DataInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.DiretoryInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkDataImpl;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.config.model.rule.RuleConfig;\nimport io.mycat.manager.response.ReloadConfig;\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\nimport io.mycat.route.function.ReloadFunction;\nimport io.mycat.util.ZKUtils;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.recipes.cache.ChildData;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.xml.bind.JAXBException;\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\n/**\n * Created by magicdoom on 2016/10/27.\n */\npublic class RuleFunctionCacheListener implements PathChildrenCacheListener {\n    private static final Logger LOGGER = LoggerFactory.getLogger(RuleFunctionCacheListener.class);\n    @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {\n        ChildData data = event.getData();\n        switch (event.getType()) {\n\n            case CHILD_ADDED:\n                addOrUpdate();\n                break;\n            case CHILD_UPDATED:\n                addOrUpdate();\n                break;\n            default:\n                break;\n        }\n    }\n\n    public RuleFunctionCacheListener() {\n        XmlProcessBase xmlProcessBase = new XmlProcessBase();\n\n        parseRulesXMl = new RuleParseXmlImpl(xmlProcessBase) ;\n        try {\n            xmlProcessBase.initJaxbClass();\n        } catch (JAXBException e) {\n            LOGGER.error(\"error\",e);\n        }\n    }\n\n    private void addOrUpdate()\n  {\n      Rules Rules = null;\n      try {\n          Rules = this.zktoRulesBean();\n      } catch (Exception e) {\n          LOGGER.error(\"error\",e);\n      }\n\n      LOGGER.info(\"RuleszkToxmlLoader notiflyProcess zk to object  zk Rules Object  :\" + Rules);\n\n      // 将mapfile信息写入到文件 中\n      writeMapFileAddFunction(Rules.getFunction());\n\n      LOGGER.info(\"RuleszkToxmlLoader notiflyProcess write mapFile is success \");\n\n      // 数配制信息写入文件\n      String path = RuleszkToxmlLoader.class.getClassLoader().getResource(ZookeeperPath.ZK_LOCAL_WRITE_PATH.getKey())\n              .getPath();\n      path = new File(path).getPath() + File.separator;\n      path = path + WRITEPATH;\n\n      LOGGER.info(\"RuleszkToxmlLoader notiflyProcess zk to object writePath :\" + path);\n\n      this.parseRulesXMl.parseToXmlWrite(Rules, path, \"rule\");\n\n      LOGGER.info(\"RuleszkToxmlLoader notiflyProcess zk to object zk Rules      write :\" + path + \" is success\");\n\n      if (MycatServer.getInstance().getProcessors() != null)\n          ReloadConfig.reload();\n\n  }\n\n\n    private static final String WRITEPATH = \"rule.xml\";\n\n    /**\n     * Rules的xml的转换信息\n     * @字段说明 parseRulesXMl\n     */\n    private ParseXmlServiceInf<Rules> parseRulesXMl;;\n\n    /**\n     * 表的路由信息\n     * @字段说明 parseJsonService\n     */\n    private ParseJsonServiceInf<List<TableRule>> parseJsonTableRuleService = new TableRuleJsonParse();\n\n    /**\n     * 表对应的字段信息\n     * @字段说明 parseJsonFunctionService\n     */\n    private ParseJsonServiceInf<List<Function>> parseJsonFunctionService = new FunctionJsonParse();\n\n\n    private Rules zktoRulesBean() throws Exception {\n        Rules Rules = new Rules();\n\n        // tablerule信息\n     String value=  new String( ZKUtils.getConnection().getData().forPath(ZKUtils.getZKBasePath()+\"rules/tableRule\"),\"UTF-8\") ;\n        DataInf RulesZkData = new ZkDataImpl(\"tableRule\",value);\n        List<TableRule> tableRuleData = parseJsonTableRuleService.parseJsonToBean(RulesZkData.getDataValue());\n        Rules.setTableRule(tableRuleData);\n\n\n\n        // 得到function信息\n        String fucValue=  new String( ZKUtils.getConnection().getData().forPath(ZKUtils.getZKBasePath()+\"rules/function\"),\"UTF-8\") ;\n        DataInf functionZkData =new ZkDataImpl(\"function\",fucValue) ;\n        List<Function> functionList = parseJsonFunctionService.parseJsonToBean(functionZkData.getDataValue());\n        Rules.setFunction(functionList);\n\n\n\n        return Rules;\n    }\n\n\n    /**\n     *  读取序列配制文件便利店\n     * 方法描述\n     * @param functionList\n     * @创建日期 2016年9月18日\n     */\n    private void writeMapFileAddFunction(List<Function> functionList) {\n\n        List<Property> tempData = new ArrayList<>();\n\n        List<Property> writeData = new ArrayList<>();\n\n        for (Function function : functionList) {\n            List<Property> proList = function.getProperty();\n            if (null != proList && !proList.isEmpty()) {\n                // 进行数据遍历\n                for (Property property : proList) {\n                    // 如果为mapfile，则需要去读取数据信息，并存到json中\n                    if (ParseParamEnum.ZK_PATH_RULE_MAPFILE_NAME.getKey().equals(property.getName())) {\n                        tempData.add(property);\n                    }\n                }\n\n                // 通过mapfile的名称，找到对应的数据信息\n                if (!tempData.isEmpty()) {\n                    for (Property property : tempData) {\n                        for (Property prozkdownload : proList) {\n                            // 根据mapfile的文件名去提取数据\n                            if (property.getValue().equals(prozkdownload.getName())) {\n                                writeData.add(prozkdownload);\n                            }\n                        }\n                    }\n                }\n\n                // 将对应的数据信息写入到磁盘中\n                if (!writeData.isEmpty()) {\n                    for (Property writeMsg : writeData) {\n                        this.writeMapFile(writeMsg.getName(), writeMsg.getValue());\n                    }\n                }\n\n                // 将数据添加的集合中\n                proList.removeAll(writeData);\n\n                // 清空，以进行下一次的添加\n                tempData.clear();\n                writeData.clear();\n            }\n        }\n\n    }\n\n    /**\n     * 读取 mapFile文件的信息\n     * 方法描述\n     * @param name 名称信息\n     * @return\n     * @创建日期 2016年9月18日\n     */\n    private void writeMapFile(String name, String value) {\n\n        // 加载数据\n        String path = RuleszkToxmlLoader.class.getClassLoader().getResource(ZookeeperPath.ZK_LOCAL_WRITE_PATH.getKey())\n                .getPath();\n\n        checkNotNull(path, \"write Map file curr Path :\" + path + \" is null! must is not null\");\n        path = new File(path).getPath() + File.separator;\n        path += name;\n\n        // 进行数据写入\n        try {\n            Files.write(value.getBytes(), new File(path));\n        } catch (IOException e1) {\n            e1.printStackTrace();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zktoxml/listen/RuleszkToxmlLoader.java",
    "content": "package io.mycat.config.loader.zkprocess.zktoxml.listen;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.io.Files;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.NotiflyService;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.console.ParseParamEnum;\nimport io.mycat.config.loader.zkprocess.entity.Property;\nimport io.mycat.config.loader.zkprocess.entity.Rules;\nimport io.mycat.config.loader.zkprocess.entity.rule.function.Function;\nimport io.mycat.config.loader.zkprocess.entity.rule.tablerule.TableRule;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.rule.json.FunctionJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.rule.json.TableRuleJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.rule.xml.RuleParseXmlImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.DataInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.DiretoryInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkDirectoryImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkMultLoader;\nimport io.mycat.manager.response.ReloadConfig;\n\n/**\n * 进行rule的文件从zk中加载\n* 源文件名：RuleszkToxmlLoader.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class RuleszkToxmlLoader extends ZkMultLoader implements NotiflyService {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(RuleszkToxmlLoader.class);\n\n    /**\n     * 当前文件中的zkpath信息 \n    * @字段说明 currZkPath\n    */\n    private final String currZkPath;\n\n    /**\n     * 写入本地的文件路径\n    * @字段说明 WRITEPATH\n    */\n    private static final String WRITEPATH = \"rule.xml\";\n\n    /**\n     * Rules的xml的转换信息\n    * @字段说明 parseRulesXMl\n    */\n    private ParseXmlServiceInf<Rules> parseRulesXMl;\n\n    /**\n     * 表的路由信息\n    * @字段说明 parseJsonService\n    */\n    private ParseJsonServiceInf<List<TableRule>> parseJsonTableRuleService = new TableRuleJsonParse();\n\n    /**\n     * 表对应的字段信息\n    * @字段说明 parseJsonFunctionService\n    */\n    private ParseJsonServiceInf<List<Function>> parseJsonFunctionService = new FunctionJsonParse();\n\n    /**\n     * zk的监控路径信息\n    * @字段说明 zookeeperListen\n    */\n    private ZookeeperProcessListen zookeeperListen;\n\n    public RuleszkToxmlLoader(ZookeeperProcessListen zookeeperListen, CuratorFramework curator,\n            XmlProcessBase xmlParseBase) {\n\n        this.setCurator(curator);\n\n        this.zookeeperListen = zookeeperListen;\n\n        // 获得当前集群的名称\n        String RulesPath = zookeeperListen.getBasePath();\n        RulesPath = RulesPath + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_RULE.getKey();\n\n        currZkPath = RulesPath;\n        // 将当前自己注册为事件接收对象\n        zookeeperListen.addListen(RulesPath, this);\n\n        // 生成xml与类的转换信息\n        parseRulesXMl = new RuleParseXmlImpl(xmlParseBase);\n    }\n\n    @Override\n    public boolean notiflyProcess() throws Exception {\n        // 1,将集群Rules目录下的所有集群按层次结构加载出来\n        // 通过组合模式进行zk目录树的加载\n        DiretoryInf RulesDirectory = new ZkDirectoryImpl(currZkPath, null);\n        // 进行递归的数据获取\n        this.getTreeDirectory(currZkPath, ZookeeperPath.FLOW_ZK_PATH_RULE.getKey(), RulesDirectory);\n\n        // 从当前的下一级开始进行遍历,获得到\n        ZkDirectoryImpl zkDirectory = (ZkDirectoryImpl) RulesDirectory.getSubordinateInfo().get(0);\n        Rules Rules = this.zktoRulesBean(zkDirectory);\n\n        LOGGER.info(\"RuleszkToxmlLoader notiflyProcess zk to object  zk Rules Object  :\" + Rules);\n\n        // 将mapfile信息写入到文件 中\n        writeMapFileAddFunction(Rules.getFunction());\n\n        LOGGER.info(\"RuleszkToxmlLoader notiflyProcess write mapFile is success \");\n\n        // 数配制信息写入文件\n        String path = RuleszkToxmlLoader.class.getClassLoader().getResource(ZookeeperPath.ZK_LOCAL_WRITE_PATH.getKey())\n                .getPath();\n        path = new File(path).getPath() + File.separator;\n        path = path + WRITEPATH;\n\n        LOGGER.info(\"RuleszkToxmlLoader notiflyProcess zk to object writePath :\" + path);\n\n        this.parseRulesXMl.parseToXmlWrite(Rules, path, \"rule\");\n\n        LOGGER.info(\"RuleszkToxmlLoader notiflyProcess zk to object zk Rules      write :\" + path + \" is success\");\n\n        if (MycatServer.getInstance().getProcessors() != null)\n            ReloadConfig.reload();\n\n        return true;\n    }\n\n    /**\n     * 将zk上面的信息转换为javabean对象\n    * 方法描述\n    * @param zkDirectory\n    * @return\n    * @创建日期 2016年9月17日\n    */\n    private Rules zktoRulesBean(DiretoryInf zkDirectory) {\n        Rules Rules = new Rules();\n\n        // tablerule信息\n        DataInf RulesZkData = this.getZkData(zkDirectory, ZookeeperPath.FLOW_ZK_PATH_RULE_TABLERULE.getKey());\n        List<TableRule> tableRuleData = parseJsonTableRuleService.parseJsonToBean(RulesZkData.getDataValue());\n        Rules.setTableRule(tableRuleData);\n\n        // tablerule的监控路径信息\n        String watchPath = ZookeeperPath.FLOW_ZK_PATH_RULE.getKey();\n        watchPath = watchPath + ZookeeperPath.ZK_SEPARATOR.getKey()\n                + ZookeeperPath.FLOW_ZK_PATH_RULE_TABLERULE.getKey();\n        this.zookeeperListen.watchPath(currZkPath, watchPath);\n\n        // 得到function信息\n        DataInf functionZkData = this.getZkData(zkDirectory, ZookeeperPath.FLOW_ZK_PATH_RULE_FUNCTION.getKey());\n        List<Function> functionList = parseJsonFunctionService.parseJsonToBean(functionZkData.getDataValue());\n        Rules.setFunction(functionList);\n\n        // function的监控路径信息\n        String functionWatchPath = ZookeeperPath.FLOW_ZK_PATH_RULE.getKey();\n        functionWatchPath = functionWatchPath + ZookeeperPath.ZK_SEPARATOR.getKey()\n                + ZookeeperPath.FLOW_ZK_PATH_RULE_FUNCTION.getKey();\n        this.zookeeperListen.watchPath(currZkPath, functionWatchPath);\n\n        return Rules;\n    }\n\n    /**\n     *  读取序列配制文件便利店  \n    * 方法描述\n    * @param functionList\n    * @创建日期 2016年9月18日\n    */\n    private void writeMapFileAddFunction(List<Function> functionList) {\n\n        List<Property> tempData = new ArrayList<>();\n\n        List<Property> writeData = new ArrayList<>();\n\n        for (Function function : functionList) {\n            List<Property> proList = function.getProperty();\n            if (null != proList && !proList.isEmpty()) {\n                // 进行数据遍历\n                for (Property property : proList) {\n                    // 如果为mapfile，则需要去读取数据信息，并存到json中\n                    if (ParseParamEnum.ZK_PATH_RULE_MAPFILE_NAME.getKey().equals(property.getName())) {\n                        tempData.add(property);\n                    }\n                }\n\n                // 通过mapfile的名称，找到对应的数据信息\n                if (!tempData.isEmpty()) {\n                    for (Property property : tempData) {\n                        for (Property prozkdownload : proList) {\n                            // 根据mapfile的文件名去提取数据\n                            if (property.getValue().equals(prozkdownload.getName())) {\n                                writeData.add(prozkdownload);\n                            }\n                        }\n                    }\n                }\n\n                // 将对应的数据信息写入到磁盘中\n                if (!writeData.isEmpty()) {\n                    for (Property writeMsg : writeData) {\n                        this.writeMapFile(writeMsg.getName(), writeMsg.getValue());\n                    }\n                }\n\n                // 将数据添加的集合中\n                proList.removeAll(writeData);\n\n                // 清空，以进行下一次的添加\n                tempData.clear();\n                writeData.clear();\n            }\n        }\n\n    }\n\n    /**\n     * 读取 mapFile文件的信息\n    * 方法描述\n    * @param name 名称信息\n    * @return\n    * @创建日期 2016年9月18日\n    */\n    private void writeMapFile(String name, String value) {\n\n        // 加载数据\n        String path = RuleszkToxmlLoader.class.getClassLoader().getResource(ZookeeperPath.ZK_LOCAL_WRITE_PATH.getKey())\n                .getPath();\n\n        checkNotNull(path, \"write Map file curr Path :\" + path + \" is null! must is not null\");\n        path = new File(path).getPath() + File.separator;\n        path += name;\n\n        // 进行数据写入\n        try {\n            Files.write(value.getBytes(), new File(path));\n        } catch (IOException e1) {\n            e1.printStackTrace();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zktoxml/listen/SchemaszkToxmlLoader.java",
    "content": "package io.mycat.config.loader.zkprocess.zktoxml.listen;\n\nimport java.io.File;\nimport java.util.List;\n\nimport io.mycat.MycatServer;\nimport io.mycat.manager.response.ReloadConfig;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.NotiflyService;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.entity.Schemas;\nimport io.mycat.config.loader.zkprocess.entity.schema.datahost.DataHost;\nimport io.mycat.config.loader.zkprocess.entity.schema.datanode.DataNode;\nimport io.mycat.config.loader.zkprocess.entity.schema.schema.Schema;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.schema.json.DataHostJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.schema.json.DataNodeJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.schema.json.SchemaJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.schema.xml.SchemasParseXmlImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.DataInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.DiretoryInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkDirectoryImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkMultLoader;\n\n/**\n * 进行schema的文件从zk中加载\n* 源文件名：SchemasLoader.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class SchemaszkToxmlLoader extends ZkMultLoader implements NotiflyService {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(SchemaszkToxmlLoader.class);\n\n    /**\n     * 当前文件中的zkpath信息 \n    * @字段说明 currZkPath\n    */\n    private final String currZkPath;\n\n    /**\n     * 写入本地的文件路径\n    * @字段说明 WRITEPATH\n    */\n    private static final String WRITEPATH = \"schema.xml\";\n\n    /**\n     * schema类与xml转换服务 \n    * @字段说明 parseSchemaService\n    */\n    private ParseXmlServiceInf<Schemas> parseSchemaXmlService;\n\n    /**\n     * 进行将schema\n    * @字段说明 parseJsonSchema\n    */\n    private ParseJsonServiceInf<List<Schema>> parseJsonSchema = new SchemaJsonParse();\n\n    /**\n     * 进行将dataNode\n     * @字段说明 parseJsonSchema\n     */\n    private ParseJsonServiceInf<List<DataNode>> parseJsonDataNode = new DataNodeJsonParse();\n\n    /**\n     * 进行将dataNode\n     * @字段说明 parseJsonSchema\n     */\n    private ParseJsonServiceInf<List<DataHost>> parseJsonDataHost = new DataHostJsonParse();\n\n    /**\n     * zk的监控路径信息\n    * @字段说明 zookeeperListen\n    */\n    private ZookeeperProcessListen zookeeperListen;\n\n    public SchemaszkToxmlLoader(ZookeeperProcessListen zookeeperListen, CuratorFramework curator,\n            XmlProcessBase xmlParseBase) {\n\n        this.setCurator(curator);\n\n        this.zookeeperListen = zookeeperListen;\n\n        // 获得当前集群的名称\n        String schemaPath = zookeeperListen.getBasePath();\n        schemaPath = schemaPath + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FOW_ZK_PATH_SCHEMA.getKey();\n\n        currZkPath = schemaPath;\n        // 将当前自己注册为事件接收对象\n        this.zookeeperListen.addListen(schemaPath, this);\n\n        // 生成xml与类的转换信息\n        this.parseSchemaXmlService = new SchemasParseXmlImpl(xmlParseBase);\n    }\n\n    @Override\n    public boolean notiflyProcess() throws Exception {\n        // 1,将集群schema目录下的所有集群按层次结构加载出来\n        // 通过组合模式进行zk目录树的加载\n        DiretoryInf schemaDirectory = new ZkDirectoryImpl(currZkPath, null);\n        // 进行递归的数据获取\n        this.getTreeDirectory(currZkPath, ZookeeperPath.FLOW_ZK_PATH_SCHEMA_SCHEMA.getKey(), schemaDirectory);\n\n        // 从当前的下一级开始进行遍历,获得到\n        ZkDirectoryImpl zkDirectory = (ZkDirectoryImpl) schemaDirectory.getSubordinateInfo().get(0);\n\n        Schemas schema = this.zktoSchemasBean(zkDirectory);\n\n        LOGGER.info(\"SchemasLoader notiflyProcess zk to object  zk schema Object  :\" + schema);\n\n        String path = SchemaszkToxmlLoader.class.getClassLoader()\n                .getResource(ZookeeperPath.ZK_LOCAL_WRITE_PATH.getKey()).getPath();\n        path=new File(path).getPath()+File.separator;\n        path += WRITEPATH;\n\n        LOGGER.info(\"SchemasLoader notiflyProcess zk to object writePath :\" + path);\n\n        this.parseSchemaXmlService.parseToXmlWrite(schema, path, \"schema\");\n\n        LOGGER.info(\"SchemasLoader notiflyProcess zk to object zk schema      write :\" + path + \" is success\");\n\n        if(MycatServer.getInstance().getStartup().get()) {\n            ReloadConfig.reload_all();\n        }\n        return true;\n    }\n\n    /**\n     * 将zk上面的信息转换为javabean对象\n    * 方法描述\n    * @param zkDirectory\n    * @return\n    * @创建日期 2016年9月17日\n    */\n    private Schemas zktoSchemasBean(ZkDirectoryImpl zkDirectory) {\n        Schemas schema = new Schemas();\n\n        // 得到schema对象的目录信息\n        DataInf schemaZkDirectory = this.getZkData(zkDirectory, ZookeeperPath.FLOW_ZK_PATH_SCHEMA_SCHEMA.getKey());\n        List<Schema> schemaList = parseJsonSchema.parseJsonToBean(schemaZkDirectory.getDataValue());\n        schema.setSchema(schemaList);\n\n        this.zookeeperListen.watchPath(currZkPath, ZookeeperPath.FLOW_ZK_PATH_SCHEMA_SCHEMA.getKey());\n\n        // 得到dataNode的信息\n        DataInf dataNodeZkDirectory = this.getZkData(zkDirectory, ZookeeperPath.FLOW_ZK_PATH_SCHEMA_DATANODE.getKey());\n        List<DataNode> dataNodeList = parseJsonDataNode.parseJsonToBean(dataNodeZkDirectory.getDataValue());\n        schema.setDataNode(dataNodeList);\n\n        this.zookeeperListen.watchPath(currZkPath, ZookeeperPath.FLOW_ZK_PATH_SCHEMA_DATANODE.getKey());\n\n        // 得到dataNode的信息\n        DataInf dataHostZkDirectory = this.getZkData(zkDirectory, ZookeeperPath.FLOW_ZK_PATH_SCHEMA_DATAHOST.getKey());\n        List<DataHost> dataHostList = parseJsonDataHost.parseJsonToBean(dataHostZkDirectory.getDataValue());\n        schema.setDataHost(dataHostList);\n\n        this.zookeeperListen.watchPath(currZkPath, ZookeeperPath.FLOW_ZK_PATH_SCHEMA_DATAHOST.getKey());\n\n        return schema;\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zktoxml/listen/SequenceTopropertiesLoader.java",
    "content": "package io.mycat.config.loader.zkprocess.zktoxml.listen;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.io.Files;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.NotiflyService;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.zookeeper.DiretoryInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkDataImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkDirectoryImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkMultLoader;\nimport io.mycat.manager.response.ReloadConfig;\n\n/**\n * 进行从sequence加载到zk中加载\n* 源文件名：SchemasLoader.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class SequenceTopropertiesLoader extends ZkMultLoader implements NotiflyService {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(SequenceTopropertiesLoader.class);\n\n    /**\n     * 当前文件中的zkpath信息 \n    * @字段说明 currZkPath\n    */\n    private final String currZkPath;\n\n    /**\n     * 后缀名\n    * @字段说明 PROPERTIES_SUFFIX\n    */\n    private static final String PROPERTIES_SUFFIX = \".properties\";\n\n    /**\n     * 序列配制信息\n    * @字段说明 PROPERTIES_SEQUENCE_CONF\n    */\n    private static final String PROPERTIES_SEQUENCE_CONF = \"sequence_conf\";\n\n    /**\n     * db序列配制信息\n     * @字段说明 PROPERTIES_SEQUENCE_CONF\n     */\n    private static final String PROPERTIES_SEQUENCE_DB_CONF = \"sequence_db_conf\";\n\n    /**\n     * 分布式的序列配制\n     * @字段说明 PROPERTIES_SEQUENCE_CONF\n     */\n    private static final String PROPERTIES_SEQUENCE_DISTRIBUTED_CONF = \"sequence_distributed_conf\";\n\n    /**\n     * 时间的序列配制\n     * @字段说明 PROPERTIES_SEQUENCE_CONF\n     */\n    private static final String PROPERTIES_SEQUENCE_TIME_CONF = \"sequence_time_conf\";\n\n    /**\n     * 监控路径信息\n    * @字段说明 zookeeperListen\n    */\n    private ZookeeperProcessListen zookeeperListen;\n\n    public SequenceTopropertiesLoader(ZookeeperProcessListen zookeeperListen, CuratorFramework curator,\n            XmlProcessBase xmlParseBase) {\n\n        this.setCurator(curator);\n\n        this.zookeeperListen = zookeeperListen;\n\n        // 获得当前集群的名称\n        String schemaPath = zookeeperListen.getBasePath();\n        schemaPath = schemaPath + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE.getKey();\n\n        currZkPath = schemaPath;\n        // 将当前自己注册为事件接收对象\n        this.zookeeperListen.addListen(schemaPath, this);\n\n    }\n\n    @Override\n    public boolean notiflyProcess() throws Exception {\n\n        // 1,将集群server目录下的所有集群按层次结构加载出来\n        // 通过组合模式进行zk目录树的加载\n        DiretoryInf sequenceDirectory = new ZkDirectoryImpl(currZkPath, null);\n        // 进行递归的数据获取\n        this.getTreeDirectory(currZkPath, ZookeeperPath.FLOW_ZK_PATH_SEQUENCE.getKey(), sequenceDirectory);\n\n        // 取到当前根目录 信息\n        sequenceDirectory = (DiretoryInf) sequenceDirectory.getSubordinateInfo().get(0);\n\n        // 将zk序列配配制信息入本地文件\n        this.sequenceZkToProperties(currZkPath, PROPERTIES_SEQUENCE_CONF, sequenceDirectory);\n\n        LOGGER.info(\"SequenceTozkLoader notiflyProcess sequence_conf to local properties success\");\n\n        // 将zk的db方式信息入本地文件\n        this.sequenceZkToProperties(currZkPath, PROPERTIES_SEQUENCE_DB_CONF, sequenceDirectory);\n\n        LOGGER.info(\"SequenceTozkLoader notiflyProcess sequence_db_conf to local properties success\");\n\n        // 将zk的分布式信息入本地文件\n        this.seqWriteOneZkToProperties(currZkPath, PROPERTIES_SEQUENCE_DISTRIBUTED_CONF, sequenceDirectory);\n\n        LOGGER.info(\"SequenceTozkLoader notiflyProcess sequence_distributed_conf to local properties success\");\n\n        // 将zk时间序列入本地文件\n        this.seqWriteOneZkToProperties(currZkPath, PROPERTIES_SEQUENCE_TIME_CONF, sequenceDirectory);\n\n        LOGGER.info(\"SequenceTozkLoader notiflyProcess sequence_time_conf to local properties success\");\n\n        LOGGER.info(\"SequenceTozkLoader notiflyProcess xml to local properties is success\");\n\n        if (MycatServer.getInstance().getProcessors() != null)\n            ReloadConfig.reload();\n        return true;\n    }\n\n    /**\n     * 将xml文件的信息写入到zk中\n    * 方法描述\n    * @param basePath 基本路径\n    * @param schema schema文件的信息\n    * @throws Exception 异常信息\n    * @创建日期 2016年9月17日\n    */\n    private void sequenceZkToProperties(String basePath, String name, DiretoryInf seqDirectory) throws Exception {\n        // 读取当前节的信息\n        ZkDirectoryImpl zkDirectory = (ZkDirectoryImpl) this.getZkDirectory(seqDirectory,\n                ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_COMMON.getKey());\n\n        if (null != zkDirectory) {\n            String writeFile = name + PROPERTIES_SUFFIX;\n\n            // 读取common目录下的数据\n            ZkDataImpl commData = (ZkDataImpl) this.getZkData(zkDirectory, writeFile);\n\n            // 读取公共节点的信息\n            this.writeMapFile(commData.getName(), commData.getValue());\n\n            String seqComm = ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_COMMON.getKey();\n            seqComm = seqComm + ZookeeperPath.ZK_SEPARATOR.getKey() + commData.getName();\n\n            this.zookeeperListen.watchPath(currZkPath, seqComm);\n\n        }\n\n        // 集群中特有的节点的配制信息\n        ZkDirectoryImpl zkClusterDir = (ZkDirectoryImpl) this.getZkDirectory(seqDirectory,\n                ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_CLUSTER.getKey());\n\n        if (null != zkClusterDir) {\n\n            String clusterName = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID);\n\n            String nodeName = name + \"-\" + clusterName + PROPERTIES_SUFFIX;\n\n            // 读取cluster目录下的数据\n            ZkDataImpl clusterData = (ZkDataImpl) this.getZkData(zkClusterDir, nodeName);\n\n            if (null != clusterData) {\n                // 读取当前集群中特有的节点的信息\n                this.writeMapFile(clusterData.getName(), clusterData.getValue());\n\n                String seqCluster = ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_COMMON.getKey();\n                seqCluster = seqCluster + ZookeeperPath.ZK_SEPARATOR.getKey() + clusterData.getName();\n\n                this.zookeeperListen.watchPath(currZkPath, seqCluster);\n            }\n        }\n    }\n\n    /**\n     * 将xml文件的信息写入到zk中\n     * 方法描述\n     * @param basePath 基本路径\n     * @param schema schema文件的信息\n     * @throws Exception 异常信息\n     * @创建日期 2016年9月17日\n     */\n    private void seqWriteOneZkToProperties(String basePath, String name, DiretoryInf seqDirectory) throws Exception {\n        // 读取当前节的信息\n        ZkDirectoryImpl zkDirectory = (ZkDirectoryImpl) this.getZkDirectory(seqDirectory,\n                ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_COMMON.getKey());\n\n        ZkDataImpl commData = null;\n\n        if (null != zkDirectory) {\n            String writeFile = name + PROPERTIES_SUFFIX;\n\n            // 读取common目录下的数据\n            commData = (ZkDataImpl) this.getZkData(zkDirectory, writeFile);\n\n            // comm路径的监控路径\n            String seqComm = ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_COMMON.getKey();\n            seqComm = seqComm + ZookeeperPath.ZK_SEPARATOR.getKey() + commData.getName();\n\n            this.zookeeperListen.watchPath(currZkPath, seqComm);\n        }\n\n        // 集群中特有的节点的配制信息\n        ZkDirectoryImpl zkClusterDir = (ZkDirectoryImpl) this.getZkDirectory(seqDirectory,\n                ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_CLUSTER.getKey());\n\n        ZkDataImpl clusterData = null;\n\n        if (null != zkClusterDir) {\n\n            String clusterName = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID);\n\n            String nodeName = name + \"-\" + clusterName + PROPERTIES_SUFFIX;\n\n            // 读取cluster目录下的数据\n            clusterData = (ZkDataImpl) this.getZkData(zkClusterDir, nodeName);\n\n            if (null != clusterData) {\n                // comm路径的监控路径\n                String seqCluster = ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_CLUSTER.getKey();\n                seqCluster = seqCluster + ZookeeperPath.ZK_SEPARATOR.getKey() + clusterData.getName();\n\n                this.zookeeperListen.watchPath(currZkPath, seqCluster);\n            }\n        }\n\n        // 如果配制了单独节点的信息,以公共的名称，写入当前的值\n        if (clusterData != null && commData != null) {\n            // 读取公共节点的信息\n            this.writeMapFile(commData.getName(), clusterData.getValue());\n        } else if (commData != null) {\n            // 读取当前集群中特有的节点的信息\n            this.writeMapFile(commData.getName(), commData.getValue());\n        }\n    }\n\n    /**\n     * 读取 mapFile文件的信息\n    * 方法描述\n    * @param name 名称信息\n    * @return\n    * @创建日期 2016年9月18日\n    */\n    private void writeMapFile(String name, String value) {\n\n        // 加载数据\n        String path = RuleszkToxmlLoader.class.getClassLoader().getResource(ZookeeperPath.ZK_LOCAL_WRITE_PATH.getKey())\n                .getPath();\n\n        checkNotNull(path, \"write Map file curr Path :\" + path + \" is null! must is not null\");\n\n        path = new File(path).getPath() + File.separator;\n        path += name;\n\n        // 进行数据写入\n        try {\n            Files.write(value.getBytes(), new File(path));\n        } catch (IOException e1) {\n            e1.printStackTrace();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zktoxml/listen/ServerzkToxmlLoader.java",
    "content": "package io.mycat.config.loader.zkprocess.zktoxml.listen;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.List;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.io.Files;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.NotiflyService;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.loader.zkprocess.comm.ZookeeperProcessListen;\nimport io.mycat.config.loader.zkprocess.entity.Server;\nimport io.mycat.config.loader.zkprocess.entity.server.System;\nimport io.mycat.config.loader.zkprocess.entity.server.user.User;\nimport io.mycat.config.loader.zkprocess.parse.ParseJsonServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.ParseXmlServiceInf;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.server.json.SystemJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.server.json.UserJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.server.xml.ServerParseXmlImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.DataInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.DiretoryInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkDataImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkDirectoryImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkMultLoader;\nimport io.mycat.manager.response.ReloadConfig;\n\n/**\n * 进行server的文件从zk中加载\n* 源文件名：ServerzkToxmlLoader.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class ServerzkToxmlLoader extends ZkMultLoader implements NotiflyService {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(ServerzkToxmlLoader.class);\n\n    /**\n     * 当前文件中的zkpath信息 \n    * @字段说明 currZkPath\n    */\n    private final String currZkPath;\n\n    /**\n     * 写入本地的文件路径\n    * @字段说明 WRITEPATH\n    */\n    private static final String WRITEPATH = \"server.xml\";\n\n    /**\n     * index_to_charset文件的路径信息\n     * @字段说明 SCHEMA_PATH\n     */\n    private static final String INDEX_TOCHARSET_PATH = \"index_to_charset.properties\";\n\n    /**\n     * server的xml的转换信息\n    * @字段说明 parseServerXMl\n    */\n    private ParseXmlServiceInf<Server> parseServerXMl;\n\n    /**\n     * system信息\n    * @字段说明 parseJsonserver\n    */\n    private ParseJsonServiceInf<System> parseJsonSystem = new SystemJsonParse();\n\n    /**\n     * system信息\n     * @字段说明 parseJsonserver\n     */\n    private ParseJsonServiceInf<List<User>> parseJsonUser = new UserJsonParse();\n\n    /**\n     * zk监控路径\n    * @字段说明 zookeeperListen\n    */\n    private ZookeeperProcessListen zookeeperListen;\n\n    public ServerzkToxmlLoader(ZookeeperProcessListen zookeeperListen, CuratorFramework curator,\n            XmlProcessBase xmlParseBase) {\n\n        this.setCurator(curator);\n\n        this.zookeeperListen = zookeeperListen;\n\n        // 获得当前集群的名称\n        String serverPath = zookeeperListen.getBasePath();\n        serverPath = serverPath + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SERVER.getKey();\n\n        currZkPath = serverPath;\n        // 将当前自己注册为事件接收对象\n        this.zookeeperListen.addListen(serverPath, this);\n\n        // 生成xml与类的转换信息\n        parseServerXMl = new ServerParseXmlImpl(xmlParseBase);\n    }\n\n    @Override\n    public boolean notiflyProcess() throws Exception {\n        // 1,将集群server目录下的所有集群按层次结构加载出来\n        // 通过组合模式进行zk目录树的加载\n        DiretoryInf serverDirectory = new ZkDirectoryImpl(currZkPath, null);\n        // 进行递归的数据获取\n        this.getTreeDirectory(currZkPath, ZookeeperPath.FLOW_ZK_PATH_SERVER.getKey(), serverDirectory);\n\n        // 从当前的下一级开始进行遍历,获得到\n        ZkDirectoryImpl zkDirectory = (ZkDirectoryImpl) serverDirectory.getSubordinateInfo().get(0);\n        Server server = this.zktoServerBean(zkDirectory);\n\n        // 读取当前集群中当前节点的特殊的配制信息\n        Server currSer = this.zktoServerBeanByCurrNode(zkDirectory);\n\n        // 为当前的参数赋新值\n        if (null != currSer) {\n            server.getSystem().setNewValue(currSer.getSystem());\n        }\n\n        LOGGER.info(\"ServerzkToxmlLoader notiflyProcess zk to object  zk server Object  :\" + server);\n\n        // 数配制信息写入文件\n        String path = ServerzkToxmlLoader.class.getClassLoader().getResource(ZookeeperPath.ZK_LOCAL_WRITE_PATH.getKey())\n                .getPath();\n        path = new File(path).getPath() + File.separator;\n        path += WRITEPATH;\n\n        LOGGER.info(\"ServerzkToxmlLoader notiflyProcess zk to object writePath :\" + path);\n\n        this.parseServerXMl.parseToXmlWrite(server, path, \"server\");\n\n        LOGGER.info(\"ServerzkToxmlLoader notiflyProcess zk to object zk server      write :\" + path + \" is success\");\n\n        // 得到server对象的目录信息\n        DataInf indexToCharSet = this.getZkData(zkDirectory, INDEX_TOCHARSET_PATH);\n\n        if (null != indexToCharSet) {\n\n            if (indexToCharSet instanceof ZkDataImpl) {\n                ZkDataImpl dataImpl = (ZkDataImpl) indexToCharSet;\n                this.writeProperties(dataImpl.getName(), dataImpl.getValue());\n            }\n\n            LOGGER.info(\"ServerzkToxmlLoader notiflyProcess zk to write index_to_charset.properties is success\");\n        }\n        if (MycatServer.getInstance().getProcessors() != null)\n            ReloadConfig.reload();\n        return true;\n    }\n\n    /**\n     * 将zk上面的信息转换为javabean对象\n    * 方法描述\n    * @param zkDirectory\n    * @return\n    * @创建日期 2016年9月17日\n    */\n    private Server zktoServerBean(DiretoryInf zkDirectory) {\n        Server server = new Server();\n\n        // 得到server对象的目录信息\n        DataInf serverZkDirectory = this.getZkData(zkDirectory, ZookeeperPath.FLOW_ZK_PATH_SERVER_DEFAULT.getKey());\n        System systemValue = parseJsonSystem.parseJsonToBean(serverZkDirectory.getDataValue());\n        server.setSystem(systemValue);\n\n        this.zookeeperListen.watchPath(currZkPath, ZookeeperPath.FLOW_ZK_PATH_SERVER_DEFAULT.getKey());\n\n        // 得到user的信息\n        DataInf userZkDirectory = this.getZkData(zkDirectory, ZookeeperPath.FLOW_ZK_PATH_SERVER_USER.getKey());\n        List<User> userList = parseJsonUser.parseJsonToBean(userZkDirectory.getDataValue());\n        server.setUser(userList);\n\n        // 用户路径的监控\n        this.zookeeperListen.watchPath(currZkPath, ZookeeperPath.FLOW_ZK_PATH_SERVER_USER.getKey());\n\n        return server;\n    }\n\n    /**\n     * 加载当前节点的特殊配制信息\n    * 方法描述\n    * @param zkDirectory\n    * @return\n    * @创建日期 2016年9月17日\n    */\n    private Server zktoServerBeanByCurrNode(DiretoryInf zkDirectory) {\n\n        Server server = null;\n\n        // 得到集群节点的配制信息\n        DiretoryInf directory = this.getZkDirectory(zkDirectory, ZookeeperPath.FLOW_ZK_PATH_SERVER_CLUSTER.getKey());\n\n        if (null != directory) {\n\n            // 获得当前myid的名称\n            String myid = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID);\n\n            // 获邓当前节点的信息\n            DataInf currDataCfg = this.getZkData(directory, myid);\n\n            // 如果当前节点存在配制信息，则加载\n            if (null != currDataCfg) {\n                server = new Server();\n\n                System systemValue = parseJsonSystem.parseJsonToBean(currDataCfg.getDataValue());\n                server.setSystem(systemValue);\n\n                if (currDataCfg instanceof ZkDataImpl) {\n                    ZkDataImpl zkData = (ZkDataImpl) currDataCfg;\n\n                    // 监控的路径信息\n                    String defaultWatchPath = ZookeeperPath.FLOW_ZK_PATH_SERVER_CLUSTER.getKey();\n                    defaultWatchPath = defaultWatchPath + ZookeeperPath.ZK_SEPARATOR.getKey() + zkData.getName();\n\n                    this.zookeeperListen.watchPath(currZkPath, defaultWatchPath);\n                }\n            }\n        }\n\n        return server;\n    }\n\n    /**\n     * 写入本地文件配制信息\n    * 方法描述\n    * @param name 名称信息\n    * @return\n    * @创建日期 2016年9月18日\n    */\n    private void writeProperties(String name, String value) {\n\n        // 加载数据\n        String path = RuleszkToxmlLoader.class.getClassLoader().getResource(ZookeeperPath.ZK_LOCAL_WRITE_PATH.getKey())\n                .getPath();\n\n        checkNotNull(path, \"write properties curr Path :\" + path + \" is null! must is not null\");\n\n        path = new File(path).getPath() + File.separator;\n        path += name;\n\n        // 进行数据写入\n        try {\n            Files.write(value.getBytes(), new File(path));\n        } catch (IOException e1) {\n            e1.printStackTrace();\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zookeeper/ClusterInfo.java",
    "content": "package io.mycat.config.loader.zkprocess.zookeeper;\n\n/**\n * Created by magicdoom on 2016/12/21.\n */\npublic class ClusterInfo {\n    private int clusterSize;\n    private String clusterNodes;\n\n\n    public int getClusterSize() {\n        return clusterSize;\n    }\n\n    public void setClusterSize(int clusterSize) {\n        this.clusterSize = clusterSize;\n    }\n\n    public String getClusterNodes() {\n        return clusterNodes;\n    }\n\n    public void setClusterNodes(String clusterNodes) {\n        this.clusterNodes = clusterNodes;\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zookeeper/DataInf.java",
    "content": "package io.mycat.config.loader.zkprocess.zookeeper;\n\n/**\n * 数据节点信息\n* 源文件名：DataInf.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic interface DataInf {\n\n    /**\n     * 获取信息,以:分隔两个值 \n     * @return\n     */\n    String getDataInfo();\n\n    /**\n     * 返回数据节点值信息\n    * 方法描述\n    * @return\n    * @创建日期 2016年9月17日\n    */\n    String getDataValue();\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zookeeper/DiretoryInf.java",
    "content": "package io.mycat.config.loader.zkprocess.zookeeper;\n\nimport java.util.List;\n\n/**\n * 目录接口信息\n* 源文件名：DiretoryInf.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic interface DiretoryInf {\n\n    /**\n     * 获取当前的目录信息\n     * @return\n     */\n    String getDiretoryInfo();\n\n    /**\n     * 添加目录或者数据节点\n     * @param branch\n     */\n    void add(DiretoryInf directory);\n\n    /**\n     * 添加数据节点信息\n    * 方法描述\n    * @param data\n    * @创建日期 2016年9月15日\n    */\n    void add(DataInf data);\n\n    /**\n     * 获取子节点信息\n     * @return\n     */\n    List<Object> getSubordinateInfo();\n\n    /**\n     * 获取节点的名称\n    * @字段说明 getDataName\n    */\n    String getDataName();\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zookeeper/process/ZkDataImpl.java",
    "content": "package io.mycat.config.loader.zkprocess.zookeeper.process;\n\nimport io.mycat.config.loader.zkprocess.zookeeper.DataInf;\n\n/**\n * 数据节点信息\n* 源文件名：DataImpl.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class ZkDataImpl implements DataInf {\n\n    /**\n     * 名称信息\n    * @字段说明 name\n    */\n    private String name;\n\n    /**\n     * 当前值信息\n    * @字段说明 value\n    */\n    private String value;\n\n    public ZkDataImpl(String name, String value) {\n        super();\n        this.name = name;\n        this.value = value;\n    }\n\n    @Override\n    public String getDataInfo() {\n        return this.name + \":\" + this.value;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public void setValue(String value) {\n        this.value = value;\n    }\n\n    @Override\n    public String getDataValue() {\n        return this.value;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zookeeper/process/ZkDirectoryImpl.java",
    "content": "package io.mycat.config.loader.zkprocess.zookeeper.process;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.mycat.config.loader.zkprocess.zookeeper.DataInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.DiretoryInf;\n\n/**\n * zk的目录节点信息\n* 源文件名：ZkDirectoryMsg.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class ZkDirectoryImpl implements DiretoryInf {\n\n    /**\n     * 整个节点信息\n    * @字段说明 subordinateInfo\n    */\n    private List<Object> subordinateInfoList = new ArrayList<Object>();\n\n    /**\n     * 节点的名称信息\n    * @字段说明 name\n    */\n    private String name;\n\n    /**\n     * 当前节点的数据信息\n    * @字段说明 value\n    */\n    private String value;\n\n    public ZkDirectoryImpl(String name, String value) {\n        this.name = name;\n        this.value = value;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public void setValue(String value) {\n        this.value = value;\n    }\n\n    @Override\n    public String getDiretoryInfo() {\n        return name + \":\" + value;\n    }\n\n    @Override\n    public void add(DiretoryInf branch) {\n        this.subordinateInfoList.add(branch);\n    }\n\n    @Override\n    public List<Object> getSubordinateInfo() {\n        return this.subordinateInfoList;\n    }\n\n    @Override\n    public void add(DataInf data) {\n        this.subordinateInfoList.add(data);\n    }\n\n    @Override\n    public String getDataName() {\n        return this.name;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/loader/zkprocess/zookeeper/process/ZkMultLoader.java",
    "content": "package io.mycat.config.loader.zkprocess.zookeeper.process;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.utils.ZKPaths;\nimport org.apache.zookeeper.data.Stat;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.gson.Gson;\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.zookeeper.DataInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.DiretoryInf;\n\n/**\n * 进行zk获取数据类信息\n* 源文件名：AbstractLoader.java\n* 文件版本：1.0.0\n* 创建作者：liujun\n* 创建日期：2016年9月15日\n* 修改作者：liujun\n* 修改日期：2016年9月15日\n* 文件描述：TODO\n* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.\n*/\npublic class ZkMultLoader {\n\n    /**\n     * 日志\n    * @字段说明 LOGGER\n    */\n    private static final Logger LOGGER = LoggerFactory.getLogger(ZkMultLoader.class);\n\n    /**\n     * zk连接信息\n    * @字段说明 curator\n    */\n    private CuratorFramework curator;\n\n    /**\n     * 进行数据转换操作\n    * @字段说明 gson\n    */\n    private Gson gson = new Gson();\n\n    /**\n     * 得到树形节点信息\n    * 方法描述\n    * @param path\n    * @param zkDirectory\n    * @throws Exception\n    * @创建日期 2016年9月15日\n    */\n    public void getTreeDirectory(String path, String name, DiretoryInf zkDirectory) throws Exception {\n\n        boolean check = this.checkPathExists(path);\n\n        // 如果文件存在，则继续遍历\n        if (check) {\n            // 首先获取当前节点的数据，然后再递归\n            String currDate = this.getDataToString(path);\n\n            List<String> childPathList = this.getChildNames(path);\n\n            // 如果存在子目录信息，则进行\n            if (null != childPathList && !childPathList.isEmpty()) {\n                DiretoryInf directory = new ZkDirectoryImpl(name, currDate);\n\n                // 添加目录节点信息\n                zkDirectory.add(directory);\n\n                for (String childPath : childPathList) {\n                    this.getTreeDirectory(path + ZookeeperPath.ZK_SEPARATOR.getKey() + childPath, childPath, directory);\n                }\n            }\n            // 添加当前的数据节点信息\n            else {\n                zkDirectory.add(new ZkDataImpl(name, currDate));\n            }\n        }\n    }\n\n    /**\n     * 检查文件是否存在\n    * 方法描述\n    * @param path\n    * @return\n    * @创建日期 2016年9月21日\n    */\n    protected boolean checkPathExists(String path) {\n        try {\n            Stat state = this.curator.checkExists().forPath(path);\n\n            if (null != state) {\n                return true;\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return false;\n    }\n\n    /**\n     * get data from zookeeper and convert to string with check not null.\n     */\n    protected String getDataToString(String path) throws Exception {\n        byte[] raw = curator.getData().forPath(path);\n\n        checkNotNull(raw, \"data of \" + path + \" must be not null!\");\n        return byteToString(raw);\n    }\n\n    /**\n     * get child node name list based on path from zookeeper.\n     * @throws Exception \n     */\n    protected List<String> getChildNames(String path) throws Exception {\n        return curator.getChildren().forPath(path);\n    }\n\n    protected void checkAndwriteString(String parentPath, String currpath, String value) throws Exception {\n        checkNotNull(parentPath, \"data of path\" + parentPath + \" must be not null!\");\n        checkNotNull(currpath, \"data of path\" + currpath + \" must be not null!\");\n        checkNotNull(value, \"data of value:\" + value + \" must be not null!\");\n\n        String nodePath = ZKPaths.makePath(parentPath, currpath);\n\n        Stat stat = curator.checkExists().forPath(nodePath);\n\n        if (null == stat) {\n            this.createPath(nodePath);\n        }\n\n        LOGGER.debug(\"ZkMultLoader write file :\" + nodePath + \", value :\" + value);\n\n        curator.setData().inBackground().forPath(nodePath, value.getBytes());\n\n    }\n\n    /**\n     * 创建配制信息\n     * 方法描述\n     * @param configKey 配制的当前路径名称信息\n     * @param filterInnerMap  最终的信息是否为map\n     * @param configDirectory 配制的目录\n     * @param restDirectory 子目录信息\n     * @创建日期 2016年9月11日\n     */\n    public boolean createPath(String path) {\n\n        // 得到当前的目录信息\n        LOGGER.trace(\"createPath child path is {}\", path);\n\n        boolean result = true;\n        try {\n            // 进行目录的创建操作\n            ZKPaths.mkdirs(curator.getZookeeperClient().getZooKeeper(), path);\n        } catch (Exception e) {\n            LOGGER.error(\" createPath error\", e);\n            result = false;\n        }\n\n        return result;\n    }\n\n    protected void writeZkString(String path, String value) throws Exception {\n        checkNotNull(path, \"data of path\" + path + \" must be not null!\");\n        checkNotNull(value, \"data of value:\" + value + \" must be not null!\");\n\n        curator.setData().forPath(path, value.getBytes());\n    }\n\n    /**\n     * raw byte data to string\n     */\n    protected String byteToString(byte[] raw) {\n        // return empty json {}.\n        if (raw.length == 0) {\n            return \"{}\";\n        }\n        return new String(raw, StandardCharsets.UTF_8);\n    }\n\n    /**\n     * 通过名称数据节点信息\n    * 方法描述\n    * @param zkDirectory\n    * @param name\n    * @return\n    * @创建日期 2016年9月16日\n    */\n    protected DataInf getZkData(DiretoryInf zkDirectory, String name) {\n        List<Object> list = zkDirectory.getSubordinateInfo();\n\n        if (null != list && !list.isEmpty()) {\n            for (Object directObj : list) {\n\n                if (directObj instanceof ZkDataImpl) {\n                    ZkDataImpl zkDirectoryValue = (ZkDataImpl) directObj;\n\n                    if (name.equals(zkDirectoryValue.getName())) {\n\n                        return zkDirectoryValue;\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 通过名称获得目录节点信息\n     * 方法描述\n     * @param zkDirectory\n     * @param name\n     * @return\n     * @创建日期 2016年9月16日\n     */\n    protected DiretoryInf getZkDirectory(DiretoryInf zkDirectory, String name) {\n        List<Object> list = zkDirectory.getSubordinateInfo();\n\n        if (null != list && !list.isEmpty()) {\n            for (Object directObj : list) {\n\n                if (directObj instanceof DiretoryInf) {\n                    DiretoryInf zkDirectoryValue = (DiretoryInf) directObj;\n\n                    if (name.equals(zkDirectoryValue.getDataName())) {\n\n                        return zkDirectoryValue;\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n    public CuratorFramework getCurator() {\n        return curator;\n    }\n\n    public void setCurator(CuratorFramework curator) {\n        this.curator = curator;\n    }\n\n    public Gson getGson() {\n        return gson;\n    }\n\n    public void setGson(Gson gson) {\n        this.gson = gson;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/model/ClusterConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\nimport io.mycat.config.util.ConfigException;\nimport io.mycat.config.util.ConfigUtil;\nimport io.mycat.util.SplitUtil;\n\n/**\n * @author mycat\n */\npublic class ClusterConfig {\n    private final Map<String, MycatNodeConfig> nodes;\n    private final Map<String, List<String>> groups;\n\n    public ClusterConfig(Element root, int port) {\n        nodes = Collections.unmodifiableMap(loadNode(root, port));\n        groups = Collections.unmodifiableMap(loadGroup(root, nodes));\n    }\n\n    public Map<String, MycatNodeConfig> getNodes() {\n        return nodes;\n    }\n\n    public Map<String, List<String>> getGroups() {\n        return groups;\n    }\n\n    private static Map<String, MycatNodeConfig> loadNode(Element root, int port) {\n        Map<String, MycatNodeConfig> nodes = new HashMap<String, MycatNodeConfig>();\n        NodeList list = root.getElementsByTagName(\"node\");\n        Set<String> hostSet = new HashSet<String>();\n        for (int i = 0, n = list.getLength(); i < n; i++) {\n            Node node = list.item(i);\n            if (node instanceof Element) {\n                Element element = (Element) node;\n                String name = element.getAttribute(\"name\").trim();\n                if (nodes.containsKey(name)) {\n                    throw new ConfigException(\"node name duplicated :\" + name);\n                }\n\n                Map<String, Object> props = ConfigUtil.loadElements(element);\n                String host = (String) props.get(\"host\");\n                if (null == host || \"\".equals(host)) {\n                    throw new ConfigException(\"host empty in node: \" + name);\n                }\n                if (hostSet.contains(host)) {\n                    throw new ConfigException(\"node host duplicated :\" + host);\n                }\n\n                String wei = (String) props.get(\"weight\");\n                if (null == wei || \"\".equals(wei)) {\n                    throw new ConfigException(\"weight should not be null in host:\" + host);\n                }\n                int weight = Integer.parseInt(wei);\n                if (weight <= 0) {\n                    throw new ConfigException(\"weight should be > 0 in host:\" + host + \" weight:\" + weight);\n                }\n\n                MycatNodeConfig conf = new MycatNodeConfig(name, host, port, weight);\n                nodes.put(name, conf);\n                hostSet.add(host);\n            }\n        }\n        return nodes;\n    }\n\n    private static Map<String, List<String>> loadGroup(Element root, Map<String, MycatNodeConfig> nodes) {\n        Map<String, List<String>> groups = new HashMap<String, List<String>>();\n        NodeList list = root.getElementsByTagName(\"group\");\n        for (int i = 0, n = list.getLength(); i < n; i++) {\n            Node node = list.item(i);\n            if (node instanceof Element) {\n                Element e = (Element) node;\n                String groupName = e.getAttribute(\"name\").trim();\n                if (groups.containsKey(groupName)) {\n                    throw new ConfigException(\"group duplicated : \" + groupName);\n                }\n\n                Map<String, Object> props = ConfigUtil.loadElements(e);\n                String value = (String) props.get(\"nodeList\");\n                if (null == value || \"\".equals(value)) {\n                    throw new ConfigException(\"group should contain 'nodeList'\");\n                }\n\n                String[] sList = SplitUtil.split(value, ',', true);\n\n                if (null == sList || sList.length == 0) {\n                    throw new ConfigException(\"group should contain 'nodeList'\");\n                }\n\n                for (String s : sList) {\n                    if (!nodes.containsKey(s)) {\n                        throw new ConfigException(\"[ node :\" + s + \"] in [ group:\" + groupName + \"] doesn't exist!\");\n                    }\n                }\n                List<String> nodeList = Arrays.asList(sList);\n                groups.put(groupName, nodeList);\n            }\n        }\n        if (!groups.containsKey(\"default\")) {\n            List<String> nodeList = new ArrayList<String>(nodes.keySet());\n            groups.put(\"default\", nodeList);\n        }\n        return groups;\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/model/DBHostConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model;\n\npublic class DBHostConfig {\n\t\n\tprivate long idleTimeout = SystemConfig.DEFAULT_IDLE_TIMEOUT; // 连接池中连接空闲超时时间\n\tprivate final String hostName;\n\tprivate final String ip;\n\tprivate final int port;\n\tprivate final String url;\n\tprivate final String user;\n\tprivate final String password;\n    private final String encryptPassword; //密文\n\tprivate final boolean checkAlive;\n\tprivate int maxCon ;\n\tprivate int minCon ;\n\tprivate String dbType;\n\tprivate String filters=\"mergeStat\";\n\tprivate long logTime = 300000;\n\tprivate int weight;\t\t\t\t\n\n\tpublic String getDbType() {\n\t\treturn dbType;\n\t}\n\n\tpublic void setDbType(String dbType) {\n\t\tthis.dbType = dbType;\n\t}\n\n\tpublic DBHostConfig(String hostName, String ip, int port, String url,\n\t\t\t\t\t\tString user, String password, String encryptPassword, boolean checkAlive) {\n\t\tsuper();\n\t\tthis.hostName = hostName;\n\t\tthis.ip = ip;\n\t\tthis.port = port;\n\t\tthis.url = url;\n\t\tthis.user = user;\n\t\tthis.password = password;\n\t\tthis.encryptPassword = encryptPassword;\n\t\tthis.checkAlive = checkAlive;\n\t}\n\n\tpublic long getIdleTimeout() {\n\t\treturn idleTimeout;\n\t}\n\n\tpublic void setIdleTimeout(long idleTimeout) {\n\t\tthis.idleTimeout = idleTimeout;\n\t}\n\n\tpublic int getMaxCon() {\n\t\treturn maxCon;\n\t}\n\n\tpublic void setMaxCon(int maxCon) {\n\t\tthis.maxCon = maxCon;\n\t}\n\n\tpublic int getMinCon() {\n\t\treturn minCon;\n\t}\n\n\tpublic void setMinCon(int minCon) {\n\t\tthis.minCon = minCon;\n\t}\n\n\tpublic String getHostName() {\n\t\treturn hostName;\n\t}\n\n\tpublic String getIp() {\n\t\treturn ip;\n\t}\n\n\tpublic int getPort() {\n\t\treturn port;\n\t}\n\n\tpublic String getUrl() {\n\t\treturn url;\n\t}\n\n\tpublic String getUser() {\n\t\treturn user;\n\t}\n\tpublic String getFilters() {\n\t\treturn filters;\n\t}\n\n\tpublic void setFilters(String filters) {\n\t\tthis.filters = filters;\n\t}\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\n\tpublic long getLogTime() {\n\t\treturn logTime;\n\t}\n\n\tpublic void setLogTime(long logTime) {\n\t\tthis.logTime = logTime;\n\t}\n\n\tpublic int getWeight() {\n\t\treturn weight;\n\t}\n\n\tpublic void setWeight(int weight) {\n\t\tthis.weight = weight;\n\t}\n\n\tpublic String getEncryptPassword() {\n\t\treturn this.encryptPassword;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DBHostConfig [hostName=\" + hostName + \", url=\" + url + \"]\";\n\t}\n\n\tpublic boolean isCheckAlive() {\n\t\treturn checkAlive;\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/model/DataHostConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport com.google.common.collect.Iterables;\nimport io.mycat.backend.datasource.PhysicalDBPool;\n\n/**\n * Datahost is a group of DB servers which is synchronized with each other\n *\n * @author wuzhih\n *\n */\npublic class DataHostConfig {\n\tpublic static final int NOT_SWITCH_DS = -1;\n\tpublic static final int DEFAULT_SWITCH_DS = 1;\n\tpublic static final int SYN_STATUS_SWITCH_DS = 2;\n\tpublic static final int CLUSTER_STATUS_SWITCH_DS = 3;\n    private static final Pattern pattern = Pattern.compile(\"\\\\s*show\\\\s+slave\\\\s+status\\\\s*\",Pattern.CASE_INSENSITIVE);\n    private static final Pattern patternCluster = Pattern.compile(\"\\\\s*show\\\\s+status\\\\s+like\\\\s+'wsrep%'\",Pattern.CASE_INSENSITIVE);\n\tprivate String name;\n\tprivate int maxCon = SystemConfig.DEFAULT_POOL_SIZE;\n\tprivate int minCon = 10;\n\tprivate int balance = PhysicalDBPool.BALANCE_NONE;\n\tprivate int balanceType = PhysicalDBPool.RANDOM;\n\tprivate int writeType = PhysicalDBPool.WRITE_ONLYONE_NODE;\n\tprivate final String dbType;\n\tprivate final String dbDriver;\n\tprivate final DBHostConfig[] writeHosts;\n\tprivate final Map<Integer, DBHostConfig[]> readHosts;\n\tprivate String hearbeatSQL;\n    private boolean isShowSlaveSql=false;\n    private boolean isShowClusterSql=false;\n\tprivate String connectionInitSql;\n    private int slaveThreshold = -1;\n\tprivate final int switchType;\n\tprivate String filters=\"mergeStat\";\n\tprivate long logTime=300000;\n\tprivate boolean tempReadHostAvailable = false;  //如果写服务挂掉, 临时读服务是否继续可用\n\tprivate final Set<String> dataNodes; //包含的所有dataNode名字\n\tprivate String slaveIDs;\n\tprivate int maxRetryCount = 3; // 心跳失败时候重试的次数. @auth zwy\n\tpublic static final String FOVER_NOT_SWITCH_DS = \"1\";\n\tpublic static final String CAN_SWITCH_DS = \"0\";\n\n\tprivate String notSwitch = CAN_SWITCH_DS;\n\t\n\tpublic DataHostConfig(String name, String dbType, String dbDriver,\n\t\t\tDBHostConfig[] writeHosts, Map<Integer, DBHostConfig[]> readHosts,int switchType,int slaveThreshold, boolean tempReadHostAvailable) {\n\t\tsuper();\n\t\tthis.name = name;\n\t\tthis.dbType = dbType;\n\t\tthis.dbDriver = dbDriver;\n\t\tthis.writeHosts = writeHosts;\n\t\tthis.readHosts = readHosts;\n\t\tthis.switchType=switchType;\n\t\tthis.slaveThreshold=slaveThreshold;\n\t\tthis.tempReadHostAvailable = tempReadHostAvailable;\n\t\tthis.dataNodes = new HashSet<>();\n\t}\n\n\tpublic boolean isTempReadHostAvailable() {\n\t\treturn this.tempReadHostAvailable;\n\t}\n\n\tpublic int getSlaveThreshold() {\n\t\treturn slaveThreshold;\n\t}\n\n\tpublic void setSlaveThreshold(int slaveThreshold) {\n\t\tthis.slaveThreshold = slaveThreshold;\n\t}\n\n\tpublic int getSwitchType() {\n\t\treturn switchType;\n\t}\n\n\tpublic String getConnectionInitSql()\n\t{\n\t\treturn connectionInitSql;\n\t}\n\n\tpublic void setConnectionInitSql(String connectionInitSql)\n\t{\n\t\tthis.connectionInitSql = connectionInitSql;\n\t}\n\n\tpublic int getWriteType() {\n\t\treturn writeType;\n\t}\n\n\tpublic void setWriteType(int writeType) {\n\t\tthis.writeType = writeType;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n    public boolean isShowSlaveSql()\n    {\n        return isShowSlaveSql;\n    }\n\n    public int getMaxCon() {\n\t\treturn maxCon;\n\t}\n\n\tpublic void setMaxCon(int maxCon) {\n\t\tthis.maxCon = maxCon;\n\t}\n\n\tpublic int getMinCon() {\n\t\treturn minCon;\n\t}\n\n\tpublic void setMinCon(int minCon) {\n\t\tthis.minCon = minCon;\n\t}\n\n\tpublic String getSlaveIDs() {\n\t\treturn slaveIDs;\n\t}\n\n\tpublic void setSlaveIDs(String slaveIDs) {\n\t\tthis.slaveIDs = slaveIDs;\n\t}\n\n\tpublic int getBalance() {\n\t\treturn balance;\n\t}\n\n\tpublic void setBalance(int balance) {\n\t\tthis.balance = balance;\n\t}\n\n\tpublic int getBalanceType() {\n\t\treturn balanceType;\n\t}\n\n\tpublic void setBalanceType(int balanceType) {\n\t\tthis.balanceType = balanceType;\n\t}\n\n\tpublic String getDbType() {\n\t\treturn dbType;\n\t}\n\n\tpublic String getDbDriver() {\n\t\treturn dbDriver;\n\t}\n\n\tpublic DBHostConfig[] getWriteHosts() {\n\t\treturn writeHosts;\n\t}\n\n\tpublic Map<Integer, DBHostConfig[]> getReadHosts() {\n\t\treturn readHosts;\n\t}\n\n\tpublic String getHearbeatSQL() {\n\t\treturn hearbeatSQL;\n\t}\n\n\tpublic void setHearbeatSQL(String heartbeatSQL) {\n\t\tthis.hearbeatSQL = heartbeatSQL;\n        Matcher matcher = pattern.matcher(heartbeatSQL);\n        if (matcher.find())\n        {\n            isShowSlaveSql=true;\n        }\n        Matcher matcher2 = patternCluster.matcher(heartbeatSQL);\n        if (matcher2.find())\n        {\n        \tisShowClusterSql=true;\n        }\n\t}\n\n\tpublic String getFilters() {\n\t\treturn filters;\n\t}\n\n\tpublic void setFilters(String filters) {\n\t\tthis.filters = filters;\n\t}\n\n\tpublic long getLogTime() {\n\t\treturn logTime;\n\t}\n\n\tpublic boolean isShowClusterSql() {\n\t\treturn this.isShowClusterSql;\n\t}\n\n\tpublic void setLogTime(long logTime) {\n\t\tthis.logTime = logTime;\n\t}\n\n\tpublic void addDataNode(String name){\n\t\tthis.dataNodes.add(name);\n\t}\n\n\tpublic String getRandomDataNode() {\n\t\tint index = (int) (Math.random() * dataNodes.size());\n\t\treturn Iterables.get(dataNodes,index);\n\t}\n\n    public boolean containDataNode(String randomDn) {\n        return dataNodes.contains(randomDn);\n    }\n\n\tpublic int getMaxRetryCount() {\n\t\treturn maxRetryCount;\n\t}\n\n\tpublic void setMaxRetryCount(int maxRetryCount) {\n\t\tthis.maxRetryCount = maxRetryCount;\n\t}\n\n\tpublic String getNotSwitch() {\n\t\treturn notSwitch;\n\t}\n\n\tpublic void setNotSwitch(String notSwitch) {\n\t\tthis.notSwitch = notSwitch;\n\t}\n\n\tpublic boolean isJDBCDriver() {\n\t\treturn \"jdbc\".equalsIgnoreCase(dbDriver);\n\t}\n\n\tpublic boolean isNativeDriver() {\n\t\treturn \"native\".equalsIgnoreCase(dbDriver);\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/model/DataNodeConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model;\n\n/**\n * 用于描述一个数据节点的配置\n * \n * @author mycat\n */\npublic final class DataNodeConfig {\n\n\tprivate final String name;\n\tprivate final String database;\n\tprivate final String dataHost;\n\n\tpublic DataNodeConfig(String name, String database, String dataHost) {\n\t\tsuper();\n\t\tthis.name = name;\n\t\tthis.database = database;\n\t\tthis.dataHost = dataHost;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic String getDatabase() {\n\t\treturn database;\n\t}\n\n\tpublic String getDataHost() {\n\t\treturn dataHost;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/model/FirewallConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model;\n\nimport java.io.File;\nimport java.io.InputStream;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.transform.Transformer;\nimport javax.xml.transform.TransformerFactory;\nimport javax.xml.transform.dom.DOMSource;\nimport javax.xml.transform.stream.StreamResult;\n\nimport io.mycat.util.StringUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.xml.sax.EntityResolver;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\n\nimport com.alibaba.druid.wall.WallConfig;\nimport com.alibaba.druid.wall.WallProvider;\nimport com.alibaba.druid.wall.spi.MySqlWallProvider;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.loader.xml.XMLServerLoader;\n\n/**\n * 防火墙配置定义\n * \n * @author songwie\n * @author zhuam\n */\npublic final class FirewallConfig {\n\t\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(FirewallConfig.class);\n\t\n    private Map<String, List<UserConfig>> whitehost;//具体host的白名单\n\tprivate Map<Pattern, List<UserConfig>> whitehostMask;//网段的白名单\n\tpublic static Pattern getMaskPattern(String host){\n\t\treturn Pattern.compile(host.replaceAll(\"\\\\.\",\"\\\\\\\\.\").replaceAll(\"[*]\",\"[0-9]*\").replaceAll(\"%\",\"[0-9]*\"));\n\t}\n\tpublic static String getHost(Pattern maskPattern){\n\t\treturn maskPattern.pattern().replaceAll(\"\\\\\\\\.\",\"\\\\.\").replaceAll(\"\\\\[0-9\\\\]\",\"\");\n\t}\n    private List<String> blacklist;\n    private boolean check = false;\n    \n    private WallConfig wallConfig = new WallConfig();\n     \n    private static WallProvider provider ;\n    \n    public FirewallConfig() { }\n    \n    public void init(){\n    \tif(check){\n    \t\tprovider = new MySqlWallProvider(wallConfig);\n    \t\tprovider.setBlackListEnable(true);\n    \t}\n    }\n\n\tpublic Map<Pattern, List<UserConfig>> getWhitehostMask() {\n\t\treturn whitehostMask;\n\t}\n\n\tpublic void setWhitehostMask(Map<Pattern, List<UserConfig>> whitehostMask) {\n\t\tthis.whitehostMask = whitehostMask;\n\t}\n\n    public WallProvider getWallProvider(){\n    \treturn provider;\n    }\n\n\tpublic Map<String, List<UserConfig>> getWhitehost() {\n\t\treturn this.whitehost;\n\t}\n\tpublic void setWhitehost(Map<String, List<UserConfig>> whitehost) {\n\t\tthis.whitehost = whitehost;\n\t}\n\t/**\n\t * 通过manager端命令动态配置白名单，配置防火墙方法之一，一共有两处，另一处:\n\t * @see  XMLServerLoader\n\t *\n\t * @modification 修改增加网段白名单\n\t * @date 2016/12/8\n\t * @modifiedBy Hash Zhang\n\t */\n\tpublic boolean addWhitehost(String host, List<UserConfig> Users) {\n\t\tif (existsHost(host)){\n\t\t\treturn false;\t\n\t\t}\n\t\telse {\n\t\t if(host.contains(\"*\")||host.contains(\"%\")){\n\t\t\t this.whitehostMask.put(getMaskPattern(host),Users);\n\t\t }else {\n\t\t \tthis.whitehost.put(host, Users);\n\n\t\t }\n\t\t return true;\n\t\t}\n\t}\n\t\n\tpublic List<String> getBlacklist() {\n\t\treturn this.blacklist;\n\t}\n\tpublic void setBlacklist(List<String> blacklist) {\n\t\tthis.blacklist = blacklist;\n\t}\n\t\n\tpublic WallProvider getProvider() {\n\t\treturn provider;\n\t}\n\n\tpublic boolean existsHost(String host) {\n\t\treturn this.whitehost!=null && whitehost.get(host)!=null ;\n\t}\n\tpublic boolean canConnect(String host,String user) {\n\t\tif(whitehost==null || whitehost.size()==0){\n\t\t\tMycatConfig config = MycatServer.getInstance().getConfig();\n\t\t\tMap<String, UserConfig> users = config.getUsers();\n\t\t\treturn users.containsKey(user);\n\t\t}else{\n\t\t\tList<UserConfig> list = whitehost.get(host);\n\t\t\tif(list==null){\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfor(UserConfig userConfig : list){\n\t\t\t\tif(userConfig.getName().equals(user)){\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false ;\n\t}\n\t\n\tpublic static void setProvider(WallProvider provider) {\n\t\tFirewallConfig.provider = provider;\n\t}\n\n\tpublic void setWallConfig(WallConfig wallConfig) {\n\t\tthis.wallConfig = wallConfig;\n\t\t\n\t}\n\n\tpublic boolean isCheck() {\n\t\treturn this.check;\n\t}\n\n\tpublic void setCheck(boolean check) {\n\t\tthis.check = check;\n\t}\n\n\tpublic WallConfig getWallConfig() {\n\t\treturn this.wallConfig;\n\t}\n\t\n\tpublic synchronized static void updateToFile(String host, List<UserConfig> userConfigs) throws Exception{\n\t\tLOGGER.debug(\"set white host:\" + host + \"user:\" + userConfigs);\n\t\tString filename = SystemConfig.getHomePath()+ File.separator +\"conf\"+ File.separator +\"server.xml\";\n\t\t//String filename = \"E:\\\\MyProject\\\\Mycat-Server\\\\src\\\\main\\\\resources\\\\server.xml\";\n\n        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n        factory.setNamespaceAware(false);\n        factory.setValidating(false);\n        DocumentBuilder builder = factory.newDocumentBuilder();\n        builder.setEntityResolver(new IgnoreDTDEntityResolver());\n        Document xmldoc = builder.parse(filename);\n        Element whitehost = (Element) xmldoc.getElementsByTagName(\"whitehost\").item(0);\n        Element firewall = (Element) xmldoc.getElementsByTagName(\"firewall\").item(0);\n        \n\t\tif (firewall == null) {\n\t\t\tfirewall = xmldoc.createElement(\"firewall\");\n            Element root = xmldoc.getDocumentElement();\n            root.appendChild(firewall);\n            if(whitehost==null){\n            \twhitehost = xmldoc.createElement(\"whitehost\");\n            \tfirewall.appendChild(whitehost);\n            }\n        }\n\n        for(UserConfig userConfig : userConfigs){\n        \tString user = userConfig.getName();\n        \tElement hostEle = xmldoc.createElement(\"host\");\n        \thostEle.setAttribute(\"host\", host);\n        \thostEle.setAttribute(\"user\", user);\n\n        \twhitehost.appendChild(hostEle);\n        }\n        \n             \n        TransformerFactory factory2 = TransformerFactory.newInstance();\n        Transformer former = factory2.newTransformer();\n        String systemId = xmldoc.getDoctype().getSystemId();\n        if(systemId!=null){\n            former.setOutputProperty(javax.xml.transform.OutputKeys.DOCTYPE_SYSTEM, systemId);    \n        }\n        former.transform(new DOMSource(xmldoc), new StreamResult(new File(filename)));\n\n\t}\n\tstatic class IgnoreDTDEntityResolver implements EntityResolver{\n\t\tpublic InputSource resolveEntity(java.lang.String publicId, java.lang.String systemId) throws SAXException, java.io.IOException{\n\t\t\tif (systemId.contains(\"server.dtd\")){ \n\t\t\t\t//InputSource is = new InputSource(new ByteArrayInputStream(\"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\".getBytes()));\n\t\t\t\tInputStream dtd = XMLServerLoader.class.getResourceAsStream(\"/server.dtd\");\n\t\t\t\tInputSource is = new InputSource(dtd);\n\t\t\t\treturn is; \n\t\t    } else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t} \n\t}\n//\tpublic static void main(String[] args) throws Exception {\n//        List<UserConfig> userConfigs = new ArrayList<UserConfig>();\n//        UserConfig user = new UserConfig();\n//        user.setName(\"mycat\");\n//        userConfigs.add(user);\n//\t\tupdateToFile(\"127.0.0.1\",userConfigs);\n//\t}\n\t\n\t\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/model/MycatNodeConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model;\n\n/**\n * @author mycat\n * @author mycat\n */\npublic final class MycatNodeConfig {\n\n    private String name;\n    private String host;\n    private int port;\n    private int weight;\n\n    public MycatNodeConfig(String name, String host, int port, int weight) {\n        this.name = name;\n        this.host = host;\n        this.port = port;\n        this.weight = weight;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getHost() {\n        return host;\n    }\n\n    public void setHost(String host) {\n        this.host = host;\n    }\n\n    public int getPort() {\n        return port;\n    }\n\n    public void setPort(int port) {\n        this.port = port;\n    }\n\n    public int getWeight() {\n        return weight;\n    }\n\n    public void setWeight(int weight) {\n        this.weight = weight;\n    }\n\n    @Override\n    public String toString() {\n        return new StringBuilder().append(\"[name=\").append(name).append(\",host=\").append(host).append(\",port=\")\n                .append(port).append(\",weight=\").append(weight).append(']').toString();\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/model/SchemaConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.Set;\nimport java.util.concurrent.ThreadLocalRandom;\n\n/**\n * @author mycat\n */\npublic class SchemaConfig {\n//\tprivate final Random random = new Random();\n\tprivate final String name;\n\tprivate final Map<String, TableConfig> tables;\n\tprivate final boolean noSharding;\n\tprivate final String dataNode;\n\tprivate final Set<String> metaDataNodes;\n\tprivate final Set<String> allDataNodes;\n\tprivate String randomDataNode = null;\n\t/**\n\t * when a select sql has no limit condition ,and default max limit to\n\t * prevent memory problem when return a large result set\n\t */\n\tprivate final int defaultMaxLimit;\n\tprivate final boolean checkSQLSchema;\n\tprivate  boolean needSupportMultiDBType=false;\n\tprivate  String defaultDataNodeDbType;\n\t/**\n\t * key is join relation ,A.ID=B.PARENT_ID value is Root Table ,if a->b*->c*\n\t * ,then A is root table\n\t */\n\tprivate final Map<String, TableConfig> joinRel2TableMap = new HashMap<String, TableConfig>();\n\tprivate final String[] allDataNodeStrArr;\n\n\tprivate  Map<String,String> dataNodeDbTypeMap=new HashMap<>();\n\n\tpublic SchemaConfig(String name, String dataNode,\n\t\t\tMap<String, TableConfig> tables, int defaultMaxLimit,\n\t\t\tboolean checkSQLschema,String randomDataNode) {\n\t\tthis.name = name;\n\t\tthis.dataNode = dataNode;\n\t\tthis.checkSQLSchema = checkSQLschema;\n\t\tthis.tables = tables;\n\t\tthis.defaultMaxLimit = defaultMaxLimit;\n\t\tbuildJoinMap(tables);\n\t\tthis.noSharding = (tables == null || tables.isEmpty());\n\t\tif (noSharding && dataNode == null) {\n\t\t\tthrow new RuntimeException(name\n\t\t\t\t\t+ \" in noSharding mode schema must have default dataNode \");\n\t\t}\n\t\tthis.metaDataNodes = buildMetaDataNodes();\n\t\tthis.allDataNodes = buildAllDataNodes();\n//\t\tthis.metaDataNodes = buildAllDataNodes();\n\t\tif (this.allDataNodes != null && !this.allDataNodes.isEmpty()) {\n\t\t\tString[] dnArr = new String[this.allDataNodes.size()];\n\t\t\tdnArr = this.allDataNodes.toArray(dnArr);\n\t\t\tthis.allDataNodeStrArr = dnArr;\n\t\t} else {\n\t\t\tthis.allDataNodeStrArr = null;\n\t\t}\n\t\tthis.randomDataNode =randomDataNode;\n\t}\n\n\tpublic String getDefaultDataNodeDbType()\n\t{\n\t\treturn defaultDataNodeDbType;\n\t}\n\n\tpublic void setDefaultDataNodeDbType(String defaultDataNodeDbType)\n\t{\n\t\tthis.defaultDataNodeDbType = defaultDataNodeDbType;\n\t}\n\n\tpublic boolean isCheckSQLSchema() {\n\t\treturn checkSQLSchema;\n\t}\n\n\tpublic int getDefaultMaxLimit() {\n\t\treturn defaultMaxLimit;\n\t}\n\n\tprivate void buildJoinMap(Map<String, TableConfig> tables2) {\n\n\t\tif (tables == null || tables.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (TableConfig tc : tables.values()) {\n\t\t\tif (tc.isChildTable()) {\n\t\t\t\tTableConfig rootTc = tc.getRootParent();\n\t\t\t\tString joinRel1 = tc.getName() + '.' + tc.getJoinKey() + '='\n\t\t\t\t\t\t+ tc.getParentTC().getName() + '.' + tc.getParentKey();\n\t\t\t\tString joinRel2 = tc.getParentTC().getName() + '.'\n\t\t\t\t\t\t+ tc.getParentKey() + '=' + tc.getName() + '.'\n\t\t\t\t\t\t+ tc.getJoinKey();\n\t\t\t\tjoinRel2TableMap.put(joinRel1, rootTc);\n\t\t\t\tjoinRel2TableMap.put(joinRel2, rootTc);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tpublic boolean isNeedSupportMultiDBType()\n\t{\n\t\treturn needSupportMultiDBType;\n\t}\n\n\tpublic void setNeedSupportMultiDBType(boolean needSupportMultiDBType)\n\t{\n\t\tthis.needSupportMultiDBType = needSupportMultiDBType;\n\t}\n\n\tpublic Map<String, TableConfig> getJoinRel2TableMap() {\n\t\treturn joinRel2TableMap;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic String getDataNode() {\n\t\treturn dataNode;\n\t}\n\n\tpublic Map<String, TableConfig> getTables() {\n\t\treturn tables;\n\t}\n\n\tpublic boolean isNoSharding() {\n\t\treturn noSharding;\n\t}\n\n\tpublic Set<String> getMetaDataNodes() {\n\t\treturn metaDataNodes;\n\t}\n\n\tpublic Set<String> getAllDataNodes() {\n\t\treturn allDataNodes;\n\t}\n\n\tpublic Map<String, String> getDataNodeDbTypeMap()\n\t{\n\t\treturn dataNodeDbTypeMap;\n\t}\n\n\tpublic void setDataNodeDbTypeMap(Map<String, String> dataNodeDbTypeMap)\n\t{\n\t\tthis.dataNodeDbTypeMap = dataNodeDbTypeMap;\n\t}\n\n\tpublic String getRandomDataNode() {\n\t\tif (this.randomDataNode != null&&!\"\".equalsIgnoreCase(this.randomDataNode)){\n\t\t\treturn this.randomDataNode;\n\t\t}\n\t\tif (this.allDataNodeStrArr == null) {\n\t\t\treturn null;\n\t\t}\n\t\tint index = Math.abs(ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE)) % allDataNodeStrArr.length;\n\t\treturn this.allDataNodeStrArr[index];\n\t}\n\n\t/**\n\t * 取得含有不同Meta信息的数据节点,比如表和表结构。\n\t */\n\tprivate Set<String> buildMetaDataNodes() {\n\t\tSet<String> set = new HashSet<String>();\n\t\tif (!isEmpty(dataNode)) {\n\t\t\tset.add(dataNode);\n\t\t}\n\t\tif (!noSharding) {\n\t\t\tfor (TableConfig tc : tables.values()) {\n\t\t\t\tset.add(tc.getDataNodes().get(0));\n\t\t\t}\n\t\t}\n\n\t\treturn set;\n\t}\n\n\t/**\n\t * 取得该schema的所有数据节点\n\t */\n\tprivate Set<String> buildAllDataNodes() {\n\t\tSet<String> set = new HashSet<String>();\n\t\tif (!isEmpty(dataNode)) {\n\t\t\tset.add(dataNode);\n\t\t}\n\t\tif (!noSharding) {\n\t\t\tfor (TableConfig tc : tables.values()) {\n\t\t\t\tset.addAll(tc.getDataNodes());\n\t\t\t}\n\t\t}\n\t\treturn set;\n\t}\n\n\tprivate static boolean isEmpty(String str) {\n\t\treturn ((str == null) || (str.length() == 0));\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/model/SystemConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport io.mycat.config.Isolations;\n\n/**\n * 系统基础配置项\n *\n * @author mycat\n */\npublic final class SystemConfig {\n\n\tpublic static final String SYS_HOME = \"MYCAT_HOME\";\n\tprivate static final int DEFAULT_PORT = 8066;\n\tprivate static final int DEFAULT_MANAGER_PORT = 9066;\n\tprivate static final String DEFAULT_CHARSET = \"utf8\";\n\n\tprivate static final String DEFAULT_SQL_PARSER = \"druidparser\";// fdbparser, druidparser\n\tprivate static final short DEFAULT_BUFFER_CHUNK_SIZE = 4096;\n\tprivate static final int DEFAULT_BUFFER_POOL_PAGE_SIZE = 512*1024*4;\n\tprivate static final short DEFAULT_BUFFER_POOL_PAGE_NUMBER = 64;\n\n\n\n\tprivate int removeGraveAccent;\n\tprivate int processorBufferLocalPercent;\n\tprivate static final int DEFAULT_PROCESSORS = Runtime.getRuntime().availableProcessors();\n\tprivate int frontSocketSoRcvbuf = 1024 * 1024;\n\tprivate int frontSocketSoSndbuf = 4 * 1024 * 1024;\n\tprivate int backSocketSoRcvbuf = 4 * 1024 * 1024;// mysql 5.6\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// net_buffer_length\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// defaut 4M\n    \n\tprivate final  static String RESERVED_SYSTEM_MEMORY_BYTES = \"384m\";\n\tprivate final static String MEMORY_PAGE_SIZE = \"1m\";\n\tprivate final static String SPILLS_FILE_BUFFER_SIZE = \"2K\";\n\tprivate final static String DATANODE_SORTED_TEMP_DIR = \"datanode\";\n\tprivate int backSocketSoSndbuf = 1024 * 1024;\n\tprivate int frontSocketNoDelay = 1; // 0=false\n\tprivate int backSocketNoDelay = 1; // 1=true\n\tpublic static final int DEFAULT_POOL_SIZE = 128;// 保持后端数据通道的默认最大值\n\tpublic static final long  DEFAULT_IDLE_TIMEOUT = 30 * 60 * 1000L;\n\tpublic static final long  DEFAULT_AUTH_TIMEOUT = 15 * 1000L;\n\tprivate static final long DEFAULT_PROCESSOR_CHECK_PERIOD = 1 * 1000L;\n\tprivate static final long DEFAULT_DATANODE_IDLE_CHECK_PERIOD = 5 * 60 * 1000L; //连接空闲检查\n\tprivate static final long DEFAULT_DATANODE_HEARTBEAT_PERIOD = 10 * 1000L;  //心跳检查周期\n\tprivate static final long DEFAULT_CLUSTER_HEARTBEAT_PERIOD = 5 * 1000L;\n\tprivate static final long DEFAULT_CLUSTER_HEARTBEAT_TIMEOUT = 10 * 1000L;\n\tprivate static final int  DEFAULT_CLUSTER_HEARTBEAT_RETRY = 10;\n\tprivate static final int  DEFAULT_MAX_LIMIT = 100;\n\tprivate static final String  DEFAULT_CLUSTER_HEARTBEAT_USER = \"_HEARTBEAT_USER_\";\n\tprivate static final String  DEFAULT_CLUSTER_HEARTBEAT_PASS = \"_HEARTBEAT_PASS_\";\n\tprivate static final int     DEFAULT_PARSER_COMMENT_VERSION = 50148;\n\tprivate static final int     DEFAULT_SQL_RECORD_COUNT = 10;\n\tprivate static final boolean DEFAULT_USE_ZK_SWITCH = false;\n\tprivate static final int     DEFAULT_MAX_PREPAREDSTMT_COUNT = 16382;\n\tprivate int maxStringLiteralLength = 65535;\n\tprivate int frontWriteQueueSize = 2048;\n\tprivate String bindIp = \"0.0.0.0\";\n\tprivate String fakeMySQLVersion = null;\n\tprivate int serverPort;\n\tprivate int managerPort;\n\tprivate String charset;\n\tprivate int processors;\n\tprivate int processorExecutor;\n\tprivate int timerExecutor;\n\tprivate int managerExecutor;\n\tprivate int serverBacklog = 2048;\n\tprivate long idleTimeout;\n\tprivate long authTimeout;\n\tprivate int catletClassCheckSeconds = 60;\n\t// sql execute timeout (second)\n\tprivate long sqlExecuteTimeout = 300;\n\tprivate long processorCheckPeriod;\n\tprivate long dataNodeIdleCheckPeriod;\n\tprivate long dataNodeHeartbeatPeriod;\n\tprivate String clusterHeartbeatUser;\n\tprivate String clusterHeartbeatPass;\n\tprivate long clusterHeartbeatPeriod;\n\tprivate long clusterHeartbeatTimeout;\n\tprivate int clusterHeartbeatRetry;\n\tprivate int txIsolation;\n\tprivate int parserCommentVersion;\n\tprivate int sqlRecordCount;\n\tprivate String sequnceHandlerPattern = SEQUENCEHANDLER_PATTERN;\n\n\t/**\n\t * 预处理占位符最大数量\n\t */\n\tprivate int maxPreparedStmtCount;\n\n\t// a page size\n\tprivate int bufferPoolPageSize;\n\n\t//minimum allocation unit\n\tprivate short bufferPoolChunkSize;\n\t\n\t// buffer pool page number \n\tprivate short bufferPoolPageNumber;\n\t\n\t//大结果集阈值，默认512kb\n\tprivate int maxResultSet=512*1024;\n\t//大结果集拒绝策略次数过滤限制,默认10次\n\tprivate int bigResultSizeSqlCount=10;\n\t//大结果集拒绝策咯，bufferpool使用率阈值(0-100)，默认80%\n\tprivate int  bufferUsagePercent=80;\n\t//大结果集保护策咯，0:不开启,1:级别1为在当前mucat bufferpool\n\t//使用率大于bufferUsagePercent阈值时，拒绝超过defaultBigResultSizeSqlCount\n\t//sql次数阈值并且符合超过大结果集阈值maxResultSet的所有sql\n\t//默认值0\n\tprivate int  flowControlRejectStrategy=0;\n\t//清理大结果集记录周期\n\tprivate long clearBigSqLResultSetMapMs=10*60*1000;\n\n\tprivate int defaultMaxLimit = DEFAULT_MAX_LIMIT;\n\tpublic static final int SEQUENCEHANDLER_LOCALFILE = 0;\n\tpublic static final int SEQUENCEHANDLER_MYSQLDB = 1;\n\tpublic static final int SEQUENCEHANDLER_LOCAL_TIME = 2;\n\tpublic static final int SEQUENCEHANDLER_ZK_DISTRIBUTED = 3;\n\tpublic static final int SEQUENCEHANDLER_ZK_GLOBAL_INCREMENT = 4;\n\tpublic static final int SEQUENCEHANDLER_DEF_GLOBAL_INCREMENT = 5;\n\tpublic static String sequenceHanlderClass = null;\n\tpublic static final String SEQUENCEHANDLER_PATTERN = \"(?:(\\\\s*next\\\\s+value\\\\s+for\\\\s*MYCATSEQ_(\\\\w+))(,|\\\\)|\\\\s)*)+\";\n\t\n\tprivate final int DEFAULT_SEQUNCE_MYSQL_RETRY_COUT=4;  //mysql全局序列默认重试次数\n\tprivate final long DEFAULT_SEQUNCE_MYSQL_WATI_TIME=10 * 1000;//mysql db方式默认等待时间\n\n\tprivate int sequnceMySqlRetryCount = DEFAULT_SEQUNCE_MYSQL_RETRY_COUT;\n\tprivate long sequnceMySqlWaitTime = DEFAULT_SEQUNCE_MYSQL_WATI_TIME;\n\tprivate int ignoreUnknownCommand = 0;//io/mycat/net/handler/FrontendCommandHandler.java:忽略未知命令\n\t\n\t\n\t/*\n\t * 注意！！！ 目前mycat支持的MySQL版本，如果后续有新的MySQL版本,请添加到此数组， 对于MySQL的其他分支，\n\t * 比如MariaDB目前版本号已经到10.1.x，但是其驱动程序仍然兼容官方的MySQL,因此这里版本号只需要MySQL官方的版本号即可。\n\t */\n\tpublic static final String[] MySQLVersions = { \"5.5\", \"5.6\", \"5.7\" };\n\tprivate int sequenceHandlerType = SEQUENCEHANDLER_LOCALFILE;\n\tprivate String sqlInterceptor = \"io.mycat.server.interceptor.impl.DefaultSqlInterceptor\";\n\tprivate String sqlInterceptorType = \"select\";\n\tprivate String sqlInterceptorFile = System.getProperty(\"user.dir\")+\"/logs/sql.txt\";\n\tpublic static final int MUTINODELIMIT_SMALL_DATA = 0;\n\tpublic static final int MUTINODELIMIT_LAR_DATA = 1;\n\tprivate int mutiNodeLimitType = MUTINODELIMIT_SMALL_DATA;\n\n\tpublic static final int MUTINODELIMIT_PATCH_SIZE = 100;\n\tprivate int mutiNodePatchSize = MUTINODELIMIT_PATCH_SIZE;\n\n\tprivate String defaultSqlParser = DEFAULT_SQL_PARSER;\n\tprivate int usingAIO = 0;\n\tprivate int packetHeaderSize = 4;\n\tprivate int maxPacketSize = 16 * 1024 * 1024;\n\tprivate int mycatNodeId=1;\n\tprivate int useCompression =0;\t\n\tprivate int useSqlStat = 1;\n\t//子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false\n\tprivate boolean subqueryRelationshipCheck = false;\n\t\n\t// 是否使用HandshakeV10Packet来与client进行通讯, 1:是 , 0:否(使用HandshakePacket)\n\t// 使用HandshakeV10Packet为的是兼容高版本的jdbc驱动, 后期稳定下来考虑全部采用HandshakeV10Packet来通讯\n\tprivate int useHandshakeV10 = 0;\n\n\t//处理分布式事务开关，默认为不过滤分布式事务\n\tprivate int handleDistributedTransactions = 0;\n\n\tprivate int checkTableConsistency = 0;\n\tprivate long checkTableConsistencyPeriod = CHECKTABLECONSISTENCYPERIOD;\n\tprivate final static long CHECKTABLECONSISTENCYPERIOD = 1 * 60 * 1000;\n\n\tprivate int processorBufferPoolType = 0;\n\n\t// 全局表一致性检测任务，默认24小时调度一次\n\tprivate static final long DEFAULT_GLOBAL_TABLE_CHECK_PERIOD = 24 * 60 * 60 * 1000L;\n\tprivate int useGlobleTableCheck = 1;\t// 全局表一致性检查开关\n\t\n\tprivate long glableTableCheckPeriod;\n\n\t// 如果为true的话 严格遵守隔离级别,不会在仅仅只有select语句的时候在事务中切换连接\n\tprivate boolean strictTxIsolation = false;\n\t/**\n\t * Mycat 使用 Off Heap For Merge/Order/Group/Limit计算相关参数\n\t */\n\n\n\t/**\n\t * 是否启用Off Heap for Merge  1-启用，0-不启用\n\t */\n\tprivate int useOffHeapForMerge;\n\n\t/**\n\t *页大小,对应MemoryBlock的大小，单位为M\n\t */\n\tprivate String memoryPageSize;\n\n\n\t/**\n\t * DiskRowWriter写磁盘是临时写Buffer，单位为K\n\t */\n\tprivate String spillsFileBufferSize;\n\n\t/**\n\t * 启用结果集流输出，不经过merge模块,\n\t */\n\tprivate int useStreamOutput;\n\n\t/**\n\t * 该变量仅在Merge使用On Heap\n\t * 内存方式时起作用，如果使用Off Heap内存方式\n\t * 那么可以认为-Xmx就是系统预留内存。\n\t * 在On Heap上给系统预留的内存，\n\t * 主要供新小对象创建，JAVA简单数据结构使用\n\t * 以保证在On Heap上大结果集计算时情况，能快速响应其他\n\t * 连接操作。\n\t */\n\tprivate String systemReserveMemorySize;\n\n\tprivate String XARecoveryLogBaseDir;\n\n\tprivate String XARecoveryLogBaseName;\n\n\t/**\n\t * 排序时，内存不够时，将已经排序的结果集\n\t * 写入到临时目录\n\t */\n\tprivate String dataNodeSortedTempDir;\n\n\t/**\n\t * 是否启用zk切换\n\t */\n\tprivate boolean\tuseZKSwitch=DEFAULT_USE_ZK_SWITCH;\n\n\t\n \t/**\n \t * huangyiming add\n\t * 无密码登陆标示, 0:否,1:是,默认为0\n\t */\n\tprivate int nonePasswordLogin = DEFAULT_NONEPASSWORDLOGIN ;\n\n\tprivate final static int DEFAULT_NONEPASSWORDLOGIN = 0;\n\n\tprivate int parallExecute;\n\t\n    private boolean enableWriteQueueFlowControl;// 写队列流量控制\n    private int writeQueueStopThreshold;// 写队列停止写入阈值\n    private int writeQueueRecoverThreshold;// 写队列恢复写入阈值\n\n\tpublic String getDefaultSqlParser() {\n\t\treturn defaultSqlParser;\n\t}\n\n\tpublic void setDefaultSqlParser(String defaultSqlParser) {\n\t\tthis.defaultSqlParser = defaultSqlParser;\n\t}\n\n\tpublic SystemConfig() {\n\t\tthis.serverPort = DEFAULT_PORT;\n\t\tthis.managerPort = DEFAULT_MANAGER_PORT;\n\t\tthis.charset = DEFAULT_CHARSET;\n\t\tthis.processors = DEFAULT_PROCESSORS;\n\t\tthis.bufferPoolPageSize = DEFAULT_BUFFER_POOL_PAGE_SIZE;\n\t\tthis.bufferPoolChunkSize = DEFAULT_BUFFER_CHUNK_SIZE;\n\t\t\n\t\t/**\n\t\t * 大结果集时 需增大 network buffer pool pages.\n\t\t */\n\t\tthis.bufferPoolPageNumber = (short) (DEFAULT_PROCESSORS*20);\n\n\t\tthis.processorExecutor = (DEFAULT_PROCESSORS != 1) ? DEFAULT_PROCESSORS * 2 : 4;\n\t\tthis.managerExecutor = 2;\n\n\t\tthis.processorBufferLocalPercent = 100;\n\t\tthis.timerExecutor = 2;\n\t\tthis.idleTimeout = DEFAULT_IDLE_TIMEOUT;\n\t\tthis.authTimeout = DEFAULT_AUTH_TIMEOUT;\n\t\tthis.processorCheckPeriod = DEFAULT_PROCESSOR_CHECK_PERIOD;\n\t\tthis.dataNodeIdleCheckPeriod = DEFAULT_DATANODE_IDLE_CHECK_PERIOD;\n\t\tthis.dataNodeHeartbeatPeriod = DEFAULT_DATANODE_HEARTBEAT_PERIOD;\n\t\tthis.clusterHeartbeatUser = DEFAULT_CLUSTER_HEARTBEAT_USER;\n\t\tthis.clusterHeartbeatPass = DEFAULT_CLUSTER_HEARTBEAT_PASS;\n\t\tthis.clusterHeartbeatPeriod = DEFAULT_CLUSTER_HEARTBEAT_PERIOD;\n\t\tthis.clusterHeartbeatTimeout = DEFAULT_CLUSTER_HEARTBEAT_TIMEOUT;\n\t\tthis.clusterHeartbeatRetry = DEFAULT_CLUSTER_HEARTBEAT_RETRY;\n\t\tthis.txIsolation = Isolations.REPEATED_READ;\n\t\tthis.parserCommentVersion = DEFAULT_PARSER_COMMENT_VERSION;\n\t\tthis.sqlRecordCount = DEFAULT_SQL_RECORD_COUNT;\n\t\tthis.glableTableCheckPeriod = DEFAULT_GLOBAL_TABLE_CHECK_PERIOD;\n\t\tthis.useOffHeapForMerge = 0;\n\t\tthis.memoryPageSize = MEMORY_PAGE_SIZE;\n\t\tthis.spillsFileBufferSize = SPILLS_FILE_BUFFER_SIZE;\n\t\tthis.useStreamOutput = 0;\n\t\tthis.systemReserveMemorySize = RESERVED_SYSTEM_MEMORY_BYTES;\n\t\tthis.dataNodeSortedTempDir = System.getProperty(\"user.dir\");\n\t\tthis.XARecoveryLogBaseDir = SystemConfig.getHomePath()+\"/tmlogs/\";\n\t\tthis.XARecoveryLogBaseName =\"tmlog\";\n\n\t\tthis.maxPreparedStmtCount = DEFAULT_MAX_PREPAREDSTMT_COUNT;\n\t\tthis.ignoreUnknownCommand = 0;\n\t\tthis.parallExecute = 0;\n\t\tthis.removeGraveAccent = 1;\n\n        // 流量控制相关\n        this.enableWriteQueueFlowControl = false;\n        this.writeQueueStopThreshold = 10 * 1024;\n        this.writeQueueRecoverThreshold = 512;\n\t}\n\n\tpublic void setMaxPreparedStmtCount(int maxPreparedStmtCount){\n\t\tthis.maxPreparedStmtCount = maxPreparedStmtCount;\n\t}\n\n\tpublic int getMaxPreparedStmtCount(){\n\t\treturn this.maxPreparedStmtCount;\n\t}\n\n\tpublic String getDataNodeSortedTempDir() {\n\t\treturn dataNodeSortedTempDir;\n\t}\n\n\tpublic int getUseOffHeapForMerge() {\n\t\treturn useOffHeapForMerge;\n\t}\n\n\tpublic void setUseOffHeapForMerge(int useOffHeapForMerge) {\n\t\tthis.useOffHeapForMerge = useOffHeapForMerge;\n\t}\n\n\tpublic String getMemoryPageSize() {\n\t\treturn memoryPageSize;\n\t}\n\n\tpublic void setMemoryPageSize(String memoryPageSize) {\n\t\tthis.memoryPageSize = memoryPageSize;\n\t}\n\n\tpublic String getSpillsFileBufferSize() {\n\t\treturn spillsFileBufferSize;\n\t}\n\n\tpublic void setSpillsFileBufferSize(String spillsFileBufferSize) {\n\t\tthis.spillsFileBufferSize = spillsFileBufferSize;\n\t}\n\n\tpublic int getUseStreamOutput() {\n\t\treturn useStreamOutput;\n\t}\n\n\tpublic void setUseStreamOutput(int useStreamOutput) {\n\t\tthis.useStreamOutput = useStreamOutput;\n\t}\n\n\tpublic String getSystemReserveMemorySize() {\n\t\treturn systemReserveMemorySize;\n\t}\n\n\tpublic void setSystemReserveMemorySize(String systemReserveMemorySize) {\n\t\tthis.systemReserveMemorySize = systemReserveMemorySize;\n\t}\n\n\tpublic boolean isUseZKSwitch() {\n\t\treturn useZKSwitch;\n\t}\n\n\tpublic void setUseZKSwitch(boolean useZKSwitch) {\n\t\tthis.useZKSwitch = useZKSwitch;\n\t}\n\n\tpublic String getXARecoveryLogBaseDir() {\n\t\treturn XARecoveryLogBaseDir;\n\t}\n\n\tpublic void setXARecoveryLogBaseDir(String XARecoveryLogBaseDir) {\n\t\tthis.XARecoveryLogBaseDir = XARecoveryLogBaseDir;\n\t}\n\n\tpublic String getXARecoveryLogBaseName() {\n\t\treturn XARecoveryLogBaseName;\n\t}\n\n\tpublic void setXARecoveryLogBaseName(String XARecoveryLogBaseName) {\n\t\tthis.XARecoveryLogBaseName = XARecoveryLogBaseName;\n\t}\n\n\tpublic int getUseGlobleTableCheck() {\n\t\treturn useGlobleTableCheck;\n\t}\n\n\tpublic void setUseGlobleTableCheck(int useGlobleTableCheck) {\n\t\tthis.useGlobleTableCheck = useGlobleTableCheck;\n\t}\n\n\tpublic long getGlableTableCheckPeriod() {\n\t\treturn glableTableCheckPeriod;\n\t}\n\n\tpublic void setGlableTableCheckPeriod(long glableTableCheckPeriod) {\n\t\tthis.glableTableCheckPeriod = glableTableCheckPeriod;\n\t}\n\n\tpublic String getSqlInterceptor() {\n\t\treturn sqlInterceptor;\n\t}\n\n\tpublic void setSqlInterceptor(String sqlInterceptor) {\n\t\tthis.sqlInterceptor = sqlInterceptor;\n\t}\n\n\tpublic int getSequenceHandlerType() {\n\t\treturn sequenceHandlerType;\n\t}\n\n\tpublic void setSequenceHandlerType(int sequenceHandlerType) {\n\t\tthis.sequenceHandlerType = sequenceHandlerType;\n\t}\n\n\tpublic int getPacketHeaderSize() {\n\t\treturn packetHeaderSize;\n\t}\n\n\tpublic void setPacketHeaderSize(int packetHeaderSize) {\n\t\tthis.packetHeaderSize = packetHeaderSize;\n\t}\n\n\tpublic int getMaxPacketSize() {\n\t\treturn maxPacketSize;\n\t}\n\n\tpublic int getCatletClassCheckSeconds() {\n\t\treturn catletClassCheckSeconds;\n\t}\n\n\tpublic void setCatletClassCheckSeconds(int catletClassCheckSeconds) {\n\t\tthis.catletClassCheckSeconds = catletClassCheckSeconds;\n\t}\n\n\tpublic void setMaxPacketSize(int maxPacketSize) {\n\t\tthis.maxPacketSize = maxPacketSize;\n\t}\n\n\tpublic int getFrontWriteQueueSize() {\n\t\treturn frontWriteQueueSize;\n\t}\n\n\tpublic void setFrontWriteQueueSize(int frontWriteQueueSize) {\n\t\tthis.frontWriteQueueSize = frontWriteQueueSize;\n\t}\n\n\tpublic String getBindIp() {\n\t\treturn bindIp;\n\t}\n\n\tpublic void setBindIp(String bindIp) {\n\t\tthis.bindIp = bindIp;\n\t}\n\n\tpublic int getDefaultMaxLimit() {\n\t\treturn defaultMaxLimit;\n\t}\n\n\tpublic void setDefaultMaxLimit(int defaultMaxLimit) {\n\t\tthis.defaultMaxLimit = defaultMaxLimit;\n\t}\n\n\tpublic static String getHomePath() {\n\t\tString home = System.getProperty(SystemConfig.SYS_HOME);\n\t\tif (home != null\n\t\t\t\t&& home.endsWith(File.pathSeparator)) {\n\t\t\t\thome = home.substring(0, home.length() - 1);\n\t\t\t\tSystem.setProperty(SystemConfig.SYS_HOME, home);\n\t\t}\n\n\t\t// MYCAT_HOME为空，默认尝试设置为当前目录或上级目录。BEN\n\t\tif(home == null) {\n\t\t\ttry {\n\t\t\t\tString path = new File(\"..\").getCanonicalPath().replaceAll(\"\\\\\\\\\", \"/\");\n\t\t\t\tFile conf = new File(path+\"/conf\");\n\t\t\t\tif(conf.exists() && conf.isDirectory()) {\n\t\t\t\t\thome = path;\n\t\t\t\t} else {\n\t\t\t\t\tpath = new File(\".\").getCanonicalPath().replaceAll(\"\\\\\\\\\", \"/\");\n\t\t\t\t\tconf = new File(path+\"/conf\");\n\t\t\t\t\tif(conf.exists() && conf.isDirectory()) {\n\t\t\t\t\t\thome = path;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (home != null) {\n\t\t\t\t\tSystem.setProperty(SystemConfig.SYS_HOME, home);\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\t// 如出错，则忽略。\n\t\t\t}\n\t\t}\n\n\t\treturn home;\n\t}\n\t\n\t// 是否使用SQL统计\n\tpublic int getUseSqlStat() \n\t{\n\t\treturn useSqlStat;\n\t}\n\t\n\tpublic void setUseSqlStat(int useSqlStat) \n\t{\n\t\tthis.useSqlStat = useSqlStat;\n\t}\n\n\tpublic int getUseCompression()\n\t{\n\t\treturn useCompression;\n\t}\n\n\tpublic void setUseCompression(int useCompression)\n\t{\n\t\tthis.useCompression = useCompression;\n\t}\n\n\tpublic String getCharset() {\n\t\treturn charset;\n\t}\n\n\tpublic void setCharset(String charset) {\n\t\tthis.charset = charset;\n\t}\n\n\tpublic String getFakeMySQLVersion() {\n\t\treturn fakeMySQLVersion;\n\t}\n\n\tpublic void setFakeMySQLVersion(String mysqlVersion) {\n\t\tthis.fakeMySQLVersion = mysqlVersion;\n\t}\n\n\tpublic int getServerPort() {\n\t\treturn serverPort;\n\t}\n\n\tpublic void setServerPort(int serverPort) {\n\t\tthis.serverPort = serverPort;\n\t}\n\n\tpublic int getManagerPort() {\n\t\treturn managerPort;\n\t}\n\n\tpublic void setManagerPort(int managerPort) {\n\t\tthis.managerPort = managerPort;\n\t}\n\n\tpublic int getProcessors() {\n\t\treturn processors;\n\t}\n\n\tpublic void setProcessors(int processors) {\n\t\tthis.processors = processors;\n\t}\n\n\tpublic int getProcessorExecutor() {\n\t\treturn processorExecutor;\n\t}\n\n\tpublic void setProcessorExecutor(int processorExecutor) {\n\t\tthis.processorExecutor = processorExecutor;\n\t}\n\n\tpublic int getManagerExecutor() {\n\t\treturn managerExecutor;\n\t}\n\n\tpublic void setManagerExecutor(int managerExecutor) {\n\t\tthis.managerExecutor = managerExecutor;\n\t}\n\n\tpublic int getTimerExecutor() {\n\t\treturn timerExecutor;\n\t}\n\n\tpublic void setTimerExecutor(int timerExecutor) {\n\t\tthis.timerExecutor = timerExecutor;\n\t}\n\n\tpublic int getServerBacklog() {\n\t\treturn serverBacklog;\n\t}\n\n\tpublic void setServerBacklog(int serverBacklog) {\n\t\tthis.serverBacklog = serverBacklog;\n\t}\n\n\tpublic long getIdleTimeout() {\n\t\treturn idleTimeout;\n\t}\n\n\tpublic void setIdleTimeout(long idleTimeout) {\n\t\tthis.idleTimeout = idleTimeout;\n\t}\n\n\tpublic long getAuthTimeout() {\n\t\treturn authTimeout;\n\t}\n\n\tpublic void setAuthTimeout(long authTimeout) {\n\t\tthis.authTimeout = authTimeout;\n\t}\n\n\tpublic long getProcessorCheckPeriod() {\n\t\treturn processorCheckPeriod;\n\t}\n\n\tpublic void setProcessorCheckPeriod(long processorCheckPeriod) {\n\t\tthis.processorCheckPeriod = processorCheckPeriod;\n\t}\n\n\tpublic long getDataNodeIdleCheckPeriod() {\n\t\treturn dataNodeIdleCheckPeriod;\n\t}\n\n\tpublic void setDataNodeIdleCheckPeriod(long dataNodeIdleCheckPeriod) {\n\t\tthis.dataNodeIdleCheckPeriod = dataNodeIdleCheckPeriod;\n\t}\n\n\tpublic long getDataNodeHeartbeatPeriod() {\n\t\treturn dataNodeHeartbeatPeriod;\n\t}\n\n\tpublic void setDataNodeHeartbeatPeriod(long dataNodeHeartbeatPeriod) {\n\t\tthis.dataNodeHeartbeatPeriod = dataNodeHeartbeatPeriod;\n\t}\n\n\tpublic String getClusterHeartbeatUser() {\n\t\treturn clusterHeartbeatUser;\n\t}\n\n\tpublic void setClusterHeartbeatUser(String clusterHeartbeatUser) {\n\t\tthis.clusterHeartbeatUser = clusterHeartbeatUser;\n\t}\n\n\tpublic long getSqlExecuteTimeout() {\n\t\treturn sqlExecuteTimeout;\n\t}\n\n\tpublic void setSqlExecuteTimeout(long sqlExecuteTimeout) {\n\t\tthis.sqlExecuteTimeout = sqlExecuteTimeout;\n\t}\n\n\tpublic String getClusterHeartbeatPass() {\n\t\treturn clusterHeartbeatPass;\n\t}\n\n\tpublic void setClusterHeartbeatPass(String clusterHeartbeatPass) {\n\t\tthis.clusterHeartbeatPass = clusterHeartbeatPass;\n\t}\n\n\tpublic long getClusterHeartbeatPeriod() {\n\t\treturn clusterHeartbeatPeriod;\n\t}\n\n\tpublic void setClusterHeartbeatPeriod(long clusterHeartbeatPeriod) {\n\t\tthis.clusterHeartbeatPeriod = clusterHeartbeatPeriod;\n\t}\n\n\tpublic long getClusterHeartbeatTimeout() {\n\t\treturn clusterHeartbeatTimeout;\n\t}\n\n\tpublic void setClusterHeartbeatTimeout(long clusterHeartbeatTimeout) {\n\t\tthis.clusterHeartbeatTimeout = clusterHeartbeatTimeout;\n\t}\n\n\tpublic int getFrontsocketsorcvbuf() {\n\t\treturn frontSocketSoRcvbuf;\n\t}\n\n\tpublic int getFrontsocketsosndbuf() {\n\t\treturn frontSocketSoSndbuf;\n\t}\n\n\tpublic int getBacksocketsorcvbuf() {\n\t\treturn backSocketSoRcvbuf;\n\t}\n\n\tpublic int getBacksocketsosndbuf() {\n\t\treturn backSocketSoSndbuf;\n\t}\n\n\tpublic int getClusterHeartbeatRetry() {\n\t\treturn clusterHeartbeatRetry;\n\t}\n\n\tpublic void setClusterHeartbeatRetry(int clusterHeartbeatRetry) {\n\t\tthis.clusterHeartbeatRetry = clusterHeartbeatRetry;\n\t}\n\n\tpublic int getTxIsolation() {\n\t\treturn txIsolation;\n\t}\n\n\tpublic void setTxIsolation(int txIsolation) {\n\t\tthis.txIsolation = txIsolation;\n\t}\n\n\tpublic int getParserCommentVersion() {\n\t\treturn parserCommentVersion;\n\t}\n\n\tpublic void setParserCommentVersion(int parserCommentVersion) {\n\t\tthis.parserCommentVersion = parserCommentVersion;\n\t}\n\n\tpublic int getSqlRecordCount() {\n\t\treturn sqlRecordCount;\n\t}\n\n\tpublic void setSqlRecordCount(int sqlRecordCount) {\n\t\tthis.sqlRecordCount = sqlRecordCount;\n\t}\n\n\n\tpublic short getBufferPoolChunkSize() {\n\t\treturn bufferPoolChunkSize;\n\t}\n\n\tpublic void setBufferPoolChunkSize(short bufferPoolChunkSize) {\n\t\tthis.bufferPoolChunkSize = bufferPoolChunkSize;\n\t}\n\t\n\tpublic int getMaxResultSet() {\n\t\treturn maxResultSet;\n\t}\n\n\tpublic void setMaxResultSet(int maxResultSet) {\n\t\tthis.maxResultSet = maxResultSet;\n\t}\n\n\tpublic int getBigResultSizeSqlCount() {\n\t\treturn bigResultSizeSqlCount;\n\t}\n\n\tpublic void setBigResultSizeSqlCount(int bigResultSizeSqlCount) {\n\t\tthis.bigResultSizeSqlCount = bigResultSizeSqlCount;\n\t}\n\n\tpublic int getBufferUsagePercent() {\n\t\treturn bufferUsagePercent;\n\t}\n\n\tpublic void setBufferUsagePercent(int bufferUsagePercent) {\n\t\tthis.bufferUsagePercent = bufferUsagePercent;\n\t}\n\n\tpublic int getFlowControlRejectStrategy() {\n\t\treturn flowControlRejectStrategy;\n\t}\n\n\tpublic void setFlowControlRejectStrategy(int flowControlRejectStrategy) {\n\t\tthis.flowControlRejectStrategy = flowControlRejectStrategy;\n\t}\n\n\tpublic long getClearBigSqLResultSetMapMs() {\n\t\treturn clearBigSqLResultSetMapMs;\n\t}\n\n\tpublic void setClearBigSqLResultSetMapMs(long clearBigSqLResultSetMapMs) {\n\t\tthis.clearBigSqLResultSetMapMs = clearBigSqLResultSetMapMs;\n\t}\n\n\tpublic int getBufferPoolPageSize() {\n\t\treturn bufferPoolPageSize;\n\t}\n\n\tpublic void setBufferPoolPageSize(int bufferPoolPageSize) {\n\t\tthis.bufferPoolPageSize = bufferPoolPageSize;\n\t}\n\n\tpublic short getBufferPoolPageNumber() {\n\t\treturn bufferPoolPageNumber;\n\t}\n\n\tpublic void setBufferPoolPageNumber(short bufferPoolPageNumber) {\n\t\tthis.bufferPoolPageNumber = bufferPoolPageNumber;\n\t}\n\n\tpublic int getFrontSocketSoRcvbuf() {\n\t\treturn frontSocketSoRcvbuf;\n\t}\n\n\tpublic void setFrontSocketSoRcvbuf(int frontSocketSoRcvbuf) {\n\t\tthis.frontSocketSoRcvbuf = frontSocketSoRcvbuf;\n\t}\n\n\tpublic int getFrontSocketSoSndbuf() {\n\t\treturn frontSocketSoSndbuf;\n\t}\n\n\tpublic void setFrontSocketSoSndbuf(int frontSocketSoSndbuf) {\n\t\tthis.frontSocketSoSndbuf = frontSocketSoSndbuf;\n\t}\n\n\tpublic int getBackSocketSoRcvbuf() {\n\t\treturn backSocketSoRcvbuf;\n\t}\n\n\tpublic void setBackSocketSoRcvbuf(int backSocketSoRcvbuf) {\n\t\tthis.backSocketSoRcvbuf = backSocketSoRcvbuf;\n\t}\n\n\tpublic int getBackSocketSoSndbuf() {\n\t\treturn backSocketSoSndbuf;\n\t}\n\n\tpublic void setBackSocketSoSndbuf(int backSocketSoSndbuf) {\n\t\tthis.backSocketSoSndbuf = backSocketSoSndbuf;\n\t}\n\n\tpublic int getFrontSocketNoDelay() {\n\t\treturn frontSocketNoDelay;\n\t}\n\n\tpublic void setFrontSocketNoDelay(int frontSocketNoDelay) {\n\t\tthis.frontSocketNoDelay = frontSocketNoDelay;\n\t}\n\n\tpublic int getBackSocketNoDelay() {\n\t\treturn backSocketNoDelay;\n\t}\n\n\tpublic void setBackSocketNoDelay(int backSocketNoDelay) {\n\t\tthis.backSocketNoDelay = backSocketNoDelay;\n\t}\n\n\tpublic int getMaxStringLiteralLength() {\n\t\treturn maxStringLiteralLength;\n\t}\n\n\tpublic void setMaxStringLiteralLength(int maxStringLiteralLength) {\n\t\tthis.maxStringLiteralLength = maxStringLiteralLength;\n\t}\n\n\tpublic int getMutiNodeLimitType() {\n\t\treturn mutiNodeLimitType;\n\t}\n\n\tpublic void setMutiNodeLimitType(int mutiNodeLimitType) {\n\t\tthis.mutiNodeLimitType = mutiNodeLimitType;\n\t}\n\n\tpublic int getMutiNodePatchSize() {\n\t\treturn mutiNodePatchSize;\n\t}\n\n\tpublic void setMutiNodePatchSize(int mutiNodePatchSize) {\n\t\tthis.mutiNodePatchSize = mutiNodePatchSize;\n\t}\n\n\tpublic int getProcessorBufferLocalPercent() {\n\t\treturn processorBufferLocalPercent;\n\t}\n\n\tpublic void setProcessorBufferLocalPercent(int processorBufferLocalPercent) {\n\t\tthis.processorBufferLocalPercent = processorBufferLocalPercent;\n\t}\n\n\tpublic String getSqlInterceptorType() {\n\t\treturn sqlInterceptorType;\n\t}\n\n\tpublic void setSqlInterceptorType(String sqlInterceptorType) {\n\t\tthis.sqlInterceptorType = sqlInterceptorType;\n\t}\n\n\tpublic String getSqlInterceptorFile() {\n\t\treturn sqlInterceptorFile;\n\t}\n\n\tpublic void setSqlInterceptorFile(String sqlInterceptorFile) {\n\t\tthis.sqlInterceptorFile = sqlInterceptorFile;\n\t}\n\n\tpublic int getUsingAIO() {\n\t\treturn usingAIO;\n\t}\n\n\tpublic void setUsingAIO(int usingAIO) {\n\t\tthis.usingAIO = usingAIO;\n\t}\n\n\tpublic int getMycatNodeId() {\n\t\treturn mycatNodeId;\n\t}\n\n\tpublic void setMycatNodeId(int mycatNodeId) {\n\t\tthis.mycatNodeId = mycatNodeId;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"SystemConfig [processorBufferLocalPercent=\"\n\t\t\t\t+ processorBufferLocalPercent + \", frontSocketSoRcvbuf=\"\n\t\t\t\t+ frontSocketSoRcvbuf + \", frontSocketSoSndbuf=\"\n\t\t\t\t+ frontSocketSoSndbuf + \", backSocketSoRcvbuf=\"\n\t\t\t\t+ backSocketSoRcvbuf + \", backSocketSoSndbuf=\"\n\t\t\t\t+ backSocketSoSndbuf + \", frontSocketNoDelay=\"\n\t\t\t\t+ frontSocketNoDelay + \", backSocketNoDelay=\"\n\t\t\t\t+ backSocketNoDelay + \", maxStringLiteralLength=\"\n\t\t\t\t+ maxStringLiteralLength + \", frontWriteQueueSize=\"\n\t\t\t\t+ frontWriteQueueSize + \", bindIp=\" + bindIp + \", serverPort=\"\n\t\t\t\t+ serverPort + \", managerPort=\" + managerPort + \", charset=\"\n\t\t\t\t+ charset + \", processors=\" + processors\n\t\t\t\t+ \", processorExecutor=\" + processorExecutor\n\t\t\t\t+ \", timerExecutor=\" + timerExecutor\n\t\t\t\t+ \", managerExecutor=\" + managerExecutor\n\t\t\t\t+ \", serverBacklog=\" + serverBacklog\n\t\t\t\t+ \", idleTimeout=\" + idleTimeout\n        + \", authTimeout=\" + authTimeout\n\t\t\t\t+ \", catletClassCheckSeconds=\" + catletClassCheckSeconds\n\t\t\t\t+ \", sqlExecuteTimeout=\" + sqlExecuteTimeout\n\t\t\t\t+ \", processorCheckPeriod=\" + processorCheckPeriod\n\t\t\t\t+ \", dataNodeIdleCheckPeriod=\" + dataNodeIdleCheckPeriod\n\t\t\t\t+ \", dataNodeHeartbeatPeriod=\" + dataNodeHeartbeatPeriod\n\t\t\t\t+ \", clusterHeartbeatUser=\" + clusterHeartbeatUser\n\t\t\t\t+ \", clusterHeartbeatPass=\" + clusterHeartbeatPass\n\t\t\t\t+ \", clusterHeartbeatPeriod=\" + clusterHeartbeatPeriod\n\t\t\t\t+ \", clusterHeartbeatTimeout=\" + clusterHeartbeatTimeout\n\t\t\t\t+ \", clusterHeartbeatRetry=\" + clusterHeartbeatRetry\n\t\t\t\t+ \", txIsolation=\" + txIsolation + \", parserCommentVersion=\"\n\t\t\t\t+ parserCommentVersion + \", sqlRecordCount=\" + sqlRecordCount\n\t\t\t\t+ \", bufferPoolPageSize=\" + bufferPoolPageSize\n\t\t\t\t+ \", bufferPoolChunkSize=\" + bufferPoolChunkSize\n\t\t\t\t+ \", bufferPoolPageNumber=\" + bufferPoolPageNumber\n\t\t\t\t+ \", maxResultSet=\" +maxResultSet\n\t\t\t\t+ \", bigResultSizeSqlCount=\"+bigResultSizeSqlCount\n\t\t\t\t+ \", bufferUsagePercent=\"+bufferUsagePercent\n\t\t\t\t+ \", flowControlRejectStrategy=\"+flowControlRejectStrategy\n\t\t\t\t+ \", clearBigSqLResultSetMapMs=\"+clearBigSqLResultSetMapMs\n\t\t\t\t+ \", defaultMaxLimit=\" + defaultMaxLimit\n\t\t\t\t+ \", sequenceHandlerType=\" + sequenceHandlerType\n\t\t\t\t+ \", sqlInterceptor=\" + sqlInterceptor\n\t\t\t\t+ \", sqlInterceptorType=\" + sqlInterceptorType\n\t\t\t\t+ \", sqlInterceptorFile=\" + sqlInterceptorFile\n\t\t\t\t+ \", mutiNodeLimitType=\" + mutiNodeLimitType \n\t\t\t\t+ \", mutiNodePatchSize=\" + mutiNodePatchSize \n\t\t\t\t+ \", defaultSqlParser=\" + defaultSqlParser\n\t\t\t\t+ \", usingAIO=\" + usingAIO \n\t\t\t\t+ \", packetHeaderSize=\" + packetHeaderSize \n\t\t\t\t+ \", maxPacketSize=\" + maxPacketSize\n\t\t\t\t+ \", mycatNodeId=\" + mycatNodeId\n\t\t\t\t+ \",ignoreUnknownCommand=\"+ignoreUnknownCommand\n\t\t\t\t+ \",parallExecute=\"+parallExecute\n\t\t\t\t+ \",removeGraveAccent=\"+ removeGraveAccent\n\t\t\t\t+ \"]\";\n\t}\n\n\n\tpublic int getCheckTableConsistency() {\n\t\treturn checkTableConsistency;\n\t}\n\n\tpublic void setCheckTableConsistency(int checkTableConsistency) {\n\t\tthis.checkTableConsistency = checkTableConsistency;\n\t}\n\n\tpublic long getCheckTableConsistencyPeriod() {\n\t\treturn checkTableConsistencyPeriod;\n\t}\n\n\tpublic void setCheckTableConsistencyPeriod(long checkTableConsistencyPeriod) {\n\t\tthis.checkTableConsistencyPeriod = checkTableConsistencyPeriod;\n\t}\n\n\tpublic int getProcessorBufferPoolType() {\n\t\treturn processorBufferPoolType;\n\t}\n\n\tpublic void setProcessorBufferPoolType(int processorBufferPoolType) {\n\t\tthis.processorBufferPoolType = processorBufferPoolType;\n\t}\n\n\tpublic int getHandleDistributedTransactions() {\n\t\treturn handleDistributedTransactions;\n\t}\n\n\tpublic void setHandleDistributedTransactions(int handleDistributedTransactions) {\n\t\tthis.handleDistributedTransactions = handleDistributedTransactions;\n\t}\n\n\tpublic int getUseHandshakeV10() {\n\t\treturn useHandshakeV10;\n\t}\n\n\tpublic void setUseHandshakeV10(int useHandshakeV10) {\n\t\tthis.useHandshakeV10 = useHandshakeV10;\n\t}\n\n\tpublic int getNonePasswordLogin() {\n\t\treturn nonePasswordLogin;\n\t}\n\n\tpublic void setNonePasswordLogin(int nonePasswordLogin) {\n\t\tthis.nonePasswordLogin = nonePasswordLogin;\n\t}\n\n\tpublic boolean isSubqueryRelationshipCheck() {\n\t\treturn subqueryRelationshipCheck;\n\t}\n\n\tpublic void setSubqueryRelationshipCheck(boolean subqueryRelationshipCheck) {\n\t\tthis.subqueryRelationshipCheck = subqueryRelationshipCheck;\n\t}\n\n\tpublic boolean isStrictTxIsolation() {\n\t\treturn strictTxIsolation;\n\t}\n\n\tpublic void setStrictTxIsolation(boolean strictTxIsolation) {\n\t\tthis.strictTxIsolation = strictTxIsolation;\n\t}\n\t\n\tpublic int getSequnceMySqlRetryCount() {\n\t\treturn sequnceMySqlRetryCount;\n\t}\n\n\tpublic void setSequnceMySqlRetryCount(int sequnceMySqlRetryCount) {\n\t\tthis.sequnceMySqlRetryCount = sequnceMySqlRetryCount;\n\t}\n\n\tpublic long getSequnceMySqlWaitTime() {\n\t\treturn sequnceMySqlWaitTime;\n\t}\n\n\tpublic void setSequnceMySqlWaitTime(long sequnceMySqlWaitTime) {\n\t\tthis.sequnceMySqlWaitTime = sequnceMySqlWaitTime;\n\t}\n\n\tpublic String getSequnceHandlerPattern() {\n\t\treturn sequnceHandlerPattern==null?SEQUENCEHANDLER_PATTERN:sequnceHandlerPattern;\n\t}\n\tpublic void setSequnceHandlerPattern(String sequnceHandlerPattern) {\n\t\tthis.sequnceHandlerPattern = sequnceHandlerPattern;\n\t}\n\n\tpublic  String getSequenceHanlderClass() {\n\t\treturn sequenceHanlderClass;\n\t}\n\tpublic void setSequenceHanlderClass(String value) {\n\t\t sequenceHanlderClass = value;\n\t}\n\n\tpublic int getIgnoreUnknownCommand() {\n\t\treturn ignoreUnknownCommand;\n\t}\n\n\tpublic void setIgnoreUnknownCommand(int ignoreUnknownCommand) {\n\t\tthis.ignoreUnknownCommand = ignoreUnknownCommand;\n\t}\n\n\tpublic int getParallExecute() {\n\t\treturn parallExecute;\n\t}\n\n\tpublic void setParallExecute(int parallExecute) {\n\t\tthis.parallExecute = parallExecute;\n\t}\n\n\tpublic int getRemoveGraveAccent() {\n\t\treturn removeGraveAccent;\n\t}\n\tpublic void setRemoveGraveAccent(int removeGraveAccent) {\n\t\tthis.removeGraveAccent = removeGraveAccent;\n\t}\n\n    public boolean isEnableWriteQueueFlowControl() {\n        return enableWriteQueueFlowControl;\n    }\n\n    public void setEnableWriteQueueFlowControl(boolean enableWriteQueueFlowControl) {\n        this.enableWriteQueueFlowControl = enableWriteQueueFlowControl;\n    }\n\n    public int getWriteQueueStopThreshold() {\n        return writeQueueStopThreshold;\n    }\n\n    public void setWriteQueueStopThreshold(int writeQueueStopThreshold) {\n        this.writeQueueStopThreshold = writeQueueStopThreshold;\n    }\n\n    public int getWriteQueueRecoverThreshold() {\n        return writeQueueRecoverThreshold;\n    }\n\n    public void setWriteQueueRecoverThreshold(int writeQueueRecoverThreshold) {\n        this.writeQueueRecoverThreshold = writeQueueRecoverThreshold;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/model/TableConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model;\n\nimport com.alibaba.druid.sql.ast.statement.SQLTableElement;\nimport io.mycat.config.model.rule.RuleConfig;\nimport io.mycat.util.SplitUtil;\n\nimport java.util.*;\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\n/**\n * @author mycat\n */\npublic class TableConfig {\n    public static final int TYPE_GLOBAL_TABLE = 1;\n    public static final int TYPE_GLOBAL_DEFAULT = 0;\n    private final String name;\n    private final String primaryKey;\n    private final boolean autoIncrement;\n    private final boolean fetchStoreNodeByJdbc;\n    private final boolean needAddLimit;\n    private final Set<String> dbTypes;\n    private final int tableType;\n    private final ArrayList<String> dataNodes;\n    private final ArrayList<String> distTables;\n    private final RuleConfig rule;\n    private final String partitionColumn;\n    private final boolean ruleRequired;\n    private final TableConfig parentTC;\n    private final boolean childTable;\n    private final String joinKey;\n    private final String parentKey;\n    private final String locateRTableKeySql;\n    // only has one level of parent\n    private final boolean secondLevel;\n    private final boolean partionKeyIsPrimaryKey;\n\n    private volatile List<SQLTableElement> tableElementList;\n    private volatile String tableStructureSQL;\n    private volatile Map<String, List<String>> dataNodeTableStructureSQLMap;\n    private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(false);\n\n    public TableConfig(String tableName, String primaryKey, boolean autoIncrement, boolean needAddLimit, int tableType,\n                       String dataNode, Set<String> dbType, RuleConfig rule, boolean ruleRequired,\n                       TableConfig parentTC, boolean isChildTable, String joinKey,\n                       String parentKey, String subTables, boolean fetchStoreNodeByJdbc) {\n        if (tableName == null) {\n            throw new IllegalArgumentException(\"table name is null\");\n        } else if (dataNode == null) {\n            throw new IllegalArgumentException(\"dataNode name is null\");\n        }\n        this.primaryKey = primaryKey;\n        this.autoIncrement = autoIncrement;\n        this.needAddLimit = needAddLimit;\n        this.fetchStoreNodeByJdbc = fetchStoreNodeByJdbc;\n        this.tableType = tableType;\n        this.dbTypes = dbType;\n        if (ruleRequired && rule == null) {\n            throw new IllegalArgumentException(\"ruleRequired but rule is null\");\n        }\n\n        this.name = tableName.toUpperCase();\n        String theDataNodes[] = SplitUtil.split(dataNode, ',', '$', '-');\n        if (theDataNodes == null || theDataNodes.length <= 0) {\n            throw new IllegalArgumentException(\"invalid table dataNodes: \" + dataNode);\n        }\n        dataNodes = new ArrayList<String>(theDataNodes.length);\n        for (String dn : theDataNodes) {\n            dataNodes.add(dn);\n        }\n\n        if (subTables != null && !subTables.equals(\"\")) {\n            String sTables[] = SplitUtil.split(subTables, ',', '$', '-');\n            if (sTables == null || sTables.length <= 0) {\n                throw new IllegalArgumentException(\"invalid table subTables\");\n            }\n            this.distTables = new ArrayList<String>(sTables.length);\n            for (String table : sTables) {\n                distTables.add(table);\n            }\n        } else {\n            this.distTables = new ArrayList<String>();\n        }\n\n        this.rule = rule;\n        this.partitionColumn = (rule == null) ? null : rule.getColumn();\n        partionKeyIsPrimaryKey = (partitionColumn == null) ? primaryKey == null : partitionColumn.equals(primaryKey);\n        this.ruleRequired = ruleRequired;\n        this.childTable = isChildTable;\n        this.parentTC = parentTC;\n        this.joinKey = joinKey;\n        this.parentKey = parentKey;\n        if (parentTC != null) {\n            locateRTableKeySql = genLocateRootParentSQL();\n            secondLevel = (parentTC.parentTC == null);\n        } else {\n            locateRTableKeySql = null;\n            secondLevel = false;\n        }\n    }\n\n    public String getPrimaryKey() {\n        return primaryKey;\n    }\n\n    public Set<String> getDbTypes() {\n        return dbTypes;\n    }\n\n    public boolean isAutoIncrement() {\n        return autoIncrement;\n    }\n\n    public boolean isNeedAddLimit() {\n        return needAddLimit;\n    }\n\n    public boolean isSecondLevel() {\n        return secondLevel;\n    }\n\n    public String getLocateRTableKeySql() {\n        return locateRTableKeySql;\n    }\n\n    public boolean isGlobalTable() {\n        return this.tableType == TableConfig.TYPE_GLOBAL_TABLE;\n    }\n\n    public String genLocateRootParentSQL() {\n        TableConfig tb = this;\n        StringBuilder tableSb = new StringBuilder();\n        StringBuilder condition = new StringBuilder();\n        TableConfig prevTC = null;\n        String ancestorTableName = null;\n        int level = 0;\n        String latestCond = null;\n        while (tb.parentTC != null) {\n            ancestorTableName = tb.parentTC.name;\n            String currentTableName = tb.name;\n            if (!ancestorTableName.contains(\"`\")){\n                ancestorTableName = \"`\"+ancestorTableName+\"`\";\n            }\n            if (!currentTableName.contains(\"`\")){\n                currentTableName = \"`\"+currentTableName+\"`\";\n            }\n            tableSb.append(ancestorTableName).append(',');\n            String relation = null;\n            if (level == 0) {\n                latestCond = \" \" + ancestorTableName + '.' + tb.parentKey\n                        + \"=\";\n            } else {\n                relation = ancestorTableName + '.' + tb.parentKey + '='\n                        + currentTableName + '.' + tb.joinKey;\n                condition.append(relation).append(\" AND \");\n            }\n            level++;\n            prevTC = tb;\n            tb = tb.parentTC;\n        }\n        String sql = \"SELECT \"\n                + ancestorTableName\n                + '.'\n                + prevTC.parentKey\n                + \" FROM \"\n                + tableSb.substring(0, tableSb.length() - 1)\n                + \" WHERE \"\n                + ((level < 2) ? latestCond : condition.toString() + latestCond);\n        // System.out.println(this.name+\" sql \" + sql);\n        return sql;\n\n    }\n\n    public String getPartitionColumn() {\n        return partitionColumn;\n    }\n\n    public int getTableType() {\n        return tableType;\n    }\n\n    /**\n     * get root parent\n     *\n     * @return\n     */\n    public TableConfig getRootParent() {\n        if (parentTC == null) {\n            return null;\n        }\n        TableConfig preParent = parentTC;\n        TableConfig parent = preParent.getParentTC();\n\n        while (parent != null) {\n            preParent = parent;\n            parent = parent.getParentTC();\n        }\n        return preParent;\n    }\n\n    public TableConfig getParentTC() {\n        return parentTC;\n    }\n\n    public boolean isChildTable() {\n        return childTable;\n    }\n\n    public String getJoinKey() {\n        return joinKey;\n    }\n\n    public String getParentKey() {\n        return parentKey;\n    }\n\n    /**\n     * @return upper-case\n     */\n    public String getName() {\n        return name;\n    }\n\n    public ArrayList<String> getDataNodes() {\n        return dataNodes;\n    }\n\n    public String getRandomDataNode() {\n        int index = Math.abs(ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE)) % dataNodes.size();\n        return dataNodes.get(index);\n    }\n\n    public boolean isRuleRequired() {\n        return ruleRequired;\n    }\n\n    public RuleConfig getRule() {\n        return rule;\n    }\n\n    public boolean primaryKeyIsPartionKey() {\n        return partionKeyIsPrimaryKey;\n    }\n\n    public ArrayList<String> getDistTables() {\n        return this.distTables;\n    }\n\n    public boolean isDistTable() {\n        if (this.distTables != null && !this.distTables.isEmpty()) {\n            return true;\n        }\n        return false;\n    }\n\n    public List<SQLTableElement> getTableElementList() {\n        return tableElementList;\n    }\n\n    public void setTableElementList(List<SQLTableElement> tableElementList) {\n        this.tableElementList = tableElementList;\n    }\n\n    public ReentrantReadWriteLock getReentrantReadWriteLock() {\n        return reentrantReadWriteLock;\n    }\n\n    public void setReentrantReadWriteLock(ReentrantReadWriteLock reentrantReadWriteLock) {\n        this.reentrantReadWriteLock = reentrantReadWriteLock;\n    }\n\n    public String getTableStructureSQL() {\n        return tableStructureSQL;\n    }\n\n    public void setTableStructureSQL(String tableStructureSQL) {\n        this.tableStructureSQL = tableStructureSQL;\n    }\n\n    public Map<String, List<String>> getDataNodeTableStructureSQLMap() {\n        return dataNodeTableStructureSQLMap;\n    }\n\n    public void setDataNodeTableStructureSQLMap(Map<String, List<String>> dataNodeTableStructureSQLMap) {\n        this.dataNodeTableStructureSQLMap = dataNodeTableStructureSQLMap;\n    }\n\n    public boolean getFetchStoreNodeByJdbc() {\n        return this.fetchStoreNodeByJdbc;\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/model/TableConfigMap.java",
    "content": "package io.mycat.config.model;\r\n\r\nimport java.util.HashMap;\r\n\r\n/**\r\n * 支持表名中包含引号[`] \r\n * \r\n * @author BEN GONG\r\n */\r\npublic class TableConfigMap extends HashMap<String, TableConfig> {\r\n\r\n\tprivate static final long serialVersionUID = -6605226933829917213L;\r\n\r\n\t@Override\r\n\tpublic TableConfig get(Object key) {\r\n\t\tString tableName = key.toString();\r\n\t\t// 忽略表名中的引号。\r\n\t\tif(tableName.contains(\"`\")) {\r\n\t\t\ttableName = tableName.replaceAll(\"`\", \"\");\r\n\t\t}\r\n\t\t\r\n\t\treturn super.get(tableName);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean containsKey(Object key) {\r\n\t\tString tableName = key.toString();\r\n\t\t// 忽略表名中的引号。\r\n\t\tif(tableName.contains(\"`\")) {\r\n\t\t\ttableName = tableName.replaceAll(\"`\", \"\");\r\n\t\t}\r\n\t\t\r\n\t\treturn super.containsKey(tableName);\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/config/model/TableRuleConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model;\n\nimport java.beans.Expression;\n\n/**\n * @author mycat\n */\npublic final class TableRuleConfig {\n\n    private final String name;\n    private final RuleConfig[] rules;\n\n    public TableRuleConfig(String name, RuleConfig[] rules) {\n        this.name = name;\n        this.rules = rules;\n        if (rules != null) {\n            for (RuleConfig r : rules) {\n                r.tableRuleName = name;\n            }\n        }\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public RuleConfig[] getRules() {\n        return rules;\n    }\n\n    public static final class RuleConfig {\n        private String tableRuleName;\n        /** upper-case */\n        private final String[] columns;\n        private final Expression algorithm;\n\n        public RuleConfig(String[] columns, Expression algorithm) {\n            this.columns = columns == null ? new String[0] : columns;\n            this.algorithm = algorithm;\n        }\n\n        public String[] getColumns() {\n            return columns;\n        }\n\n        public Expression getAlgorithm() {\n            return algorithm;\n        }\n\n        @Override\n        public String toString() {\n            StringBuilder s = new StringBuilder();\n            s.append(\"{tableRule:\").append(tableRuleName).append(\", columns:[\");\n            for (int i = 0; i < columns.length; ++i) {\n                if (i > 0) {\n                    s.append(\", \");\n                }\n                s.append(columns[i]);\n            }\n            s.append(\"]}\");\n            return s.toString();\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/model/UserConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model;\n\nimport java.util.Set;\n\n/**\n * @author mycat\n */\npublic class UserConfig {\n\n    private String name;\n    private String password;\t\t\t\t\t\t//明文\n    private String encryptPassword; \t\t\t\t//密文\n    private int benchmark = 0;\t\t\t\t\t\t// 负载限制, 默认0表示不限制\n    private UserPrivilegesConfig privilegesConfig;\t//SQL表级的增删改查权限控制\n\tprivate String defaultSchema;\n\t/**\n     * 是否无密码登陆的默认账户\n     */\n    private boolean defaultAccount = false;\n    private boolean readOnly = false;\n    \n    public boolean isReadOnly() {\n\t\treturn readOnly;\n\t}\n\n\tpublic void setReadOnly(boolean readOnly) {\n\t\tthis.readOnly = readOnly;\n\t}\n\n\tprivate Set<String> schemas;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n\tpublic int getBenchmark() {\n\t\treturn benchmark;\n\t}\n\n\tpublic void setBenchmark(int benchmark) {\n\t\tthis.benchmark = benchmark;\n\t}\n\n\tpublic Set<String> getSchemas() {\n        return schemas;\n    }\n\n\tpublic String getEncryptPassword() {\n\t\treturn this.encryptPassword;\n\t}\n\n\tpublic void setEncryptPassword(String encryptPassword) {\n\t\tthis.encryptPassword = encryptPassword;\n\t}\n\n\tpublic void setSchemas(Set<String> schemas) {\n        this.schemas = schemas;\n    }\n\t\n\tpublic UserPrivilegesConfig getPrivilegesConfig() {\n\t\treturn privilegesConfig;\n\t}\n\t\n\tpublic void setPrivilegesConfig(UserPrivilegesConfig privilegesConfig) {\n\t\tthis.privilegesConfig = privilegesConfig;\n\t}\n\n\t\n\tpublic boolean isDefaultAccount() {\n\t\treturn defaultAccount;\n\t}\n\n\tpublic void setDefaultAccount(boolean defaultAccount) {\n\t\tthis.defaultAccount = defaultAccount;\n\t}\n\n\tpublic String getDefaultSchema() {\n\t\treturn defaultSchema;\n\t}\n\n\tpublic void setDefaultSchema(String defaultSchema) {\n\t\tthis.defaultSchema = defaultSchema;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"UserConfig [name=\" + name + \", password=\" + password + \", encryptPassword=\" + encryptPassword\n\t\t\t\t+ \", benchmark=\" + benchmark + \", privilegesConfig=\" + privilegesConfig + \", defaultAccount=\"\n\t\t\t\t+ defaultAccount + \", readOnly=\" + readOnly + \", schemas=\" + schemas +\", defaultSchema=\" + defaultSchema + \"]\";\n\t}\n\n\t \n\t\n\t\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/model/UserPrivilegesConfig.java",
    "content": "package io.mycat.config.model;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n\n/**\n * 用户 SQL 权限配置\n *\n * @author zhuam\n *\n */\npublic class UserPrivilegesConfig {\n\t\n\tprivate boolean check = false;\n\n\t/** 库级权限 */\n\tprivate Map<String, SchemaPrivilege> schemaPrivileges = new HashMap<String, SchemaPrivilege>();\n\n\t/** dataNode权限 */\n\tprivate Map<String, DataNodePrivilege> dataNodePrivileges = new HashMap<String, DataNodePrivilege>();\n\n\tpublic boolean isCheck() {\n\t\treturn check;\n\t}\n\n\tpublic void setCheck(boolean check) {\n\t\tthis.check = check;\n\t}\n\n\tpublic void addSchemaPrivilege(String schemaName, SchemaPrivilege privilege) {\n\t\tthis.schemaPrivileges.put(schemaName, privilege);\n\t}\n\t\n\tpublic SchemaPrivilege getSchemaPrivilege(String schemaName) {\n\t\treturn schemaPrivileges.get( schemaName );\n\t}\n\n\tpublic void addDataNodePrivileges(String dataNodeName, DataNodePrivilege privilege) {\n\t\tthis.dataNodePrivileges.put(dataNodeName, privilege);\n\t}\n\n\tpublic DataNodePrivilege getDataNodePrivilege(String dataNodeName) {\n\t\treturn dataNodePrivileges.get(dataNodeName);\n\t}\n\n\t/**\n\t * 库级权限\n\t */\n\tpublic static class SchemaPrivilege {\n\t\t\n\t\tprivate String name;\n\t\tprivate int[] dml = new int[]{0, 0, 0, 0};\n\t\t\n\t\tprivate Map<String, TablePrivilege> tablePrivileges = new HashMap<String, TablePrivilege>();\n\t\t\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\t\t\n\t\tpublic void setName(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\t\t\n\t\tpublic int[] getDml() {\n\t\t\treturn dml;\n\t\t}\n\t\t\n\t\tpublic void setDml(int[] dml) {\n\t\t\tthis.dml = dml;\n\t\t}\n\t\t\n\t\tpublic void addTablePrivilege(String tableName, TablePrivilege privilege) {\n\t\t\tthis.tablePrivileges.put(tableName, privilege);\n\t\t}\n\t\t\n\t\tpublic TablePrivilege getTablePrivilege(String tableName) {\n\t\t\tTablePrivilege tablePrivilege = tablePrivileges.get( tableName );\n\t\t\tif ( tablePrivilege == null ) {\n\t\t\t\ttablePrivilege = new TablePrivilege();\n\t\t\t\ttablePrivilege.setName(tableName);\n\t\t\t\ttablePrivilege.setDml(dml);\n\t\t\t}\n\t\t\treturn tablePrivilege;\n\t\t}\n\t}\n\t\n\t/**\n\t * 表级权限\n\t */\n\tpublic static class TablePrivilege {\n\n\t\tprivate String name;\n\t\tprivate int[] dml = new int[] { 0, 0, 0, 0 };\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic void setName(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tpublic int[] getDml() {\n\t\t\treturn dml;\n\t\t}\n\n\t\tpublic void setDml(int[] dml) {\n\t\t\tthis.dml = dml;\n\t\t}\n\n\t}\n\n\t/**\n\t * dataNode权限\n\t */\n\tpublic static class DataNodePrivilege {\n\n\t\tprivate String name;\n\t\tprivate int[] dml = new int[] { 0, 0, 0, 0 };\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic void setName(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tpublic int[] getDml() {\n\t\t\treturn dml;\n\t\t}\n\n\t\tpublic void setDml(int[] dml) {\n\t\t\tthis.dml = dml;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/model/rule/RuleAlgorithm.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model.rule;\n\n/**\n * @author mycat\n */\npublic interface RuleAlgorithm {\n\n\t/**\n\t * init\n\t * \n\t * @param\n\t */\n\tvoid init();\n\n\t/**\n\t * \n\t * return sharding nodes's id\n\t * columnValue is column's value\n\t * @return never null\n\t */\n\tInteger calculate(String columnValue) ;\n\t\n\tInteger[] calculateRange(String beginValue,String endValue) ;\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/model/rule/RuleConfig.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.config.model.rule;\r\n\r\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\r\n\r\nimport java.io.Serializable;\r\n\r\n/**\r\n * 分片规则，column是用于分片的数据库物理字段\r\n * @author mycat\r\n */\r\npublic class RuleConfig implements Serializable {\r\n\tprivate final String column;\r\n\tprivate final String functionName;\r\n\tprivate AbstractPartitionAlgorithm ruleAlgorithm;\r\n\r\n\tpublic RuleConfig(String column, String functionName) {\r\n\t\tif (functionName == null) {\r\n\t\t\tthrow new IllegalArgumentException(\"functionName is null\");\r\n\t\t}\r\n\t\tthis.functionName = functionName;\r\n\t\tif (column == null || column.length() <= 0) {\r\n\t\t\tthrow new IllegalArgumentException(\"no rule column is found\");\r\n\t\t}\r\n\t\tthis.column = column;\r\n\t}\r\n\r\n\t\r\n\r\n\tpublic AbstractPartitionAlgorithm getRuleAlgorithm() {\r\n\t\treturn ruleAlgorithm;\r\n\t}\r\n\r\n\r\n\r\n\tpublic void setRuleAlgorithm(AbstractPartitionAlgorithm ruleAlgorithm) {\r\n\t\tthis.ruleAlgorithm = ruleAlgorithm;\r\n\t}\r\n\r\n\r\n\r\n\t/**\r\n\t * @return unmodifiable, upper-case\r\n\t */\r\n\tpublic String getColumn() {\r\n\t\treturn column;\r\n\t}\r\n\r\n\tpublic String getFunctionName() {\r\n\t\treturn functionName;\r\n\t}\r\n\r\n\r\n\t\r\n\t@Override\r\n\tpublic int hashCode() {\r\n\t\tfinal int prime = 31;\r\n\t\tint result = 1;\r\n\t\tresult = prime * result + ((column == null) ? 0 : column.hashCode());\r\n\t\tresult = prime * result + ((functionName == null) ? 0 : functionName.hashCode());\r\n\t\tresult = prime * result + ((ruleAlgorithm == null) ? 0 : ruleAlgorithm.hashCode());\r\n\t\treturn result;\r\n\t}\r\n\r\n\r\n\t//huangyiming add 判断分片规则是否相同,暂时根据这个去判断\r\n\t@Override\r\n\tpublic boolean equals(Object obj) {\r\n\t\tif (this == obj)\r\n\t\t\treturn true;\r\n\t\tif (obj == null)\r\n\t\t\treturn false;\r\n\t\tif (getClass() != obj.getClass())\r\n\t\t\treturn false;\r\n\t\tRuleConfig other = (RuleConfig) obj;\r\n\t\tif (column == null) {\r\n\t\t\tif (other.column != null)\r\n\t\t\t\treturn false;\r\n\t\t} else if (!column.equals(other.column))\r\n\t\t\treturn false;\r\n\t\tif (functionName == null) {\r\n\t\t\tif (other.functionName != null)\r\n\t\t\t\treturn false;\r\n\t\t} else if (!functionName.equals(other.functionName))\r\n\t\t\treturn false;\r\n\t\tif (ruleAlgorithm == null) {\r\n\t\t\tif (other.ruleAlgorithm != null)\r\n\t\t\t\treturn false;\r\n\t\t} else if (!ruleAlgorithm.equals(other.ruleAlgorithm))\r\n\t\t\treturn false;\r\n\t\treturn true;\r\n\t}\r\n\r\n\t\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/config/model/rule/TableRuleConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.model.rule;\n\nimport java.io.Serializable;\n\n/**\n * @author mycat\n */\npublic class TableRuleConfig implements Serializable {\n    private  String name;\n    private final RuleConfig rule;\n\n    public TableRuleConfig(String name, RuleConfig rule) {\n        if (name == null) {\n            throw new IllegalArgumentException(\"name is null\");\n        }\n        this.name = name;\n        if (rule == null) {\n            throw new IllegalArgumentException(\"no rule is found\");\n        }\n        this.rule =rule;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    /**\n     * @return unmodifiable\n     */\n    public RuleConfig getRule() {\n        return rule;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/table/structure/MySQLTableStructureDetector.java",
    "content": "package io.mycat.config.table.structure;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.sqlengine.OneRawSQLQueryResultHandler;\nimport io.mycat.sqlengine.SQLJob;\nimport io.mycat.sqlengine.SQLQueryResult;\nimport io.mycat.sqlengine.SQLQueryResultListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 表结构结果处理\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 00:09:03 2016/5/11\n */\npublic class MySQLTableStructureDetector implements Runnable {\n    private static final Logger LOGGER = LoggerFactory.getLogger(MySQLTableStructureDetector.class);\n    private static final String[] MYSQL_SHOW_CREATE_TABLE_COLMS = new String[]{\n            \"Table\",\n            \"Create Table\"};\n    private static final String sqlPrefix = \"show create table \";\n\n    @Override\n    public void run() {\n        for (SchemaConfig schema : MycatServer.getInstance().getConfig().getSchemas().values()) {\n            for (TableConfig table : schema.getTables().values()) {\n                for (String dataNode : table.getDataNodes()) {\n                    try {\n                        table.getReentrantReadWriteLock().writeLock().lock();\n                        ConcurrentHashMap<String, List<String>> map = new ConcurrentHashMap<>();\n                        table.setDataNodeTableStructureSQLMap(map);\n                    } finally {\n                        table.getReentrantReadWriteLock().writeLock().unlock();\n                    }\n                    OneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler(MYSQL_SHOW_CREATE_TABLE_COLMS, new MySQLTableStructureListener(dataNode, table));\n                    resultHandler.setMark(\"Table Structure\");\n                    PhysicalDBNode dn = MycatServer.getInstance().getConfig().getDataNodes().get(dataNode);\n                    SQLJob sqlJob = new SQLJob(sqlPrefix + table.getName(), dn.getDatabase(), resultHandler, dn.getDbPool().getSource());\n                    sqlJob.run();\n                }\n            }\n        }\n    }\n\n    private static class MySQLTableStructureListener implements SQLQueryResultListener<SQLQueryResult<Map<String, String>>> {\n        private String dataNode;\n        private TableConfig table;\n\n        public MySQLTableStructureListener(String dataNode, TableConfig table) {\n            this.dataNode = dataNode;\n            this.table = table;\n        }\n\n        /**\n         * @param result\n         * @// TODO: 2016/5/11 检查表元素，来确定是哪个元素不一致，未来还有其他用\n         */\n        @Override\n        public void onResult(SQLQueryResult<Map<String, String>> result) {\n            try {\n                table.getReentrantReadWriteLock().writeLock().lock();\n                if (!result.isSuccess()) {\n                    LOGGER.warn(\"Can't get table \" + table.getName() + \"'s config from DataNode:\" + dataNode + \"! Maybe the table is not initialized!\");\n                    return;\n                }\n                String currentSql = result.getResult().get(MYSQL_SHOW_CREATE_TABLE_COLMS[1]);\n                Map<String, List<String>> dataNodeTableStructureSQLMap = table.getDataNodeTableStructureSQLMap();\n                if (dataNodeTableStructureSQLMap.containsKey(currentSql)) {\n                    List<String> dataNodeList = dataNodeTableStructureSQLMap.get(currentSql);\n                    dataNodeList.add(dataNode);\n                } else {\n                    List<String> dataNodeList = new LinkedList<>();\n                    dataNodeList.add(dataNode);\n                    dataNodeTableStructureSQLMap.put(currentSql,dataNodeList);\n                }\n                if (dataNodeTableStructureSQLMap.size() > 1) {\n                    LOGGER.warn(\"Table [\" + table.getName() + \"] structure are not consistent!\");\n                    LOGGER.warn(\"Currently detected: \");\n                    for(String sql : dataNodeTableStructureSQLMap.keySet()){\n                        StringBuilder stringBuilder = new StringBuilder();\n                        for(String dn : dataNodeTableStructureSQLMap.get(sql)){\n                            stringBuilder.append(\"DataNode:[\").append(dn).append(\"]\");\n                        }\n                        stringBuilder.append(\":\").append(sql);\n                        LOGGER.warn(stringBuilder.toString());\n                    }\n                }\n            } finally {\n                table.getReentrantReadWriteLock().writeLock().unlock();\n            }\n        }\n    }\n\n//    public static void main(String[] args) {\n//        System.out.println(UUID.randomUUID());\n//    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/table/structure/TableStructureProcessor.java",
    "content": "package io.mycat.config.table.structure;\n\n/**\n * 将表结构持久化\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 00:09:03 2016/5/11\n */\npublic abstract class TableStructureProcessor {\n    public abstract void saveTableStructure();\n    public abstract void loadTableStructure();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/util/BeanConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.util;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport io.mycat.util.ObjectUtil;\n\n/**\n * @author mycat\n */\npublic class BeanConfig implements Cloneable {\n    private static final ReflectionProvider refProvider = new ReflectionProvider();\n\n    private String name;\n    private String className;\n    private Map<String, Object> params = new HashMap<String, Object>();\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getClassName() {\n        return className;\n    }\n\n    public void setClassName(String beanObject) {\n        this.className = beanObject;\n    }\n\n    public Map<String, Object> getParams() {\n        return params;\n    }\n\n    public void setParams(Map<String, Object> params) {\n        this.params = params;\n    }\n\n    public Object create(boolean initEarly) throws IllegalAccessException, InvocationTargetException {\n        Object obj = null;\n        try {\n            obj = refProvider.newInstance(Class.forName(className));\n        } catch (ClassNotFoundException e) {\n            throw new ConfigException(e);\n        }\n        ParameterMapping.mapping(obj, params);\n        if (initEarly && (obj instanceof Initializable)) {\n            ((Initializable) obj).init();\n        }\n        return obj;\n    }\n\n    @Override\n    public Object clone() {\n        try {\n            super.clone();\n        } catch (CloneNotSupportedException e) {\n            throw new ConfigException(e);\n        }\n        BeanConfig bc = null;\n        try {\n            bc = getClass().newInstance();\n        } catch (InstantiationException e) {\n            throw new ConfigException(e);\n        } catch (IllegalAccessException e) {\n            throw new ConfigException(e);\n        }\n//        if (bc == null) {\n//            return null;\n//        }\n        bc.className = className;\n        bc.name = name;\n//        Map<String, Object> params = new HashMap<String, Object>();\n//        params.putAll(params);\n        return bc;\n    }\n\n    @Override\n    public int hashCode() {\n        int hashcode = 37;\n        hashcode += (name == null ? 0 : name.hashCode());\n        hashcode += (className == null ? 0 : className.hashCode());\n        hashcode += (params == null ? 0 : params.hashCode());\n        return hashcode;\n    }\n\n    @Override\n    public boolean equals(Object object) {\n        if (object instanceof BeanConfig) {\n            BeanConfig entity = (BeanConfig) object;\n            boolean isEquals = equals(name, entity.name);\n            isEquals = isEquals && equals(className, entity.getClassName());\n            isEquals = isEquals && (ObjectUtil.equals(params, entity.params));\n            return isEquals;\n        }\n        return false;\n    }\n\n    private static boolean equals(String str1, String str2) {\n        if (str1 == null) {\n            return str2 == null;\n        }\n        return str1.equals(str2);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/util/ConfigException.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.util;\n\n/**\n * @author mycat\n */\npublic class ConfigException extends RuntimeException {\n    private static final long serialVersionUID = -180146385688342818L;\n\n    public ConfigException() {\n        super();\n    }\n\n    public ConfigException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public ConfigException(String message) {\n        super(message);\n    }\n\n    public ConfigException(Throwable cause) {\n        super(cause);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/util/ConfigUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.util;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport org.w3c.dom.Attr;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.NamedNodeMap;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\nimport org.xml.sax.EntityResolver;\nimport org.xml.sax.ErrorHandler;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\nimport org.xml.sax.SAXParseException;\n\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class ConfigUtil {\n\n    public static String filter(String text) {\n        return filter(text, System.getProperties());\n    }\n\n    public static String filter(String text, Properties properties) {\n        StringBuilder s = new StringBuilder();\n        int cur = 0;\n        int textLen = text.length();\n        int propStart = -1;\n        int propStop = -1;\n        String propName = null;\n        String propValue = null;\n        for (; cur < textLen; cur = propStop + 1) {\n            propStart = text.indexOf(\"${\", cur);\n            if (propStart < 0) {\n                break;\n            }\n            s.append(text.substring(cur, propStart));\n            propStop = text.indexOf(\"}\", propStart);\n            if (propStop < 0) {\n                throw new ConfigException(\"Unterminated property: \" + text.substring(propStart));\n            }\n            propName = text.substring(propStart + 2, propStop);\n            propValue = properties.getProperty(propName);\n            if (propValue == null) {\n                s.append(\"${\").append(propName).append('}');\n            } else {\n                s.append(propValue);\n            }\n        }\n        return s.append(text.substring(cur)).toString();\n    }\n\n    public static Document getDocument(final InputStream dtd, InputStream xml) throws ParserConfigurationException,\n            SAXException, IOException {\n        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n        factory.setValidating(true);\n        factory.setNamespaceAware(false);\n        DocumentBuilder builder = factory.newDocumentBuilder();\n        builder.setEntityResolver(new EntityResolver() {\n            @Override\n            public InputSource resolveEntity(String publicId, String systemId) {\n                return new InputSource(dtd);\n            }\n        });\n        builder.setErrorHandler(new ErrorHandler() {\n            @Override\n            public void warning(SAXParseException e) {\n            }\n\n            @Override\n            public void error(SAXParseException e) throws SAXException {\n                throw e;\n            }\n\n            @Override\n            public void fatalError(SAXParseException e) throws SAXException {\n                throw e;\n            }\n        });\n        return builder.parse(xml);\n    }\n\n    public static Map<String, Object> loadAttributes(Element e) {\n        Map<String, Object> map = new HashMap<String, Object>();\n        NamedNodeMap nm = e.getAttributes();\n        for (int j = 0; j < nm.getLength(); j++) {\n            Node n = nm.item(j);\n            if (n instanceof Attr) {\n                Attr attr = (Attr) n;\n                map.put(attr.getName(), attr.getNodeValue());\n            }\n        }\n        return map;\n    }\n\n    public static Element loadElement(Element parent, String tagName) {\n        NodeList nodeList = parent.getElementsByTagName(tagName);\n        if (nodeList.getLength() > 1) {\n            throw new ConfigException(tagName + \" elements length  over one!\");\n        }\n        if (nodeList.getLength() == 1) {\n            return (Element) nodeList.item(0);\n        } else {\n            return null;\n        }\n    }\n\n    /**\n     * 获取节点下所有property\n     * @param parent\n     * @return key-value property键值对\n     */\n    public static Map<String, Object> loadElements(Element parent) {\n        Map<String, Object> map = new HashMap<String, Object>();\n        NodeList children = parent.getChildNodes();\n        for (int i = 0; i < children.getLength(); i++) {\n            Node node = children.item(i);\n            if (node instanceof Element) {\n                Element e = (Element) node;\n                String name = e.getNodeName();\n                //获取property\n                if (\"property\".equals(name)) {\n                    String key = e.getAttribute(\"name\");\n                    NodeList nl = e.getElementsByTagName(\"bean\");\n                    if (nl.getLength() == 0) {\n                        String value = e.getTextContent();\n                        map.put(key, StringUtil.isEmpty(value) ? null : value.trim());\n                    } else {\n                        map.put(key, loadBean((Element) nl.item(0)));\n                    }\n                }\n            }\n        }\n        return map;\n    }\n\n    public static BeanConfig loadBean(Element parent, String tagName) {\n        NodeList nodeList = parent.getElementsByTagName(tagName);\n        if (nodeList.getLength() > 1) {\n            throw new ConfigException(tagName + \" elements length over one!\");\n        }\n        return loadBean((Element) nodeList.item(0));\n    }\n\n    public static BeanConfig loadBean(Element e) {\n        if (e == null) {\n            return null;\n        }\n        BeanConfig bean = new BeanConfig();\n        bean.setName(e.getAttribute(\"name\"));\n        Element element = loadElement(e, \"className\");\n        if (element != null) {\n            bean.setClassName(element.getTextContent());\n        } else {\n            bean.setClassName(e.getAttribute(\"class\"));\n        }\n        bean.setParams(loadElements(e));\n        return bean;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/util/DnPropertyUtil.java",
    "content": "package io.mycat.config.util;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.Properties;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.config.model.SystemConfig;\n\n/**\n * \n * @author yanglixue\n *\n */\npublic class DnPropertyUtil {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(\"DnPropertyUtil\");\n\t\n\t/**\n\t * 加载dnindex.properties属性文件\n\t * @return 属性文件\n\t */\n\tpublic static Properties loadDnIndexProps() {\n\t\tProperties prop = new Properties();\n\t\tFile file = new File(SystemConfig.getHomePath(), \"conf\"\n\t\t\t\t+ File.separator + \"dnindex.properties\");\n\t\tif (!file.exists()) {\n\t\t\treturn prop;\n\t\t}\n\t\tFileInputStream filein = null;\n\t\ttry {\n\t\t\tfilein = new FileInputStream(file);\n\t\t\tprop.load(filein);\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.warn(\"load DataNodeIndex err:\" + e);\n\t\t} finally {\n\t\t\tif (filein != null) {\n\t\t\t\ttry {\n\t\t\t\t\tfilein.close();\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn prop;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/config/util/FieldDictionary.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.util;\n\nimport java.lang.reflect.Field;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\n\n/**\n * @author mycat\n */\npublic class FieldDictionary {\n\n    private final Map<String, Map<String, Field>> nameCache = Collections\n            .synchronizedMap(new HashMap<String, Map<String, Field>>());\n    private final Map<String, Map<FieldKey, Field>> keyCache = Collections\n            .synchronizedMap(new HashMap<String, Map<FieldKey, Field>>());\n\n    /**\n     * Returns an iterator for all serializable fields for some class\n     * \n     * @param cls\n     *            the class you are interested on\n     * @return an iterator for its serializable fields\n     */\n    public Iterator<Field> serializableFieldsFor(Class<?> cls) {\n        return buildMap(cls, true).values().iterator();\n    }\n\n    /**\n     * Returns an specific field of some class. If definedIn is null, it searchs\n     * for the field named 'name' inside the class cls. If definedIn is\n     * different than null, tries to find the specified field name in the\n     * specified class cls which should be defined in class definedIn (either\n     * equals cls or a one of it's superclasses)\n     * \n     * @param cls\n     *            the class where the field is to be searched\n     * @param name\n     *            the field name\n     * @param definedIn\n     *            the superclass (or the class itself) of cls where the field\n     *            was defined\n     * @return the field itself\n     */\n    public Field field(Class<?> cls, String name, Class<?> definedIn) {\n        Map<?, Field> fields = buildMap(cls, definedIn != null);\n        Field field = fields.get(definedIn != null ? new FieldKey(name, definedIn, 0) : name);\n        if (field == null) {\n            throw new ObjectAccessException(\"No such field \" + cls.getName() + \".\" + name);\n        } else {\n            return field;\n        }\n    }\n\n    private Map<?, Field> buildMap(Class<?> cls, boolean tupleKeyed) {\n        final String clsName = cls.getName();\n        if (!nameCache.containsKey(clsName)) {\n            synchronized (keyCache) {\n                if (!nameCache.containsKey(clsName)) { // double check\n                    final Map<String, Field> keyedByFieldName = new HashMap<String, Field>();\n                    final Map<FieldKey, Field> keyedByFieldKey = new OrderRetainingMap<FieldKey, Field>();\n                    while (!Object.class.equals(cls)) {\n                        Field[] fields = cls.getDeclaredFields();\n                        if (JVMInfo.reverseFieldDefinition()) {\n                            for (int i = fields.length >> 1; i-- > 0;) {\n                                final int idx = fields.length - i - 1;\n                                final Field field = fields[i];\n                                fields[i] = fields[idx];\n                                fields[idx] = field;\n                            }\n                        }\n                        for (int i = 0; i < fields.length; i++) {\n                            Field field = fields[i];\n                            field.setAccessible(true);\n                            if (!keyedByFieldName.containsKey(field.getName())) {\n                                keyedByFieldName.put(field.getName(), field);\n                            }\n                            keyedByFieldKey.put(new FieldKey(field.getName(), field.getDeclaringClass(), i), field);\n                        }\n                        cls = cls.getSuperclass();\n                    }\n                    nameCache.put(clsName, keyedByFieldName);\n                    keyCache.put(clsName, keyedByFieldKey);\n                }\n            }\n        }\n        return tupleKeyed ? keyCache.get(clsName) : nameCache.get(clsName);\n    }\n\n    /**\n     * @author mycat\n     */\n    private static class FieldKey {\n\n        private String fieldName;\n        private Class<?> declaringClass;\n        private Integer depth;\n        private int order;\n\n        public FieldKey(String fieldName, Class<?> declaringClass, int order) {\n            this.fieldName = fieldName;\n            this.declaringClass = declaringClass;\n            this.order = order;\n            Class<?> c = declaringClass;\n            int i = 0;\n            while (c.getSuperclass() != null) {\n                i++;\n                c = c.getSuperclass();\n            }\n            //不用构造器创建Integer，用静态方法节省时间和空间，因为depth是不可变变量\n            depth = Integer.valueOf(i);\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o) {\n                return true;\n            }\n            if (!(o instanceof FieldKey)) {\n                return false;\n            }\n\n            final FieldKey fieldKey = (FieldKey) o;\n\n            if (declaringClass != null ? !declaringClass.equals(fieldKey.declaringClass)\n                    : fieldKey.declaringClass != null) {\n                return false;\n            }\n\n            if (fieldName != null ? !fieldName.equals(fieldKey.fieldName) : fieldKey.fieldName != null) {\n                return false;\n            }\n\n            return true;\n        }\n\n        @Override\n        public int hashCode() {\n            int result;\n            result = (fieldName != null ? fieldName.hashCode() : 0);\n            result = 29 * result + (declaringClass != null ? declaringClass.hashCode() : 0);\n            return result;\n        }\n\n        @Override\n        public String toString() {\n            return \"FieldKey{\" + \"order=\" + order + \", writer=\" + depth + \", declaringClass=\" + declaringClass\n                    + \", fieldName='\" + fieldName + \"'\" + \"}\";\n        }\n\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/util/Initializable.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.util;\n\n/**\n * @author mycat\n */\npublic interface Initializable {\n\n    void init();\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/util/JVMInfo.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.util;\n\nimport java.lang.reflect.Field;\nimport java.text.AttributedString;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author mycat\n */\npublic class JVMInfo {\n    private static final float DEFAULT_JAVA_VERSION = 1.3f;\n    private static final boolean reverseFieldOrder;\n    private static final float majorJavaVersion = getMajorJavaVersion(System.getProperty(\"java.specification.version\"));\n\n    private ReflectionProvider reflectionProvider;\n    private Map<String, Class<?>> loaderCache = new HashMap<String, Class<?>>();\n\n    static {\n        boolean reverse = false;\n        final Field[] fields = AttributedString.class.getDeclaredFields();\n        for (int i = 0; i < fields.length; i++) {\n            if (fields[i].getName().equals(\"text\")) {\n                reverse = i > 3;\n            }\n        }\n        reverseFieldOrder = reverse;\n    }\n\n    /**\n     * Parses the java version system property to determine the major java\n     * version, ie 1.x\n     * \n     * @param javaVersion\n     *            the system property 'java.specification.version'\n     * @return A float of the form 1.x\n     */\n    public static final float getMajorJavaVersion(String javaVersion) {\n        try {\n            return Float.parseFloat(javaVersion.substring(0, 3));\n        } catch (NumberFormatException e) {\n            // Some JVMs may not conform to the x.y.z java.version format\n            return DEFAULT_JAVA_VERSION;\n        }\n    }\n\n    public static boolean is14() {\n        return majorJavaVersion >= 1.4f;\n    }\n\n    public static boolean is15() {\n        return majorJavaVersion >= 1.5f;\n    }\n\n    public static boolean is16() {\n        return majorJavaVersion >= 1.6f;\n    }\n\n    private static boolean isSun() {\n        return System.getProperty(\"java.vm.vendor\").indexOf(\"Sun\") != -1;\n    }\n\n    private static boolean isApple() {\n        return System.getProperty(\"java.vm.vendor\").indexOf(\"Apple\") != -1;\n    }\n\n    private static boolean isHPUX() {\n        return System.getProperty(\"java.vm.vendor\").indexOf(\"Hewlett-Packard Company\") != -1;\n    }\n\n    private static boolean isIBM() {\n        return System.getProperty(\"java.vm.vendor\").indexOf(\"IBM\") != -1;\n    }\n\n    private static boolean isBlackdown() {\n        return System.getProperty(\"java.vm.vendor\").indexOf(\"Blackdown\") != -1;\n    }\n\n    /*\n     * Support for sun.misc.Unsafe and sun.reflect.ReflectionFactory is present\n     * in JRockit versions R25.1.0 and later, both 1.4.2 and 5.0 (and in future\n     * 6.0 builds).\n     */\n    private static boolean isBEAWithUnsafeSupport() {\n        // This property should be \"BEA Systems, Inc.\"\n        if (System.getProperty(\"java.vm.vendor\").indexOf(\"BEA\") != -1) {\n\n            /*\n             * Recent 1.4.2 and 5.0 versions of JRockit have a java.vm.version\n             * string starting with the \"R\" JVM version number, i.e.\n             * \"R26.2.0-38-57237-1.5.0_06-20060209...\"\n             */\n            String vmVersion = System.getProperty(\"java.vm.version\");\n            if (vmVersion.startsWith(\"R\")) {\n                /*\n                 * Wecould also check that it's R26 or later, but that is\n                 * implicitly true\n                 */\n                return true;\n            }\n\n            /*\n             * For older JRockit versions we can check java.vm.info. JRockit\n             * 1.4.2 R24 -> \"Native Threads, GC strategy: parallel\" and JRockit\n             * 5.0 R25 -> \"R25.2.0-28\".\n             */\n            String vmInfo = System.getProperty(\"java.vm.info\");\n            if (vmInfo != null) {\n                // R25.1 or R25.2 supports Unsafe, other versions do not\n                return (vmInfo.startsWith(\"R25.1\") || vmInfo.startsWith(\"R25.2\"));\n            }\n        }\n        // If non-BEA, or possibly some very old JRockit version\n        return false;\n    }\n\n    public Class<?> loadClass(String name) {\n        try {\n            Class<?> clazz = loaderCache.get(name);\n            if (clazz == null) {\n                clazz = Class.forName(name, false, getClass().getClassLoader());\n                loaderCache.put(name, clazz);\n            }\n            return clazz;\n        } catch (ClassNotFoundException e) {\n            return null;\n        }\n    }\n\n    public synchronized ReflectionProvider getReflectionProvider() {\n        if (reflectionProvider == null) {\n            reflectionProvider = new ReflectionProvider();\n        }\n        return reflectionProvider;\n    }\n\n    protected boolean canUseSun14ReflectionProvider() {\n        return (isSun() || isApple() || isHPUX() || isIBM() || isBlackdown() || isBEAWithUnsafeSupport()) && is14()\n                && loadClass(\"sun.misc.Unsafe\") != null;\n    }\n\n    public static boolean reverseFieldDefinition() {\n        return reverseFieldOrder;\n    }\n\n    public static void main(String[] args) {\n        System.out.println(majorJavaVersion);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/util/ObjectAccessException.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.util;\n\n/**\n * @author mycat\n */\npublic class ObjectAccessException extends RuntimeException {\n    private static final long serialVersionUID = 1L;\n\n    public ObjectAccessException(String message) {\n        super(message);\n    }\n\n    public ObjectAccessException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/util/OrderRetainingMap.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.util;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\n/**\n * @author mycat\n */\npublic class OrderRetainingMap<K, V> extends HashMap<K, V> {\n    private static final long serialVersionUID = 1L;\n\n    private Set<K> keyOrder = new ArraySet<K>();\n    private List<V> valueOrder = new ArrayList<V>();\n\n    @Override\n    public V put(K key, V value) {\n        keyOrder.add(key);\n        valueOrder.add(value);\n        return super.put(key, value);\n    }\n\n    @Override\n    public Collection<V> values() {\n        return Collections.unmodifiableList(valueOrder);\n    }\n\n    @Override\n    public Set<K> keySet() {\n        return Collections.unmodifiableSet(keyOrder);\n    }\n\n    @Override\n    public Set<Entry<K, V>> entrySet() {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * @author mycat\n     */\n    private static class ArraySet<T> extends ArrayList<T> implements Set<T> {\n\n        private static final long serialVersionUID = 1L;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/util/ParameterMapping.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.util;\n\nimport java.beans.BeanInfo;\nimport java.beans.IntrospectionException;\nimport java.beans.Introspector;\nimport java.beans.PropertyDescriptor;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class ParameterMapping {\n    private static final Logger                              LOGGER      = LoggerFactory\n                                                                             .getLogger(ParameterMapping.class);\n    private static final Map<Class<?>, PropertyDescriptor[]> descriptors = new HashMap<Class<?>, PropertyDescriptor[]>();\n\n    /**\n     * 将property键值对赋值组装到object中\n     * @param object 目标反射对象\n     * @param parameter property的键值对\n     * @throws IllegalAccessException\n     * @throws InvocationTargetException\n     */\n    public static void mapping(Object object, Map<String, ? extends Object> parameter) throws IllegalAccessException,\n            InvocationTargetException {\n        //获取用于导出clazz这个JavaBean的所有属性的PropertyDescriptor\n        PropertyDescriptor[] pds = getDescriptors(object.getClass());\n        for (int i = 0; i < pds.length; i++) {\n            PropertyDescriptor pd = pds[i];\n            Object obj = parameter.get(pd.getName());\n            Object value = obj;\n            Class<?> cls = pd.getPropertyType();\n            //类型转换\n            if (obj instanceof String) {\n                String string = (String) obj;\n                if (!StringUtil.isEmpty(string)) {\n                    string = ConfigUtil.filter(string);\n                }\n                if (isPrimitiveType(cls)) {\n                    value = convert(cls, string);\n                }\n            } else if (obj instanceof BeanConfig) {\n                value = createBean((BeanConfig) obj);\n            } else if (obj instanceof BeanConfig[]) {\n                List<Object> list = new ArrayList<Object>();\n                for (BeanConfig beanconfig : (BeanConfig[]) obj) {\n                    list.add(createBean(beanconfig));\n                }\n                value = list.toArray();\n            }\n            //赋值\n            if (cls != null\n                    && value != null) {\n                    Method method = pd.getWriteMethod();\n                    if (method != null) {\n                        method.invoke(object, new Object[] { value });\n                    }\n            }\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static Object createBean(BeanConfig config) throws IllegalAccessException, InvocationTargetException {\n        Object bean = config.create(true);\n        if (bean instanceof Map) {\n            Map<String, Object> map = (Map<String, Object>) bean;\n            for (Map.Entry<String, Object> entry : config.getParams().entrySet()) {\n                String key = entry.getKey();\n                Object value = entry.getValue();\n                if (value instanceof BeanConfig) {\n                    BeanConfig mapBeanConfig = (BeanConfig) entry.getValue();\n                    value = mapBeanConfig.create(true);\n                    mapping(value, mapBeanConfig.getParams());\n                }\n                map.put(key, value);\n            }\n        } else if (bean instanceof List) {\n        } else {\n            mapping(bean, config.getParams());\n        }\n        return bean;\n    }\n\n    /**\n     * 用于导出clazz这个JavaBean的所有属性的PropertyDescriptor\n     * @param clazz\n     * @return\n     */\n    private static PropertyDescriptor[] getDescriptors(Class<?> clazz) {\n        //PropertyDescriptor类表示JavaBean类通过存储器导出一个属性\n        PropertyDescriptor[] pds;\n        List<PropertyDescriptor> list;\n        PropertyDescriptor[] pds2 = descriptors.get(clazz);\n        //该clazz是否第一次加载\n        if (null == pds2) {\n            try {\n                BeanInfo beanInfo = Introspector.getBeanInfo(clazz);\n                pds = beanInfo.getPropertyDescriptors();\n                list = new ArrayList<PropertyDescriptor>();\n                //加载每一个类型不为空的property\n                for (int i = 0; i < pds.length; i++) {\n                    if (null != pds[i].getPropertyType()) {\n                        list.add(pds[i]);\n                    }\n                }\n                pds2 = new PropertyDescriptor[list.size()];\n                list.toArray(pds2);\n            } catch (IntrospectionException ie) {\n                LOGGER.error(\"ParameterMappingError\", ie);\n                pds2 = new PropertyDescriptor[0];\n            }\n        }\n        descriptors.put(clazz, pds2);\n        return (pds2);\n    }\n\n    private static Object convert(Class<?> cls, String string) {\n        Method method = null;\n        Object value = null;\n        if (cls.equals(String.class)) {\n            value = string;\n        } else if (cls.equals(Boolean.TYPE)) {\n            value = Boolean.valueOf(string);\n        } else if (cls.equals(Byte.TYPE)) {\n            value = Byte.valueOf(string);\n        } else if (cls.equals(Short.TYPE)) {\n            value = Short.valueOf(string);\n        } else if (cls.equals(Integer.TYPE)) {\n            value = Integer.valueOf(string);\n        } else if (cls.equals(Long.TYPE)) {\n            value = Long.valueOf(string);\n        } else if (cls.equals(Double.TYPE)) {\n            value = Double.valueOf(string);\n        } else if (cls.equals(Float.TYPE)) {\n            value = Float.valueOf(string);\n        } else if ((cls.equals(Boolean.class)) || (cls.equals(Byte.class)) || (cls.equals(Short.class))\n                || (cls.equals(Integer.class)) || (cls.equals(Long.class)) || (cls.equals(Float.class))\n                || (cls.equals(Double.class))) {\n            try {\n                method = cls.getMethod(\"valueOf\", new Class[] { String.class });\n                value = method.invoke(null, new Object[] { string });\n            } catch (Exception t) {\n                LOGGER.error(\"valueofError\", t);\n                value = null;\n            }\n        } else if (cls.equals(Class.class)) {\n            try {\n                value = Class.forName(string);\n            } catch (ClassNotFoundException e) {\n                throw new ConfigException(e);\n            }\n        } else {\n            value = null;\n        }\n        return (value);\n    }\n\n    private static boolean isPrimitiveType(Class<?> cls) {\n        if (cls.equals(String.class) || cls.equals(Boolean.TYPE) || cls.equals(Byte.TYPE) || cls.equals(Short.TYPE)\n                || cls.equals(Integer.TYPE) || cls.equals(Long.TYPE) || cls.equals(Double.TYPE)\n                || cls.equals(Float.TYPE) || cls.equals(Boolean.class) || cls.equals(Byte.class)\n                || cls.equals(Short.class) || cls.equals(Integer.class) || cls.equals(Long.class)\n                || cls.equals(Float.class) || cls.equals(Double.class) || cls.equals(Class.class)) {\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/util/ReflectionProvider.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.util;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectStreamClass;\nimport java.io.ObjectStreamConstants;\nimport java.io.Serializable;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\n\n/**\n * @author mycat\n */\npublic class ReflectionProvider {\n\n    private transient Map<Class<?>, byte[]> serializedDataCache = Collections\n            .synchronizedMap(new HashMap<Class<?>, byte[]>());\n    private transient FieldDictionary fieldDictionary = new FieldDictionary();\n\n    public Object newInstance(Class<?> type) {\n        try {\n            Constructor<?>[] c = type.getDeclaredConstructors();\n            for (int i = 0; i < c.length; i++) {\n                if (c[i].getParameterTypes().length == 0) {\n                    if (!Modifier.isPublic(c[i].getModifiers())) {\n                        c[i].setAccessible(true);\n                    }\n                    return c[i].newInstance(new Object[0]);\n                }\n            }\n            if (Serializable.class.isAssignableFrom(type)) {\n                return instantiateUsingSerialization(type);\n            } else {\n                throw new ObjectAccessException(\"Cannot construct \" + type.getName()\n                        + \" as it does not have a no-args constructor\");\n            }\n        } catch (InstantiationException e) {\n            throw new ObjectAccessException(\"Cannot construct \" + type.getName(), e);\n        } catch (IllegalAccessException e) {\n            throw new ObjectAccessException(\"Cannot construct \" + type.getName(), e);\n        } catch (InvocationTargetException e) {\n            if (e.getTargetException() instanceof RuntimeException) {\n                throw (RuntimeException) e.getTargetException();\n            } else if (e.getTargetException() instanceof Error) {\n                throw (Error) e.getTargetException();\n            } else {\n                throw new ObjectAccessException(\"Constructor for \" + type.getName() + \" threw an exception\",\n                        e.getTargetException());\n            }\n        }\n    }\n\n    public void visitSerializableFields(Object object, Visitor visitor) {\n        for (Iterator<Field> iterator = fieldDictionary.serializableFieldsFor(object.getClass()); iterator.hasNext();) {\n            Field field = iterator.next();\n            if (!fieldModifiersSupported(field)) {\n                continue;\n            }\n            validateFieldAccess(field);\n            try {\n                Object value = field.get(object);\n                visitor.visit(field.getName(), field.getType(), field.getDeclaringClass(), value);\n            } catch (IllegalArgumentException e) {\n                throw new ObjectAccessException(\"Could not get field \" + field.getClass() + \".\" + field.getName(), e);\n            } catch (IllegalAccessException e) {\n                throw new ObjectAccessException(\"Could not get field \" + field.getClass() + \".\" + field.getName(), e);\n            }\n        }\n    }\n\n    public void writeField(Object object, String fieldName, Object value, Class<?> definedIn) {\n        Field field = fieldDictionary.field(object.getClass(), fieldName, definedIn);\n        validateFieldAccess(field);\n        try {\n            field.set(object, value);\n        } catch (IllegalArgumentException e) {\n            throw new ObjectAccessException(\"Could not set field \" + field.getName() + \"@\" + object.getClass(), e);\n        } catch (IllegalAccessException e) {\n            throw new ObjectAccessException(\"Could not set field \" + field.getName() + \"@\" + object.getClass(), e);\n        }\n    }\n\n    public void invokeMethod(Object object, String methodName, Object value, Class<?> definedIn) {\n        try {\n            Method method = object.getClass().getMethod(methodName, new Class[] { value.getClass() });\n            method.invoke(object, new Object[] { value });\n        } catch (Exception e) {\n            throw new ObjectAccessException(\"Could not invoke \" + object.getClass() + \".\" + methodName, e);\n        }\n    }\n\n    public Class<?> getFieldType(Object object, String fieldName, Class<?> definedIn) {\n        return fieldDictionary.field(object.getClass(), fieldName, definedIn).getType();\n    }\n\n    public boolean fieldDefinedInClass(String fieldName, Class<?> type) {\n        try {\n            Field field = fieldDictionary.field(type, fieldName, null);\n            return fieldModifiersSupported(field);\n        } catch (ObjectAccessException e) {\n            return false;\n        }\n    }\n\n    public Field getField(Class<?> definedIn, String fieldName) {\n        return fieldDictionary.field(definedIn, fieldName, null);\n    }\n\n    private Object instantiateUsingSerialization(Class<?> type) {\n        try {\n            byte[] data;\n            if (serializedDataCache.containsKey(type)) {\n                data = serializedDataCache.get(type);\n            } else {\n                ByteArrayOutputStream bytes = new ByteArrayOutputStream();\n                DataOutputStream stream = new DataOutputStream(bytes);\n                stream.writeShort(ObjectStreamConstants.STREAM_MAGIC);\n                stream.writeShort(ObjectStreamConstants.STREAM_VERSION);\n                stream.writeByte(ObjectStreamConstants.TC_OBJECT);\n                stream.writeByte(ObjectStreamConstants.TC_CLASSDESC);\n                stream.writeUTF(type.getName());\n                stream.writeLong(ObjectStreamClass.lookup(type).getSerialVersionUID());\n                stream.writeByte(2); // classDescFlags (2 = Serializable)\n                stream.writeShort(0); // field count\n                stream.writeByte(ObjectStreamConstants.TC_ENDBLOCKDATA);\n                stream.writeByte(ObjectStreamConstants.TC_NULL);\n                data = bytes.toByteArray();\n                serializedDataCache.put(type, data);\n            }\n\n            ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data));\n            return in.readObject();\n        } catch (IOException e) {\n            throw new ObjectAccessException(\"Cannot create \" + type.getName() + \" by JDK serialization\", e);\n        } catch (ClassNotFoundException e) {\n            throw new ObjectAccessException(\"Cannot find class \" + e.getMessage());\n        }\n    }\n\n    private boolean fieldModifiersSupported(Field field) {\n        return !(Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers()));\n    }\n\n    private void validateFieldAccess(Field field) {\n        if (Modifier.isFinal(field.getModifiers())) {\n            if (JVMInfo.is15()) {\n                field.setAccessible(true);\n            } else {\n                throw new ObjectAccessException(\"Invalid final field \" + field.getDeclaringClass().getName() + \".\"\n                        + field.getName());\n            }\n        }\n    }\n\n    private Object readResolve() {\n        serializedDataCache = Collections.synchronizedMap(new HashMap<Class<?>, byte[]>());\n        fieldDictionary = new FieldDictionary();\n        return this;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/config/util/Visitor.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.config.util;\n\n/**\n * @author mycat\n */\npublic interface Visitor {\n\n    void visit(String name, Class<?> type, Class<?> definedIn, Object value);\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/ManagerConnection.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.manager;\r\n\r\nimport java.io.IOException;\r\nimport java.nio.channels.NetworkChannel;\r\n\r\nimport io.mycat.config.model.SystemConfig;\r\nimport io.mycat.net.FrontendConnection;\r\nimport io.mycat.util.TimeUtil;\r\n\r\n/**\r\n * @author mycat\r\n */\r\npublic class ManagerConnection extends FrontendConnection {\r\n\tprivate long authTimeout = SystemConfig.DEFAULT_AUTH_TIMEOUT;\r\n\r\n\tpublic ManagerConnection(NetworkChannel channel) throws IOException {\r\n\t\tsuper(channel);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isIdleTimeout() {\r\n\t\tif (isAuthenticated) {\r\n\t\t\treturn super.isIdleTimeout();\r\n\t\t} else {\r\n\t\t\treturn TimeUtil.currentTimeMillis() > Math.max(lastWriteTime, lastReadTime) + this.authTimeout;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic long getAuthTimeout() {\r\n\t\treturn authTimeout;\r\n\t}\r\n\r\n\tpublic void setAuthTimeout(long authTimeout) {\r\n\t\tthis.authTimeout = authTimeout;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void handle(final byte[] data) {\r\n\t\tthis.executeSqlId ++;\r\n\t\thandler.handle(data);\r\n\t}\r\n\r\n    @Override\r\n    public void checkQueueFlow() {\r\n        // TODO Auto-generated method stub\r\n\r\n    }\r\n\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/ManagerConnectionFactory.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager;\n\nimport java.io.IOException;\nimport java.nio.channels.NetworkChannel;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.MycatPrivileges;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.net.factory.FrontendConnectionFactory;\n\n/**\n * @author mycat\n */\npublic class ManagerConnectionFactory extends FrontendConnectionFactory {\n\n    @Override\n    protected FrontendConnection getConnection(NetworkChannel channel) throws IOException {\n        SystemConfig sys = MycatServer.getInstance().getConfig().getSystem();\n        ManagerConnection c = new ManagerConnection(channel);\n        MycatServer.getInstance().getConfig().setSocketParams(c, true);\n        c.setAuthTimeout(sys.getAuthTimeout());\n        c.setPrivileges(MycatPrivileges.instance());\n        c.setQueryHandler(new ManagerQueryHandler(c));\n        return c;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/ManagerQueryHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.manager.handler.*;\nimport io.mycat.manager.response.KillConnection;\nimport io.mycat.manager.response.Offline;\nimport io.mycat.manager.response.Online;\nimport io.mycat.net.handler.FrontendQueryHandler;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.route.parser.ManagerParse;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author mycat\n */\npublic class ManagerQueryHandler implements FrontendQueryHandler {\n    private static final Logger LOGGER = LoggerFactory.getLogger(ManagerQueryHandler.class);\n    private static final int SHIFT = 8;\n    private final ManagerConnection source;\n    protected Boolean readOnly;\n\n    public ManagerQueryHandler(ManagerConnection source) {\n        this.source = source;\n    }\n\n    public void setReadOnly(Boolean readOnly) {\n        this.readOnly = readOnly;\n    }\n\n    @Override\n    public void query(String sql) {\n        ManagerConnection c = this.source;\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(new StringBuilder().append(c).append(sql).toString());\n        }\n        int rs = ManagerParse.parse(sql);\n        switch (rs & 0xff) {\n            case ManagerParse.SELECT:\n                SelectHandler.handle(sql, c, rs >>> SHIFT);\n                break;\n            case ManagerParse.SET:\n                c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n                break;\n            case ManagerParse.SHOW:\n                ShowHandler.handle(sql, c, rs >>> SHIFT);\n                break;\n            case ManagerParse.SWITCH:\n                SwitchHandler.handler(sql, c, rs >>> SHIFT);\n                break;\n            case ManagerParse.KILL_CONN:\n                KillConnection.response(sql, rs >>> SHIFT, c);\n                break;\n            case ManagerParse.OFFLINE:\n                Offline.execute(sql, c);\n                break;\n            case ManagerParse.ONLINE:\n                Online.execute(sql, c);\n                break;\n            case ManagerParse.STOP:\n                StopHandler.handle(sql, c, rs >>> SHIFT);\n                break;\n            case ManagerParse.RELOAD:\n                ReloadHandler.handle(sql, c, rs >>> SHIFT);\n                break;\n            case ManagerParse.ROLLBACK:\n                RollbackHandler.handle(sql, c, rs >>> SHIFT);\n                break;\n            case ManagerParse.CLEAR:\n                ClearHandler.handle(sql, c, rs >>> SHIFT);\n                break;\n            case ManagerParse.CONFIGFILE:\n                ConfFileHandler.handle(sql, c);\n                break;\n            case ManagerParse.LOGFILE:\n                ShowServerLog.handle(sql, c);\n                break;\n            case ManagerParse.ZK:\n                ZKHandler.handle(sql, c, rs >>> SHIFT);\n                break;\n            default:\n                c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/handler/ClearHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.handler;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.manager.response.ClearSlow;\nimport io.mycat.route.parser.ManagerParseClear;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class ClearHandler {\n\n    public static void handle(String stmt, ManagerConnection c, int offset) {\n        int rs = ManagerParseClear.parse(stmt, offset);\n        switch (rs & 0xff) {\n        case ManagerParseClear.SLOW_DATANODE: {\n            String name = stmt.substring(rs >>> 8).trim();\n            if (StringUtil.isEmpty(name)) {\n                c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\n            } else {\n                ClearSlow.dataNode(c, name);\n            }\n            break;\n        }\n        case ManagerParseClear.SLOW_SCHEMA: {\n            String name = stmt.substring(rs >>> 8).trim();\n            if (StringUtil.isEmpty(name)) {\n                c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\n            } else {\n                ClearSlow.schema(c, name);\n            }\n            break;\n        }\n        default:\n            c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/handler/ConfFileHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.handler;\n\nimport java.io.BufferedOutputStream;\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\nimport org.xml.sax.SAXException;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.util.ConfigUtil;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.StringUtil;\n\n/**\n * Mycat conf file related Handler\n * \n * @author wuzh\n */\npublic final class ConfFileHandler {\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(ConfFileHandler.class);\n\tprivate static final int FIELD_COUNT = 1;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil\n\t\t\t.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tprivate static final String UPLOAD_CMD = \"FILE @@UPLOAD\";\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"DATA\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void handle( String stmt,ManagerConnection c) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c,true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c,true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c,true);\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\tString theStmt = stmt.toUpperCase().trim();\n\t\tPackageBufINf bufInf = null;\n\t\tif (theStmt.equals(\"FILE @@LIST\")) {\n\t\t\tbufInf = listConfigFiles(c, buffer, packetId);\n\t\t} else if (theStmt.startsWith(\"FILE @@SHOW\")) {\n\t\t\tint index = stmt.lastIndexOf(' ');\n\t\t\tString fileName = stmt.substring(index + 1);\n\t\t\tbufInf = showConfigFile(c, buffer, packetId, fileName);\n\t\t} else if (theStmt.startsWith(UPLOAD_CMD)) {\n\t\t\tint index = stmt.indexOf(' ', UPLOAD_CMD.length());\n\t\t\tint index2 = stmt.indexOf(' ', index + 1);\n\t\t\tif (index <= 0 || index2 <= 0 || index + 1 > stmt.length()\n\t\t\t\t\t|| index2 + 1 > stmt.length()) {\n\t\t\t\tbufInf = showInfo(c, buffer, packetId, \"Invald param ,usage  \");\n\t\t\t}\n\t\t\tString fileName = stmt.substring(index + 1, index2);\n\t\t\tString content = stmt.substring(index2 + 1).trim();\n\t\t\tbufInf = upLoadConfigFile(c, buffer, packetId, fileName, content);\n\t\t} else {\n\n\t\t\tbufInf = showInfo(c, buffer, packetId, \"Invald command \");\n\t\t}\n\n\t\tpacketId = bufInf.packetId;\n\t\tbuffer = bufInf.buffer;\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c,true);\n\n\t\t// write buffer\n\t\tc.write(buffer);\n\t}\n\n\tprivate static void checkXMLFile(String xmlFileName, byte[] data)\n\t\t\tthrows ParserConfigurationException, SAXException, IOException {\n\t\tInputStream dtdStream = new ByteArrayInputStream(new byte[0]);\n\t\tFile confDir = new File(SystemConfig.getHomePath(), \"conf\");\n\t\tif (xmlFileName.equals(\"schema.xml\")) {\n\t\t\tdtdStream = MycatServer.class.getResourceAsStream(\"/schema.dtd\");\n\t\t\tif (dtdStream == null) {\n\t\t\t\tdtdStream = new ByteArrayInputStream(readFileByBytes(new File(\n\t\t\t\t\t\tconfDir, \"schema.dtd\")));\n\t\t\t}\n\n\t\t} else if (xmlFileName.equals(\"server.xml\")) {\n\t\t\tdtdStream = MycatServer.class.getResourceAsStream(\"/server.dtd\");\n\t\t\tif (dtdStream == null) {\n\t\t\t\tdtdStream = new ByteArrayInputStream(readFileByBytes(new File(\n\t\t\t\t\t\tconfDir, \"server.dtd\")));\n\t\t\t}\n\t\t} else if (xmlFileName.equals(\"rule.xml\")) {\n\t\t\tdtdStream = MycatServer.class.getResourceAsStream(\"/rule.dtd\");\n\t\t\tif (dtdStream == null) {\n\t\t\t\tdtdStream = new ByteArrayInputStream(readFileByBytes(new File(\n\t\t\t\t\t\tconfDir, \"rule.dtd\")));\n\t\t\t}\n\t\t}\n\t\tConfigUtil.getDocument(dtdStream, new ByteArrayInputStream(data));\n\t}\n\n\t/**\n\t * 以字节为单位读取文件，常用于读二进制文件，如图片、声音、影像等文件。\n\t */\n\tprivate static byte[] readFileByBytes(File fileName) {\n\t\tInputStream in = null;\n\t\tByteArrayOutputStream outStream = new ByteArrayOutputStream();\n\t\ttry { // 一次读多个字节\n\t\t\tbyte[] tempbytes = new byte[100];\n\t\t\tint byteread = 0;\n\t\t\tin = new FileInputStream(fileName);\n\t\t\t// 读入多个字节到字节数组中，byteread为一次读入的字节数\n\t\t\twhile ((byteread = in.read(tempbytes)) != -1) {\n\t\t\t\toutStream.write(tempbytes, 0, byteread);\n\t\t\t}\n\t\t} catch (Exception e1) {\n\t\t    LOGGER.error(\"readFileByBytesError\",e1);\n\t\t} finally {\n\t\t\tif (in != null) {\n\t\t\t\ttry {\n\t\t\t\t\tin.close();\n\t\t\t\t} catch (IOException e1) {\n\t\t\t\t    LOGGER.error(\"readFileByBytesError\",e1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn outStream.toByteArray();\n\t}\n\n\tprivate static PackageBufINf upLoadConfigFile(ManagerConnection c,\n\t\t\tByteBuffer buffer, byte packetId, String fileName, String content) {\n\t\tLOGGER.info(\"Upload Daas Config file \" + fileName + \" ,content:\"\n\t\t\t\t+ content);\n\t\tString tempFileName = System.currentTimeMillis() + \"_\" + fileName;\n\t\tFile tempFile = new File(SystemConfig.getHomePath(), \"conf\"\n\t\t\t\t+ File.separator + tempFileName);\n\t\tBufferedOutputStream buff = null;\n\t\tboolean suc = false;\n\t\ttry {\n\t\t\tbyte[] fileData = content.getBytes(\"UTF-8\");\n\t\t\tif (fileName.endsWith(\".xml\")) {\n\t\t\t\tcheckXMLFile(fileName, fileData);\n\t\t\t}\n\t\t\tbuff = new BufferedOutputStream(new FileOutputStream(tempFile));\n\t\t\tbuff.write(fileData);\n\t\t\tbuff.flush();\n\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.warn(\"write file err \" + e);\n\t\t\treturn showInfo(c, buffer, packetId, \"write file err \" + e);\n\n\t\t} finally {\n\t\t\tif (buff != null) {\n\t\t\t\ttry {\n\t\t\t\t\tbuff.close();\n\t\t\t\t\tsuc = true;\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tLOGGER.warn(\"save config file err \" + e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (suc) {\n\t\t\t// if succcess\n\t\t\tFile oldFile = new File(SystemConfig.getHomePath(), \"conf\"\n\t\t\t\t\t+ File.separator + fileName);\n\t\t\tif (oldFile.exists()) {\n\t\t\t\tFile backUP = new File(SystemConfig.getHomePath(), \"conf\"\n\t\t\t\t\t\t+ File.separator + fileName + \"_\"\n\t\t\t\t\t\t+ System.currentTimeMillis() + \"_auto\");\n\t\t\t\tif (!oldFile.renameTo(backUP)) {\n\t\t\t\t\tString msg = \"rename old file failed\";\n\t\t\t\t\tLOGGER.warn(msg + \" for upload file \"\n\t\t\t\t\t\t\t+ oldFile.getAbsolutePath());\n\t\t\t\t\treturn showInfo(c, buffer, packetId, msg);\n\t\t\t\t}\n\t\t\t}\n\t\t\tFile dest = new File(SystemConfig.getHomePath(), \"conf\"\n\t\t\t\t\t+ File.separator + fileName);\n\t\t\tif (!tempFile.renameTo(dest)) {\n\t\t\t\tString msg = \"rename file failed\";\n\t\t\t\tLOGGER.warn(msg + \" for upload file \"\n\t\t\t\t\t\t+ tempFile.getAbsolutePath());\n\t\t\t\treturn showInfo(c, buffer, packetId, msg);\n\t\t\t}\n\t\t\treturn showInfo(c, buffer, packetId, \"SUCCESS SAVED FILE:\"\n\t\t\t\t\t+ fileName);\n\t\t} else {\n\t\t\treturn showInfo(c, buffer, packetId, \"UPLOAD ERROR OCCURD:\"\n\t\t\t\t\t+ fileName);\n\t\t}\n\t}\n\n\tprivate static PackageBufINf showInfo(ManagerConnection c,\n\t\t\tByteBuffer buffer, byte packetId, String string) {\n\t\tPackageBufINf bufINf = new PackageBufINf();\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(StringUtil.encode(string, c.getCharset()));\n\t\trow.packetId = ++packetId;\n\t\tbuffer = row.write(buffer, c,true);\n\t\tbufINf.packetId = packetId;\n\t\tbufINf.buffer = buffer;\n\t\treturn bufINf;\n\t}\n\n\tprivate static PackageBufINf showConfigFile(ManagerConnection c,\n\t\t\tByteBuffer buffer, byte packetId, String fileName) {\n\t\tFile file = new File(SystemConfig.getHomePath(), \"conf\"\n\t\t\t\t+ File.separator + fileName);\n\t\tBufferedReader br = null;\n\t\tPackageBufINf bufINf = new PackageBufINf();\n\t\ttry {\n\t\t\tbr = new BufferedReader(new FileReader(file));\n\t\t\tString line = null;\n\t\t\twhile ((line = br.readLine()) != null) {\n\t\t\t\tif (line.isEmpty()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\t\trow.add(StringUtil.encode(line, c.getCharset()));\n\t\t\t\trow.packetId = ++packetId;\n\t\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\t}\n\t\t\tbufINf.buffer = buffer;\n\t\t\tbufINf.packetId = packetId;\n\t\t\treturn bufINf;\n\n\t\t} catch (Exception e) {\n            LOGGER.error(\"showConfigFileError\",e);\n\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\trow.add(StringUtil.encode(e.toString(), c.getCharset()));\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\tbufINf.buffer = buffer;\n\t\t} finally {\n\t\t\tif (br != null) {\n\t\t\t\ttry {\n\t\t\t\t\tbr.close();\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t    LOGGER.error(\"showConfigFileError\",e);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\tbufINf.packetId = packetId;\n\t\treturn bufINf;\n\t}\n\n\tprivate static PackageBufINf listConfigFiles(ManagerConnection c,\n\t\t\tByteBuffer buffer, byte packetId) {\n\t\tPackageBufINf bufINf = new PackageBufINf();\n\t\tSimpleDateFormat df = new SimpleDateFormat(\"yyyy-MM-dd HH:mm\");\n\t\ttry {\n\t\t\tint i = 1;\n\t\t\tFile[] file = new File(SystemConfig.getHomePath(), \"conf\")\n\t\t\t\t\t.listFiles();\n\t\t\tfor (File f : file) {\n\t\t\t\tif (f.isFile()) {\n\t\t\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\t\t\trow.add(StringUtil.encode(\n\t\t\t\t\t\t\t(i++) + \" : \" + f.getName() + \"  time:\"\n\t\t\t\t\t\t\t\t\t+ df.format(new Date(f.lastModified())),\n\t\t\t\t\t\t\tc.getCharset()));\n\t\t\t\t\trow.packetId = ++packetId;\n\t\t\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbufINf.buffer = buffer;\n\t\t\tbufINf.packetId = packetId;\n\t\t\treturn bufINf;\n\n\t\t} catch (Exception e) {\n            LOGGER.error(\"listConfigFilesError\",e);\n\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\trow.add(StringUtil.encode(e.toString(), c.getCharset()));\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\tbufINf.buffer = buffer;\n\t\t}\n\t\tbufINf.packetId = packetId;\n\t\treturn bufINf;\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tString stmt = \"FILE @@UPLOAD test.xml 1234567890\";\n\t\tint index = stmt.indexOf(' ', UPLOAD_CMD.length());\n\t\tint index2 = stmt.indexOf(' ', index + 1);\n\t\tif (index <= 0 || index2 <= 0 || index + 1 > stmt.length()\n\t\t\t\t|| index2 + 1 > stmt.length()) {\n\t\t\tSystem.out.println(\"valid ....\");\n\t\t} else {\n\t\t\tString fileName = stmt.substring(index + 1, index2);\n\t\t\tString content = stmt.substring(index2 + 1).trim();\n\t\t\tSystem.out.println(fileName + \" content:\" + content);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/handler/ReloadHandler.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.manager.handler;\r\n\r\nimport io.mycat.config.ErrorCode;\r\nimport io.mycat.manager.ManagerConnection;\r\nimport io.mycat.manager.response.ReloadConfig;\r\nimport io.mycat.manager.response.ReloadQueryCf;\r\nimport io.mycat.manager.response.ReloadSqlSlowTime;\r\nimport io.mycat.manager.response.ReloadSqlStat;\r\nimport io.mycat.manager.response.ReloadUser;\r\nimport io.mycat.manager.response.ReloadUserStat;\r\nimport io.mycat.route.parser.ManagerParseReload;\r\nimport io.mycat.route.parser.util.ParseUtil;\r\n\r\n/**\r\n * @author mycat\r\n */\r\npublic final class ReloadHandler\r\n{\r\n\r\n    public static void handle(String stmt, ManagerConnection c, int offset)\r\n    {\r\n        int rs = ManagerParseReload.parse(stmt, offset);\r\n        switch (rs)\r\n        {\r\n            case ManagerParseReload.CONFIG:\r\n                ReloadConfig.execute(c,false);\r\n                break;\r\n            case ManagerParseReload.CONFIG_ALL:\r\n                ReloadConfig.execute(c,true);\r\n                break;\r\n            case ManagerParseReload.ROUTE:\r\n                c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\r\n                break;\r\n            case ManagerParseReload.USER:\r\n                ReloadUser.execute(c);\r\n                break;\r\n            case ManagerParseReload.USER_STAT:\r\n                ReloadUserStat.execute(c);\r\n                break;\r\n            case ManagerParseReload.SQL_SLOW:\r\n            \tReloadSqlSlowTime.execute(c, ParseUtil.getSQLId(stmt));\r\n                break;           \r\n            case ManagerParseReload.QUERY_CF:            \t\r\n            \tString filted = ParseUtil.parseString(stmt) ;\r\n            \tReloadQueryCf.execute(c, filted);\r\n            \tbreak;\r\n            case ManagerParseReload.SQL_STAT:\r\n                String openCloseFlag = ParseUtil.parseString(stmt) ;\r\n                ReloadSqlStat.execute(c, openCloseFlag);\r\n                break;\r\n            default:\r\n                c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\r\n        }\r\n    }\r\n\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/handler/RollbackHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.handler;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.manager.response.RollbackConfig;\nimport io.mycat.manager.response.RollbackUser;\nimport io.mycat.route.parser.ManagerParseRollback;\n\n/**\n * @author mycat\n */\npublic final class RollbackHandler {\n\n    public static void handle(String stmt, ManagerConnection c, int offset) {\n        switch (ManagerParseRollback.parse(stmt, offset)) {\n        case ManagerParseRollback.CONFIG:\n            RollbackConfig.execute(c);\n            break;\n        case ManagerParseRollback.ROUTE:\n            c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\n            break;\n        case ManagerParseRollback.USER:\n            RollbackUser.execute(c);\n            break;\n        default:\n            c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/handler/SelectHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.handler;\n\nimport static io.mycat.route.parser.ManagerParseSelect.SESSION_AUTO_INCREMENT;\nimport static io.mycat.route.parser.ManagerParseSelect.VERSION_COMMENT;\nimport static io.mycat.route.parser.ManagerParseSelect.SESSION_TX_READ_ONLY;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.manager.response.SelectSessionAutoIncrement;\nimport io.mycat.manager.response.SelectSessionTxReadOnly;\nimport io.mycat.manager.response.SelectVersionComment;\nimport io.mycat.route.parser.ManagerParseSelect;\n\n/**\n * @author mycat\n */\npublic final class SelectHandler {\n\n    public static void handle(String stmt, ManagerConnection c, int offset) {\n        switch (ManagerParseSelect.parse(stmt, offset)) {\n        case VERSION_COMMENT:\n            SelectVersionComment.execute(c);\n            break;\n        case SESSION_AUTO_INCREMENT:\n            SelectSessionAutoIncrement.execute(c);\n            break;\n        case SESSION_TX_READ_ONLY:\n        \tSelectSessionTxReadOnly.execute(c);\n        \tbreak;\n        default:\n            c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/handler/ShowHandler.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.manager.handler;\r\n\r\nimport io.mycat.config.ErrorCode;\r\nimport io.mycat.manager.ManagerConnection;\r\nimport io.mycat.manager.response.CheckGlobalTable;\r\nimport io.mycat.manager.response.ShowBackend;\r\nimport io.mycat.manager.response.ShowBackendOld;\r\nimport io.mycat.manager.response.ShowCollation;\r\nimport io.mycat.manager.response.ShowCommand;\r\nimport io.mycat.manager.response.ShowConnection;\r\nimport io.mycat.manager.response.ShowConnectionSQL;\r\nimport io.mycat.manager.response.ShowDataNode;\r\nimport io.mycat.manager.response.ShowDataSource;\r\nimport io.mycat.manager.response.ShowDatabase;\r\nimport io.mycat.manager.response.ShowDatasourceCluster;\r\nimport io.mycat.manager.response.ShowDatasourceSyn;\r\nimport io.mycat.manager.response.ShowDatasourceSynDetail;\r\nimport io.mycat.manager.response.ShowHeartbeat;\r\nimport io.mycat.manager.response.ShowHeartbeatDetail;\r\nimport io.mycat.manager.response.ShowHelp;\r\nimport io.mycat.manager.response.ShowParser;\r\nimport io.mycat.manager.response.ShowProcessor;\r\nimport io.mycat.manager.response.ShowRouter;\r\nimport io.mycat.manager.response.ShowSQL;\r\nimport io.mycat.manager.response.ShowSQLCondition;\r\nimport io.mycat.manager.response.ShowSQLDetail;\r\nimport io.mycat.manager.response.ShowSQLExecute;\r\nimport io.mycat.manager.response.ShowSQLHigh;\r\nimport io.mycat.manager.response.ShowSQLLarge;\r\nimport io.mycat.manager.response.ShowSQLSlow;\r\nimport io.mycat.manager.response.ShowSQLSumTable;\r\nimport io.mycat.manager.response.ShowSQLSumUser;\r\nimport io.mycat.manager.response.ShowServer;\r\nimport io.mycat.manager.response.ShowSession;\r\nimport io.mycat.manager.response.ShowSqlResultSet;\r\nimport io.mycat.manager.response.ShowSysLog;\r\nimport io.mycat.manager.response.ShowSysParam;\r\nimport io.mycat.manager.response.ShowThreadPool;\r\nimport io.mycat.manager.response.ShowTime;\r\nimport io.mycat.manager.response.ShowVariables;\r\nimport io.mycat.manager.response.ShowVersion;\r\nimport io.mycat.manager.response.ShowWhiteHost;\r\nimport io.mycat.manager.response.ShowDirectMemory;\r\nimport io.mycat.route.parser.ManagerParseShow;\r\nimport io.mycat.route.parser.util.ParseUtil;\r\nimport io.mycat.server.handler.ShowCache;\r\nimport io.mycat.util.StringUtil;\r\n\r\n/**\r\n * @author mycat\r\n */\r\npublic final class ShowHandler {\r\n\r\n\tpublic static void handle(String stmt, ManagerConnection c, int offset) {\r\n\t\tint rs = ManagerParseShow.parse(stmt, offset);\r\n\t\tswitch (rs & 0xff) {\r\n\t\tcase ManagerParseShow.SYSPARAM://add rainbow\r\n\t\t\tShowSysParam.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.SYSLOG: //add by zhuam\r\n\t\t\tString lines = stmt.substring(rs >>> 8).trim();\r\n\t\t\tShowSysLog.execute(c, Integer.parseInt( lines ) );\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.COMMAND:\r\n\t\t\tShowCommand.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.COLLATION:\r\n\t\t\tShowCollation.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.CONNECTION:\r\n\t\t\tShowConnection.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.BACKEND:\r\n\t\t\tShowBackend.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.BACKEND_OLD:\r\n\t\t\tShowBackendOld.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.CONNECTION_SQL:\r\n\t\t\tShowConnectionSQL.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.DATABASE:\r\n\t\t\tShowDatabase.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.DATANODE:\r\n\t\t\tShowDataNode.execute(c, null);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.DATANODE_WHERE: {\r\n\t\t\tString name = stmt.substring(rs >>> 8).trim();\r\n\t\t\tif (StringUtil.isEmpty(name)) {\r\n\t\t\t\tc.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\r\n\t\t\t} else {\r\n\t\t\t\tShowDataNode.execute(c, name);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tcase ManagerParseShow.DATASOURCE:\r\n\t\t\tShowDataSource.execute(c, null);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.DATASOURCE_WHERE: {\r\n\t\t\tString name = stmt.substring(rs >>> 8).trim();\r\n\t\t\tif (StringUtil.isEmpty(name)) {\r\n\t\t\t\tc.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\r\n\t\t\t} else {\r\n\t\t\t\tShowDataSource.execute(c, name);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tcase ManagerParseShow.HELP:\r\n\t\t\tShowHelp.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.HEARTBEAT:\r\n\t\t\tShowHeartbeat.response(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.PARSER:\r\n\t\t\tShowParser.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.PROCESSOR:\r\n\t\t\tShowProcessor.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.ROUTER:\r\n\t\t\tShowRouter.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.SERVER:\r\n\t\t\tShowServer.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.WHITE_HOST:\r\n\t\t\tShowWhiteHost.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.WHITE_HOST_SET:\r\n\t\t\tShowWhiteHost.setHost(c,ParseUtil.parseString(stmt));\r\n\t\t\tbreak;\t\t\t\t\t\r\n\t\tcase ManagerParseShow.SQL:\r\n\t\t\tboolean isClearSql = Boolean.valueOf( stmt.substring(rs >>> 8).trim() );\r\n\t\t\tShowSQL.execute(c, isClearSql);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.SQL_DETAIL:\r\n\t\t\tShowSQLDetail.execute(c, ParseUtil.getSQLId(stmt));\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.SQL_EXECUTE:\r\n\t\t\tShowSQLExecute.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.SQL_SLOW:\r\n\t\t\tboolean isClearSlow = Boolean.valueOf( stmt.substring(rs >>> 8).trim() );\r\n            ShowSQLSlow.execute(c, isClearSlow, null);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.SQL_HIGH:\r\n\t\t\tboolean isClearHigh = Boolean.valueOf( stmt.substring(rs >>> 8).trim() );\r\n\t\t\tShowSQLHigh.execute(c, isClearHigh);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.SQL_LARGE:\r\n\t\t\tboolean isClearLarge = Boolean.valueOf( stmt.substring(rs >>> 8).trim() );\r\n\t\t\tShowSQLLarge.execute(c, isClearLarge);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.SQL_CONDITION:\r\n\t\t\tShowSQLCondition.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.SQL_RESULTSET:\r\n\t\t\tShowSqlResultSet.execute(c);\r\n\t\t\tbreak;\t\r\n\t\tcase ManagerParseShow.SQL_SUM_USER:\r\n\t\t\tboolean isClearSum = Boolean.valueOf( stmt.substring(rs >>> 8).trim() );\r\n\t\t\tShowSQLSumUser.execute(c,isClearSum);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.SQL_SUM_TABLE:\r\n\t\t\tboolean isClearTable = Boolean.valueOf( stmt.substring(rs >>> 8).trim() );\r\n\t\t\tShowSQLSumTable.execute(c, isClearTable);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.SLOW_DATANODE: {\r\n\t\t\tString name = stmt.substring(rs >>> 8).trim();\r\n\t\t\tif (StringUtil.isEmpty(name)) {\r\n\t\t\t\tc.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\r\n\t\t\t} else {\r\n\t\t\t\t// ShowSlow.dataNode(c, name);\r\n\t\t\t\tc.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tcase ManagerParseShow.SLOW_SCHEMA: {\r\n\t\t\tString name = stmt.substring(rs >>> 8).trim();\r\n\t\t\tif (StringUtil.isEmpty(name)) {\r\n                c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement,schema value is null\");\r\n\t\t\t} else {\r\n                // c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\r\n                ShowSQLSlow.execute(c, false, StringUtil.removeBackquote(name));\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tcase ManagerParseShow.THREADPOOL:\r\n\t\t\tShowThreadPool.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.CACHE:\r\n\t\t\tShowCache.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.SESSION:\r\n\t\t\tShowSession.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.TIME_CURRENT:\r\n\t\t\tShowTime.execute(c, ManagerParseShow.TIME_CURRENT);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.TIME_STARTUP:\r\n\t\t\tShowTime.execute(c, ManagerParseShow.TIME_STARTUP);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.VARIABLES:\r\n\t\t\tShowVariables.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.VERSION:\r\n\t\t\tShowVersion.execute(c);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.HEARTBEAT_DETAIL://by songwie\r\n\t\t\tShowHeartbeatDetail.response(c,stmt);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.DATASOURCE_SYNC://by songwie\r\n\t\t\tShowDatasourceSyn.response(c,stmt);\r\n\t\t\tbreak;\t\r\n\t\tcase ManagerParseShow.DATASOURCE_SYNC_DETAIL://by songwie\r\n\t\t\tShowDatasourceSynDetail.response(c,stmt);\r\n\t\t\tbreak;\t\r\n\t\tcase ManagerParseShow.DATASOURCE_CLUSTER://by songwie\r\n\t\t\tShowDatasourceCluster.response(c,stmt);\r\n\t\t\tbreak;\t\r\n\t\tcase ManagerParseShow.DIRECTMEMORY_DETAILl:\r\n\t\t\tShowDirectMemory.execute(c,2);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.DIRECTMEMORY_TOTAL:\r\n\t\t\tShowDirectMemory.execute(c,1);\r\n\t\t\tbreak;\r\n\t\tcase ManagerParseShow.CHECK_GLOBAL:\r\n\t\t\tCheckGlobalTable.execute(c, stmt);\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tc.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/handler/ShowServerLog.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.handler;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.CircularArrayList;\nimport io.mycat.util.StringUtil;\n\npublic final class ShowServerLog {\n\tprivate static final int FIELD_COUNT = 1;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil\n\t\t\t.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tprivate static final String DEFAULT_LOGFILE = \"mycat.log\";\n    private static final Logger                LOGGER          = LoggerFactory\n                                                                   .getLogger(ShowServerLog.class);\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"LOG\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\teof.packetId = ++packetId;\n\t}\n\n\tprivate static File getLogFile(String logFile) {\n\n\t\tString daasHome = SystemConfig.getHomePath();\n\t\tFile file = new File(daasHome, \"logs\" + File.separator + logFile);\n\t\treturn file;\n\t}\n\n\tpublic static void handle(String stmt,ManagerConnection c) {\n\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c,true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c,true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c,true);\n\n\t\t// write rows\n\n\t\tbyte packetId = eof.packetId;\n\t\tPackageBufINf bufInf = null;\n\t\t// show log key=warn limit=0,30\n\t\tMap<String, String> condPairMap = getCondPair(stmt);\n\t\tif (condPairMap.isEmpty()) {\n\t\t\tbufInf = showLogSum(c, buffer, packetId);\n\t\t} else {\n\t\t\tString logFile = condPairMap.get(\"file\");\n\t\t\tif (logFile == null) {\n\t\t\t\tlogFile = DEFAULT_LOGFILE;\n\t\t\t}\n\t\t\tString limitStr = condPairMap.get(\"limit\");\n\t\t\tlimitStr = (limitStr != null) ? limitStr : \"0,\" + 100000;\n\t\t\tString[] limtArry = limitStr.split(\"\\\\s|,\");\n\t\t\tint start = Integer.parseInt(limtArry[0]);\n\t\t\tint page = Integer.parseInt(limtArry[1]);\n\t\t\tint end = Integer.valueOf(start + page);\n\t\t\tString key = condPairMap.get(\"key\");\n\t\t\tString regex = condPairMap.get(\"regex\");\n\t\t\tbufInf = showLogRange(c, buffer, packetId, key, regex, start, end,\n\t\t\t\t\tlogFile);\n\n\t\t}\n\n\t\tpacketId = bufInf.packetId;\n\t\tbuffer = bufInf.buffer;\n\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c,true);\n\n\t\t// write buffer\n\t\tc.write(buffer);\n\t}\n\n\tpublic static PackageBufINf showLogRange(ManagerConnection c,\n\t\t\tByteBuffer buffer, byte packetId, String key, String regex,\n\t\t\tint start, int end, String logFile) {\n\t\tPackageBufINf bufINf = new PackageBufINf();\n\t\tPattern pattern = null;\n\t\tif (regex != null) {\n\t\t\tpattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);\n\t\t}\n\t\tif (key != null) {\n\t\t\tkey = key.toLowerCase();\n\t\t}\n\t\tFile file = getLogFile(logFile);\n\t\tBufferedReader br = null;\n\t\tint curLine = 0;\n\t\ttry {\n\t\t\tbr = new BufferedReader(new FileReader(file));\n\t\t\tString line = null;\n\t\t\twhile ((line = br.readLine()) != null) {\n\t\t\t\tcurLine++;\n\t\t\t\tif (curLine >= start && curLine <= end\n\t\t\t\t\t\t&& (\n\t\t\t\t\t\t(pattern != null && pattern.matcher(line).find())\n\t\t\t\t\t\t\t\t|| (pattern == null && key == null)\n\t\t\t\t\t\t\t\t|| (key != null && line.toLowerCase().contains(key))\n\t\t\t\t\t\t)) {\n\t\t\t\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\t\t\t\trow.add(StringUtil.encode(curLine + \"->\" + line,\n\t\t\t\t\t\t\t\tc.getCharset()));\n\t\t\t\t\t\trow.packetId = ++packetId;\n\t\t\t\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbufINf.buffer = buffer;\n\t\t\tbufINf.packetId = packetId;\n\t\t\treturn bufINf;\n\n\t\t} catch (Exception e) {\n            LOGGER.error(\"showLogRangeError\", e);\n\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\trow.add(StringUtil.encode(e.toString(), c.getCharset()));\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\tbufINf.buffer = buffer;\n\t\t} finally {\n\t\t\tif (br != null) {\n\t\t\t\ttry {\n\t\t\t\t\tbr.close();\n\t\t\t\t} catch (IOException e) {\n\t\t            LOGGER.error(\"showLogRangeError\", e);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\tbufINf.packetId = packetId;\n\t\treturn bufINf;\n\t}\n\n\tprivate static PackageBufINf showLogSum(ManagerConnection c,\n\t\t\tByteBuffer buffer, byte packetId) {\n\t\tPackageBufINf bufINf = new PackageBufINf();\n\t\tFile[] logFiles = new File(SystemConfig.getHomePath(), \"logs\")\n\t\t\t\t.listFiles();\n\t\tString fileNames = \"\";\n\t\tfor (File f : logFiles) {\n\t\t\tif (f.isFile()) {\n\t\t\t\tfileNames += \"  \" + f.getName();\n\t\t\t}\n\t\t}\n\n\t\tFile file = getLogFile(DEFAULT_LOGFILE);\n\t\tBufferedReader br = null;\n\t\tint totalLines = 0;\n\t\tCircularArrayList<String> queue = new CircularArrayList<String>(50);\n\t\ttry {\n\t\t\tbr = new BufferedReader(new FileReader(file));\n\t\t\tString line = null;\n\t\t\twhile ((line = br.readLine()) != null) {\n\t\t\t\ttotalLines++;\n\t\t\t\tif (queue.size() == queue.capacity()) {\n\t\t\t\t\tqueue.remove(0);\n\t\t\t\t}\n\t\t\t\tqueue.add(line);\n\n\t\t\t}\n\n\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\trow.add(StringUtil.encode(\"files in log dir:\" + totalLines\n\t\t\t\t\t+ fileNames, c.getCharset()));\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\trow = new RowDataPacket(FIELD_COUNT);\n\t\t\trow.add(StringUtil.encode(\"Total lines \" + totalLines + \" ,tail \"\n\t\t\t\t\t+ queue.size() + \" line is following:\", c.getCharset()));\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\tint size = queue.size() - 1;\n\t\t\tfor (int i = size; i >= 0; i--) {\n\t\t\t\tString data = queue.get(i);\n\t\t\t\trow = new RowDataPacket(FIELD_COUNT);\n\t\t\t\trow.add(StringUtil.encode(data, c.getCharset()));\n\t\t\t\trow.packetId = ++packetId;\n\t\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\t}\n\t\t\tbufINf.buffer = buffer;\n\t\t\tbufINf.packetId = packetId;\n\t\t\treturn bufINf;\n\n\t\t} catch (Exception e) {\n            LOGGER.error(\"showLogSumError\", e);\n\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\trow.add(StringUtil.encode(e.toString(), c.getCharset()));\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\tbufINf.buffer = buffer;\n\t\t} finally {\n\t\t\tif (br != null) {\n\t\t\t\ttry {\n\t\t\t\t\tbr.close();\n\t\t\t\t} catch (IOException e) {\n\t\t            LOGGER.error(\"showLogSumError\", e);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\tbufINf.packetId = packetId;\n\t\treturn bufINf;\n\t}\n\n\tpublic static Map<String, String> getCondPair(String sql) {\n\t\tHashMap<String, String> map = new HashMap<String, String>();\n\t\tPattern p = Pattern.compile(\"(\\\\S+\\\\s*=\\\\s*\\\\S+)\");\n\t\tMatcher m = p.matcher(sql);\n\t\twhile (m.find()) {\n\t\t\tString item = m.group();\n\t\t\tPattern p2 = Pattern.compile(\"(\\\\S+)\\\\s*=\\\\s*(\\\\S+)\");\n\t\t\tMatcher m2 = p2.matcher(item);\n\t\t\tif (m2.find()) {\n\t\t\t\tmap.put(m2.group(1), m2.group(2));\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tString sql = \"show log limit =1,2 key=warn file= \\\"2\\\"  \";\n\t\tMap<String, String> condPairMap = getCondPair(sql);\n\t\tfor (Map.Entry<String, String> entry : condPairMap.entrySet()) {\n\t\t\tSystem.out.println(\"key:\" + entry.getKey() + \",value:\"\n\t\t\t\t\t+ entry.getValue());\n\n\t\t}\n\t\tString limt = \"1,2\";\n\t\tSystem.out.println(Arrays.toString(limt.split(\"\\\\s|,\")));\n\n\t}\n}\n\nclass PackageBufINf {\n\tpublic byte packetId;\n\tpublic ByteBuffer buffer;\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/handler/StopHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.handler;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.manager.response.StopHeartbeat;\nimport io.mycat.route.parser.ManagerParseStop;\n\n/**\n * @author mycat\n */\npublic final class StopHandler {\n\n    public static void handle(String stmt, ManagerConnection c, int offset) {\n        switch (ManagerParseStop.parse(stmt, offset)) {\n        case ManagerParseStop.HEARTBEAT:\n            StopHeartbeat.execute(stmt, c);\n            break;\n        default:\n            c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/handler/SwitchHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.handler;\n\nimport static io.mycat.route.parser.ManagerParseSwitch.DATASOURCE;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.manager.response.SwitchDataSource;\nimport io.mycat.route.parser.ManagerParseSwitch;\n\n/**\n * @author mycat\n */\npublic final class SwitchHandler {\n\n    public static void handler(String stmt, ManagerConnection c, int offset) {\n        switch (ManagerParseSwitch.parse(stmt, offset)) {\n        case DATASOURCE:\n            SwitchDataSource.response(stmt, c);\n            break;\n        default:\n            c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/handler/ZKHandler.java",
    "content": "package io.mycat.manager.handler;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.utils.ZKPaths;\nimport org.apache.zookeeper.data.Stat;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.loader.zkprocess.console.ZkNofiflyCfg;\nimport io.mycat.config.loader.zkprocess.zktoxml.ZktoXmlMain;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.manager.response.ReloadZktoXml;\nimport io.mycat.util.ZKUtils;\n\n/**\n * zookeeper 实现动态配置\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 23:35 2016/5/7\n */\npublic class ZKHandler {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ZKHandler.class);\n\n    /**\n     * 直接从zk拉所有配置，然后本地执行reload_all\n     */\n    public static final String RELOAD_FROM_ZK = \"zk reload_from_zk\";\n\n    /**\n     * 强制所有节点操作\n     */\n    private static final String RELOAD_ALL = \"all\";\n\n    /**\n     * 命令节点信息\n     */\n    public static final String ZK_NODE_PATH = \"command\";\n\n    public static void handle(String stmt, ManagerConnection c, int offset) {\n        String command = stmt.toLowerCase();\n        // 检查当前的命令是否为zk reload_from_zk\n        if (RELOAD_FROM_ZK.equals(command)) {\n            // 调用zktoxml操作\n            try {\n                // 通知所有节点进行数据更新\n                ZktoXmlMain.ZKLISTENER.notifly(ZkNofiflyCfg.ZK_NOTIFLY_LOAD_ALL.getKey());\n\n                // 执行重新加载本地配制信息\n                ReloadHandler.handle(\"RELOAD @@config_all\", c, 6);\n\n                offset += RELOAD_FROM_ZK.length();\n\n                ReloadZktoXml.execute(c, \"zk reload success \");\n            } catch (Exception e) {\n                LOGGER.error(\"ZKHandler loadZktoFile exception\", e);\n                c.writeErrMessage(ErrorCode.ER_YES, \"zk command send error,command is :\" + command);\n            }\n        } else {\n            String[] matchKeys = stmt.split(\"\\\\s+\");\n\n            if (null != matchKeys && matchKeys.length > 2) {\n                // 取得所有配制的节点信息\n                String key = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTER_NODES);\n\n                String[] myidArray = key.split(\",\");\n\n                String idkeys = matchKeys[1].toLowerCase();\n\n                // 发送的命令信息\n                StringBuilder commandMsg = new StringBuilder();\n\n                for (int i = 2; i < matchKeys.length; i++) {\n                    if (i == matchKeys.length - 1) {\n                        commandMsg.append(matchKeys[i]);\n                    } else {\n                        commandMsg.append(matchKeys[i]).append(\" \");\n                    }\n                }\n\n                // 命令的形式为zk all reload_from_zk\n                // 进行第二个匹配，检查是否为所有节点更新\n                if (RELOAD_ALL.equals(idkeys)) {\n                    // 按所有id，将把所有的节点都更新\n                    try {\n                        // 将所有指令发送至服务器\n                        for (String myid : myidArray) {\n                            sendZkCommand(myid, commandMsg.toString());\n                        }\n\n                        ReloadZktoXml.execute(c, \"zk reload \" + matchKeys[1] + \" success \");\n                    } catch (Exception e) {\n                        c.writeErrMessage(ErrorCode.ER_YES, \"zk command send error\");\n                    }\n                }\n                // 如果不是所有节点，则检查是否能匹配上单独的节点\n                else {\n                    for (String myid : myidArray) {\n                        if (myid.equals(idkeys)) {\n                            try {\n                                sendZkCommand(myid, commandMsg.toString());\n\n                                ReloadZktoXml.execute(c, \"zk reload \" + matchKeys[1] + \" success \");\n                            } catch (Exception e) {\n                                c.writeErrMessage(ErrorCode.ER_YES, \"zk command send error,myid :\" + myid);\n                            }\n\n                            break;\n                        }\n                    }\n                }\n\n            } else {\n                c.writeErrMessage(ErrorCode.ER_YES, \"zk command is error\");\n            }\n        }\n    }\n\n    /**\n     * 向节点发送命令\n     * @param myId 节点的id信息\n     * @param command 命令内容 \n     * @throws Exception 异常信息\n     */\n    private static void sendZkCommand(String myId, String command) throws Exception {\n        CuratorFramework zkConn = ZKUtils.getConnection();\n\n        String basePath = ZKUtils.getZKBasePath();\n\n        String nodePath = ZKPaths.makePath(basePath, ZK_NODE_PATH + \"/\" + myId);\n\n        Stat stat;\n        try {\n            stat = zkConn.checkExists().forPath(nodePath);\n\n            if (null == stat) {\n                // 进行目录的创建操作\n                ZKPaths.mkdirs(zkConn.getZookeeperClient().getZooKeeper(), nodePath);\n            }\n            // 设置节点信息\n            zkConn.setData().inBackground().forPath(nodePath, command.getBytes());\n        } catch (Exception e) {\n            LOGGER.error(\"ZKHandler sendZkCommand exception\", e);\n            throw e;\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/CheckGlobalTable.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.google.common.base.CharMatcher;\nimport com.google.common.base.Splitter;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.heartbeat.ConsistenCollectHandler;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * 全局表一致性检测\n * \n * @author mycat\n */\npublic final class CheckGlobalTable {\n\n\t\n\tprivate static final int FIELD_COUNT = 3;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    \n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"ID\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"TABLENAME\", Fields.FIELD_TYPE_VARCHAR);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"RESTULT\", Fields.FIELD_TYPE_VARCHAR);\n        fields[i++].packetId = ++packetId;\n                \n        eof.packetId = ++packetId;\n    }\n    \n    private static Map<String, String> parse(String sql) {\n        Map<String, String> map = new HashMap<>();\n        List<String> rtn = Splitter.on(CharMatcher.whitespace()).omitEmptyStrings().splitToList(sql);\n        for (String s : rtn) {\n            if (s.contains(\"=\")) {\n                int dindex = s.indexOf(\"=\");\n                if (s.startsWith(\"--\")) {\n                    String key = s.substring(2, dindex).toLowerCase().trim();\n                    String value = s.substring(dindex + 1).trim();\n                    map.put(key, value);\n                } else if (s.startsWith(\"-\")) {\n                    String key = s.substring(1, dindex).toLowerCase().trim();\n                    String value = s.substring(dindex + 1).trim();\n                    map.put(key, value);\n                }\n            }\n        }\n        return map;\n    }\n    \n    public static void execute(ManagerConnection c\n    \t\t,String stmt) {\n    \tMap<String,String> paramster = parse(stmt);\n    \tint retryTime = 1;\n    \tlong intervalTime = 200 ;\n    \t//\"show @@CKECK_GLOBAL -SCHEMA=TESTDB -TABLE=E_ACCOUNT_SUBJECT -retrytime=2\"\n//\t\t+ \" -intervaltime=20\"\n    \tString tableName = paramster.get(\"table\");\n    \tString schemaName = paramster.get(\"schema\");\n    \tString retryTimeStr = paramster.get(\"retry\");\n    \tString intervalTimeStr = paramster.get(\"interval\");\n\t\tMycatConfig config = MycatServer.getInstance().getConfig();\n\t\tTableConfig table;\n\t\tSchemaConfig schemaConfig = null;\n    \tif(StringUtil.isEmpty(schemaName)) {\n    \t\tc.writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR, \"schemaName is null, please add paramster  -schema=schemaname \");\n    \t\treturn;\n    \t} else {\n    \t\tschemaConfig = config.getSchemas().get(schemaName);\n    \t\tif(schemaConfig == null){\n        \t\tc.writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR,\n        \t\t\t\t\"schemaName is null, please add paramster  -schema=schemaname \");\n\n    \t\t}\n    \t} \n    \tif(StringUtil.isEmpty(tableName)) {\n    \t\tc.writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR, \"tableName is null, please add paramster  -table=tablename \");\n    \t\treturn;\n    \t} else {\n    \t\t table = schemaConfig.getTables().get(tableName.toUpperCase());\n\n    \t}\n    \tif(StringUtil.isEmpty(retryTimeStr)) {\n    \t\tc.writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR, \"retryTime is null, please add paramster  -retry= \");\n    \t\treturn;\n    \t}else {\n    \t\tretryTime =  Integer.valueOf(retryTimeStr);\n    \t}\n    \t\n    \tif(StringUtil.isEmpty(intervalTimeStr)) {\n    \t\tc.writeErrMessage(ErrorCode.ER_BAD_TABLE_ERROR, \"intervalTime is null, please add paramster  -interval= \");\n    \t\treturn;\n    \t} else {\n    \t\tintervalTime = Long.valueOf(intervalTimeStr);\n    \t} \n    \n//    \ttableName = \"e_account_subject\";\n\n//    \tschemaName = \"TESTDB\";\n    \t\n\n\n\t\tList<String> dataNodeList = table.getDataNodes();\n\t\t\n    \tConsistenCollectHandler cHandler = new ConsistenCollectHandler( c, tableName, schemaName, dataNodeList.size(), retryTime, intervalTime);\n    \tcHandler.startDetector();\n    \t//c.writeErrMessage(ErrorCode.ER_BAD_TABLE_ERROR, \"XXX\");\n    }\n\n\tprivate static RowDataPacket getRow(int i, String tableName, String result,String charset) {\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(LongUtil.toBytes(i));\n\t\trow.add(StringUtil.encode(tableName, charset));\n\t\trow.add(StringUtil.encode(result, charset));\n\t\treturn row;\n\t}\t\n    public static void response(ManagerConnection c,String tableName, String result) {\n    \tByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;     \n        RowDataPacket row = getRow(1, tableName, result, c.getCharset());\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c, true);\n    \n    \t\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n    public static void main(String[] args) {\n    \t// show @@check_global --schema=TESTDB -table=e_account_subject -retry=40 -interval=20;\n    \tMap<String, String> params = CheckGlobalTable.parse(\"show @@CKECK_GLOBAL -SCHEMA=TESTDB -TABLE=E_ACCOUNT_SUBJECT -retrytime=2\"\n    \t\t\t+ \" -intervaltime=20\");\n    \tSystem.out.println(params);\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ClearSlow.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.OkPacket;\n\n/**\n * @author mycat\n */\npublic class ClearSlow {\n\n    public static void dataNode(ManagerConnection c, String name) {\n    \tPhysicalDBNode dn = MycatServer.getInstance().getConfig().getDataNodes().get(name);\n    \tPhysicalDBPool ds = null;\n        if (dn != null && ((ds = dn.getDbPool())!= null)) {\n           // ds.getSqlRecorder().clear();\n            c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n        } else {\n            c.writeErrMessage(ErrorCode.ER_YES, \"Invalid DataNode:\" + name);\n        }\n    }\n\n    public static void schema(ManagerConnection c, String name) {\n        MycatConfig conf = MycatServer.getInstance().getConfig();\n        SchemaConfig schema = conf.getSchemas().get(name);\n        if (schema != null) {\n//            Map<String, MySQLDataNode> dataNodes = conf.getDataNodes();\n//            for (String n : schema.getAllDataNodes()) {\n//                MySQLDataNode dn = dataNodes.get(n);\n//                MySQLDataSource ds = null;\n//                if (dn != null && (ds = dn.getSource()) != null) {\n//                    ds.getSqlRecorder().clear();\n//                }\n//            }\n            c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n        } else {\n            c.writeErrMessage(ErrorCode.ER_YES, \"Invalid Schema:\" + name);\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/KillConnection.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.net.NIOConnection;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.util.SplitUtil;\n\n/**\n * @author mycat\n */\npublic final class KillConnection {\n\n    private static final Logger logger = LoggerFactory.getLogger(KillConnection.class);\n\n    public static void response(String stmt, int offset, ManagerConnection mc) {\n        int count = 0;\n        List<FrontendConnection> list = getList(stmt, offset, mc);\n        if (list != null) {\n            for (NIOConnection c : list) {\n                StringBuilder s = new StringBuilder();\n                logger.warn(s.append(c).append(\"killed by manager\").toString());\n                c.close(\"kill by manager\");\n                count++;\n            }\n        }\n        OkPacket packet = new OkPacket();\n        packet.packetId = 1;\n        packet.affectedRows = count;\n        packet.serverStatus = 2;\n        packet.write(mc);\n    }\n\n    private static List<FrontendConnection> getList(String stmt, int offset, ManagerConnection mc) {\n        String ids = stmt.substring(offset).trim();\n        if (ids.length() > 0) {\n            String[] idList = SplitUtil.split(ids, ',', true);\n            List<FrontendConnection> fcList = new ArrayList<FrontendConnection>(idList.length);\n            NIOProcessor[] processors = MycatServer.getInstance().getProcessors();\n            for (String id : idList) {\n                long value = 0;\n                try {\n                    value = Long.parseLong(id);\n                } catch (NumberFormatException e) {\n                    continue;\n                }\n                FrontendConnection fc = null;\n                for (NIOProcessor p : processors) {\n                    if ((fc = p.getFrontends().get(value)) != null) {\n                        fcList.add(fc);\n                        break;\n                    }\n                }\n            }\n            return fcList;\n        }\n        return null;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/Offline.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport io.mycat.MycatServer;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.OkPacket;\n\n/**\n * @author mycat\n */\npublic class Offline {\n\n    private static final OkPacket ok = new OkPacket();\n    static {\n        ok.packetId = 1;\n        ok.affectedRows = 1;\n        ok.serverStatus = 2;\n    }\n\n    public static void execute(String stmt, ManagerConnection c) {\n        MycatServer.getInstance().offline();\n        ok.write(c);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/Online.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport io.mycat.MycatServer;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.OkPacket;\n\n/**\n * @author mycat\n */\npublic class Online {\n\n    private static final OkPacket ok = new OkPacket();\n    static {\n        ok.packetId = 1;\n        ok.affectedRows = 1;\n        ok.serverStatus = 2;\n    }\n\n    public static void execute(String stmt, ManagerConnection mc) {\n        MycatServer.getInstance().online();\n        ok.write(mc);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ReloadConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport com.google.common.util.concurrent.FutureCallback;\nimport com.google.common.util.concurrent.Futures;\nimport com.google.common.util.concurrent.ListenableFuture;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.jdbc.JDBCConnection;\nimport io.mycat.backend.mysql.nio.MySQLConnection;\nimport io.mycat.config.ConfigInitializer;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.MycatCluster;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.FirewallConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.UserConfig;\nimport io.mycat.config.util.DnPropertyUtil;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.mysql.OkPacket;\n\n/**\n * @author mycat\n * @author zhuam\n */\npublic final class ReloadConfig {\n\t\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(ReloadConfig.class);\n\n\tpublic static void execute(ManagerConnection c, final boolean loadAll) {\n\t\t\n\t\t// reload @@config_all 校验前一次的事务完成情况\n\t\tif ( loadAll && !NIOProcessor.backends_old.isEmpty() ) {\n\t\t\tc.writeErrMessage(ErrorCode.ER_YES, \"The are several unfinished db transactions before executing \\\"reload @@config_all\\\", therefore the execution is terminated for logical integrity and please try again later.\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tfinal ReentrantLock lock = MycatServer.getInstance().getConfig().getLock();\t\t\n\t\tlock.lock();\n\t\ttry {\n\t\t\tListenableFuture<Boolean> listenableFuture = MycatServer.getInstance().getListeningExecutorService().submit(\n\t\t\t\tnew Callable<Boolean>() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Boolean call() throws Exception {\n\t\t\t\t\t\treturn loadAll ? reload_all() : reload();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\t\t\tFutures.addCallback(listenableFuture, new ReloadCallBack(c), MycatServer.getInstance().getListeningExecutorService());\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\n\tpublic static boolean reload_all() {\n\t\t\n\t\t/**\n\t\t *  1、载入新的配置\n\t\t *  1.1、ConfigInitializer 初始化，基本自检\n\t\t *  1.2、DataNode/DataHost 实际链路检测\n\t\t */\n\t\tConfigInitializer loader = new ConfigInitializer(true);\n\t\tMap<String, UserConfig> newUsers = loader.getUsers();\n\t\tMap<String, SchemaConfig> newSchemas = loader.getSchemas();\n\t\tMap<String, PhysicalDBNode> newDataNodes = loader.getDataNodes();\n\t\tMap<String, PhysicalDBPool> newDataHosts = loader.getDataHosts();\n\t\tMycatCluster newCluster = loader.getCluster();\n\t\tFirewallConfig newFirewall = loader.getFirewall();\n\t\t\n\t\t/**\n\t\t * 1.2、实际链路检测\n\t\t */\n\t\tloader.testConnection();\n\n\t\t/**\n\t\t *  2、承接\n\t\t *  2.1、老的 dataSource 继续承接新建请求\n\t\t *  2.2、新的 dataSource 开始初始化， 完毕后交由 2.3\n\t\t *  2.3、新的 dataSource 开始承接新建请求\n\t\t *  2.4、老的 dataSource 内部的事务执行完毕， 相继关闭\n\t\t *  2.5、老的 dataSource 超过阀值的，强制关闭\n\t\t */\n\t\t\n\t\tMycatConfig config = MycatServer.getInstance().getConfig();\n\t\t\n\t\t/**\n\t\t * 2.1 、老的 dataSource 继续承接新建请求， 此处什么也不需要做\n\t\t */\n\t\t\n\t\tboolean isReloadStatusOK = true;\n\t\t\n\t\t/**\n\t\t * 2.2、新的 dataHosts 初始化\n\t\t */\n\t\tfor (PhysicalDBPool dbPool : newDataHosts.values()) {\t\t\t\t\t\n\t\t\tString hostName = dbPool.getHostName();\n\t\t\t\n\t\t\t// 设置 schemas\n\t\t\tArrayList<String> dnSchemas = new ArrayList<String>(30);\n\t\t\tfor (PhysicalDBNode dn : newDataNodes.values()) {\n\t\t\t\tif (dn.getDbPool().getHostName().equals(hostName)) {\n\t\t\t\t\tdnSchemas.add(dn.getDatabase());\n\t\t\t\t}\n\t\t\t}\n\t\t\tdbPool.setSchemas( dnSchemas.toArray(new String[dnSchemas.size()]) );\n\t\t\t\n\t\t\t// 获取 data host\n\t\t\tString dnIndex = DnPropertyUtil.loadDnIndexProps().getProperty(dbPool.getHostName(), \"0\");\n\t\t\tif ( !\"0\".equals(dnIndex) ) {\n\t\t\t\tLOGGER.info(\"init datahost: \" + dbPool.getHostName() + \"  to use datasource index:\" + dnIndex);\n\t\t\t}\t\t\t\n\t\t\t\n\t\t\tdbPool.init( Integer.valueOf(dnIndex) );\t\t\t\n\t\t\tif ( !dbPool.isInitSuccess() ) {\n\t\t\t\tisReloadStatusOK = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/**\n\t\t *  TODO： 确认初始化情况\n\t\t *    \n\t\t *  新的 dataHosts 是否初始化成功\n\t\t */\n\t\tif ( isReloadStatusOK ) {\n\n\t\t\tconfig.getSystem().setUseSqlStat(loader.getSystem().getUseSqlStat());\n\t\t\tMycatServer.getInstance().ensureSqlstatRecycleFuture();\n\t\t\t\n\t\t\t/**\n\t\t\t * 2.3、 在老的配置上，应用新的配置，开始准备承接任务\n\t\t\t */\n\t\t\tconfig.reload(newUsers, newSchemas, newDataNodes, newDataHosts, newCluster, newFirewall, true);\n\n\n\n\t\t\t/**\n\t\t\t * 2.4、 处理旧的资源\n\t\t\t */\n\t\t\tLOGGER.warn(\"1、clear old backend connection(size): \" + NIOProcessor.backends_old.size());\n\t\t\t\n\t\t\t// 清除前一次 reload 转移出去的 old Cons\n\t\t\tIterator<BackendConnection> iter = NIOProcessor.backends_old.iterator();\n\t\t\twhile( iter.hasNext() ) {\n\t\t\t\tBackendConnection con = iter.next();\n\t\t\t\tcon.close(\"clear old datasources\");\n\t\t\t\titer.remove();\t\n\t\t\t}\n\t\t\t\n\t\t\tMap<String, PhysicalDBPool> oldDataHosts = config.getBackupDataHosts();\n\t\t\tfor (PhysicalDBPool dbPool : oldDataHosts.values()) {\t\t\t\n\t\t\t\tdbPool.stopHeartbeat();\n\t\t\t\t\n\t\t\t\t// 提取数据源下的所有连接\n\t\t\t\tfor (PhysicalDatasource ds : dbPool.getAllDataSources()) {\t\t\t\t\t\n\t\t\t\t\t//\n\t\t\t\t\tfor (NIOProcessor processor : MycatServer.getInstance().getProcessors()) {\n\t\t\t\t\t\tfor (BackendConnection con : processor.getBackends().values()) {\n\t\t\t\t\t\t\tif (con instanceof MySQLConnection) {\n\t\t\t\t\t\t\t\tMySQLConnection mysqlCon = (MySQLConnection) con;\n\t\t\t\t\t\t\t\tif ( mysqlCon.getPool() == ds) {\n\t\t\t\t\t\t\t\t\tNIOProcessor.backends_old.add( con );\n\t\t\t\t\t\t\t\t}\n\n\t\t\t                } else if (con instanceof JDBCConnection) {\n\t\t\t                    JDBCConnection jdbcCon = (JDBCConnection) con;\n\t\t\t                    if (jdbcCon.getPool() == ds) {\n\t\t\t                    \tNIOProcessor.backends_old.add( con );\n\t\t\t                    }\n\t\t\t                }\n\t\t\t            }\n\t\t\t\t\t}\n\t\t\t\t}\t\t\t\t\n\t\t\t}\t\t\t\n\t\t\tLOGGER.warn(\"2、to be recycled old backend connection(size): \" + NIOProcessor.backends_old.size());\n\n\t\t\t//清理缓存\n\t\t\tMycatServer.getInstance().getCacheService().clearCache();\n\t\t\tMycatServer.getInstance().initRuleData();\n\t\t\treturn true;\n\t\t\t\n\t\t} else {\n\t\t\t// 如果重载不成功，则清理已初始化的资源。\n\t\t\tLOGGER.warn(\"reload failed, clear previously created datasources \");\n\t\t\tfor (PhysicalDBPool dbPool : newDataHosts.values()) {\n\t\t\t\tdbPool.clearDataSources(\"reload config\");\n\t\t\t\tdbPool.stopHeartbeat();\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n    public static boolean reload() {\n    \t\n    \t/**\n\t\t *  1、载入新的配置， ConfigInitializer 内部完成自检工作, 由于不更新数据源信息,此处不自检 dataHost  dataNode\n\t\t */\n        ConfigInitializer loader = new ConfigInitializer(false);\n        Map<String, UserConfig> users = loader.getUsers();\n        Map<String, SchemaConfig> schemas = loader.getSchemas();\n        Map<String, PhysicalDBNode> dataNodes = loader.getDataNodes();\n        Map<String, PhysicalDBPool> dataHosts = loader.getDataHosts();\n        MycatCluster cluster = loader.getCluster();\n        FirewallConfig firewall = loader.getFirewall();\n\n\n\t\tMycatServer.getInstance().getConfig().getSystem().setUseSqlStat(loader.getSystem().getUseSqlStat());\n\t\tMycatServer.getInstance().ensureSqlstatRecycleFuture();\n        \n        /**\n         * 2、在老的配置上，应用新的配置\n         */\n        MycatServer.getInstance().getConfig().reload(users, schemas, dataNodes, dataHosts, cluster, firewall, false);\n\n        /**\n         * 3、清理缓存\n         */\n        MycatServer.getInstance().getCacheService().clearCache();\n\t\tMycatServer.getInstance().initRuleData();\n        return true;\n    }\n    \n\t/**\n\t * 异步执行回调类，用于回写数据给用户等。\n\t */\n\tprivate static class ReloadCallBack implements FutureCallback<Boolean> {\n\n\t\tprivate ManagerConnection mc;\n\n\t\tprivate ReloadCallBack(ManagerConnection c) {\n\t\t\tthis.mc = c;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onSuccess(Boolean result) {\n\t\t\tif (result) {\n\t\t\t\tLOGGER.warn(\"send ok package to client \" + String.valueOf(mc));\n\t\t\t\tOkPacket ok = new OkPacket();\n\t\t\t\tok.packetId = 1;\n\t\t\t\tok.affectedRows = 1;\n\t\t\t\tok.serverStatus = 2;\n\t\t\t\tok.message = \"Reload config success\".getBytes();\n\t\t\t\tok.write(mc);\n\t\t\t} else {\n\t\t\t\tmc.writeErrMessage(ErrorCode.ER_YES, \"Reload config failure\");\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onFailure(Throwable t) {\n\t\t\tmc.writeErrMessage(ErrorCode.ER_YES, \"Reload config failure\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ReloadQueryCf.java",
    "content": "package io.mycat.manager.response;\r\n\r\n\r\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\r\n\r\nimport io.mycat.manager.ManagerConnection;\r\nimport io.mycat.net.mysql.OkPacket;\r\nimport io.mycat.statistic.stat.QueryConditionAnalyzer;\r\n\r\npublic class ReloadQueryCf {\r\n\t\r\n\tprivate static final Logger logger = LoggerFactory.getLogger(ReloadSqlSlowTime.class);\r\n\r\n    public static void execute(ManagerConnection c, String cf) {\r\n    \t\r\n    \tif ( cf == null ) {\r\n            cf = \"NULL\";\r\n        }\r\n    \t\r\n    \tQueryConditionAnalyzer.getInstance().setCf(cf);\r\n    \t\r\n        StringBuilder s = new StringBuilder();\r\n        s.append(c).append(\"Reset show  @@sql.condition=\"+ cf +\" success by manager\");\r\n        \r\n        logger.warn(s.toString());\r\n        \r\n        OkPacket ok = new OkPacket();\r\n        ok.packetId = 1;\r\n        ok.affectedRows = 1;\r\n        ok.serverStatus = 2;\r\n        ok.message = \"Reset show  @@sql.condition success\".getBytes();\r\n        ok.write(c);\r\n        \r\n        System.out.println(s.toString());\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ReloadSqlSlowTime.java",
    "content": "package io.mycat.manager.response;\r\n\r\nimport java.util.Map;\r\n\r\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\r\n\r\nimport io.mycat.manager.ManagerConnection;\r\nimport io.mycat.net.mysql.OkPacket;\r\nimport io.mycat.statistic.stat.UserStat;\r\nimport io.mycat.statistic.stat.UserStatAnalyzer;\r\n\r\npublic class ReloadSqlSlowTime {\r\n\tprivate static final Logger logger = LoggerFactory.getLogger(ReloadSqlSlowTime.class);\r\n\r\n    public static void execute(ManagerConnection c,long time) {\r\n    \t\r\n    \tMap<String, UserStat> statMap = UserStatAnalyzer.getInstance().getUserStatMap();\r\n        for (UserStat userStat : statMap.values()) {\r\n        \tuserStat.setSlowTime(time);\r\n        }\r\n    \t\r\n        StringBuilder s = new StringBuilder();\r\n        s.append(c).append(\"Reset show  @@sql.slow=\"+time+\" time success by manager\");\r\n        \r\n        logger.warn(s.toString());\r\n        \r\n        OkPacket ok = new OkPacket();\r\n        ok.packetId = 1;\r\n        ok.affectedRows = 1;\r\n        ok.serverStatus = 2;\r\n        ok.message = \"Reset show  @@sql.slow time success\".getBytes();\r\n        ok.write(c);\r\n        System.out.println(s.toString());\r\n    }\r\n\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ReloadSqlStat.java",
    "content": "package io.mycat.manager.response;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport io.mycat.MycatServer;\r\nimport io.mycat.config.ErrorCode;\r\nimport io.mycat.config.model.SystemConfig;\r\nimport io.mycat.manager.ManagerConnection;\r\nimport io.mycat.net.mysql.OkPacket;\r\n\r\npublic class ReloadSqlStat {\r\n\r\n    private static final Logger logger = LoggerFactory.getLogger(ReloadSqlStat.class);\r\n\r\n    public static void execute(ManagerConnection c, String openCloseFlag) {\r\n        SystemConfig system = MycatServer.getInstance().getConfig().getSystem();\r\n        int oldStat = system.getUseSqlStat();\r\n        int newStat = oldStat;\r\n        if (\"open\".equalsIgnoreCase(openCloseFlag)) {\r\n            newStat = 1;\r\n        } else if (\"close\".equalsIgnoreCase(openCloseFlag)) {\r\n            newStat = 0;\r\n        } else {\r\n            c.writeErrMessage(ErrorCode.ER_YES, \"reload @@sqlstat=open|close\");\r\n            return;\r\n        }\r\n\r\n        system.setUseSqlStat(newStat);\r\n        MycatServer.getInstance().ensureSqlstatRecycleFuture();\r\n\r\n        StringBuilder s = new StringBuilder();\r\n        s.append(c).append(\"Reset  @@sqlstat=\" + openCloseFlag + \" success by manager\");\r\n\r\n        logger.warn(s.toString());\r\n\r\n        OkPacket ok = new OkPacket();\r\n        ok.packetId = 1;\r\n        ok.affectedRows = oldStat != newStat ? 1 : 0;\r\n        ok.serverStatus = 2;\r\n        ok.message = \"Reset  @@sqlstat success\".getBytes();\r\n        ok.write(c);\r\n        System.out.println(s.toString());\r\n    }\r\n\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ReloadUser.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.OkPacket;\n\n/**\n * @author mycat\n */\npublic final class ReloadUser {\n\n    private static final Logger logger = LoggerFactory.getLogger(ReloadUser.class);\n\n    public static void execute(ManagerConnection c) {\n        boolean status = false;\n        if (status) {\n            StringBuilder s = new StringBuilder();\n            s.append(c).append(\"Reload userConfig success by manager\");\n            logger.warn(s.toString());\n            OkPacket ok = new OkPacket();\n            ok.packetId = 1;\n            ok.affectedRows = 1;\n            ok.serverStatus = 2;\n            ok.message = \"Reload userConfig success\".getBytes();\n            ok.write(c);\n        } else {\n            c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ReloadUserStat.java",
    "content": "package io.mycat.manager.response;\n\nimport java.util.Map;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.statistic.stat.UserStat;\nimport io.mycat.statistic.stat.UserStatAnalyzer;\n\npublic final class ReloadUserStat {\n\t\n\tprivate static final Logger logger = LoggerFactory.getLogger(ReloadUserStat.class);\n\n    public static void execute(ManagerConnection c) {\n    \t\n    \tMap<String, UserStat> statMap = UserStatAnalyzer.getInstance().getUserStatMap();\n        for (UserStat userStat : statMap.values()) {\n        \tuserStat.reset();\n        }\n    \t\n        StringBuilder s = new StringBuilder();\n        s.append(c).append(\"Reset show @@sql  @@sql.sum  @@sql.slow success by manager\");\n        \n        logger.warn(s.toString());\n        \n        OkPacket ok = new OkPacket();\n        ok.packetId = 1;\n        ok.affectedRows = 1;\n        ok.serverStatus = 2;\n        ok.message = \"Reset show @@sql  @@sql.sum @@sql.slow success\".getBytes();\n        ok.write(c);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ReloadZktoXml.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.StringUtil;\n\n/**\n * 进行reload_zk操作的响应\n * \n * @author mycat\n * @author mycat\n */\npublic final class ReloadZktoXml {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"STATEMENT\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c, String rsp) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c, true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c, true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c, true);\n\n        // write rows\n        byte packetId = eof.packetId;\n\n        RowDataPacket row = getRow(rsp, c.getCharset());\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c, true);\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c, true);\n\n        // post write\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(String stmt, String charset) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(StringUtil.encode(stmt, charset));\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/RollbackConfig.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.util.Map;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.MycatCluster;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.FirewallConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.UserConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.OkPacket;\n\n/**\n * @author mycat\n */\npublic final class RollbackConfig {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(RollbackConfig.class);\n\n\tpublic static void execute(ManagerConnection c) {\n\t\tfinal ReentrantLock lock = MycatServer.getInstance().getConfig()\n\t\t\t\t.getLock();\n\t\tlock.lock();\n\t\ttry {\n\t\t\tif (rollback()) {\n\t\t\t\tStringBuilder s = new StringBuilder();\n\t\t\t\ts.append(c).append(\"Rollback config success by manager\");\n\t\t\t\tLOGGER.warn(s.toString());\n\t\t\t\tOkPacket ok = new OkPacket();\n\t\t\t\tok.packetId = 1;\n\t\t\t\tok.affectedRows = 1;\n\t\t\t\tok.serverStatus = 2;\n\t\t\t\tok.message = \"Rollback config success\".getBytes();\n\t\t\t\tok.write(c);\n\t\t\t} else {\n\t\t\t\tc.writeErrMessage(ErrorCode.ER_YES, \"Rollback config failure\");\n\t\t\t}\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t}\n\n\tprivate static boolean rollback() {\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\tMap<String, UserConfig> users = conf.getBackupUsers();\n\t\tMap<String, SchemaConfig> schemas = conf.getBackupSchemas();\n\t\tMap<String, PhysicalDBNode> dataNodes = conf.getBackupDataNodes();\n\t\tMap<String, PhysicalDBPool> dataHosts = conf.getBackupDataHosts();\n\t\tMycatCluster cluster = conf.getBackupCluster();\n\t\tFirewallConfig firewall = conf.getBackupFirewall();\n\n\t\t// 检查可回滚状态\n\t\tif (!conf.canRollback()) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// 如果回滚已经存在的pool\n\t\tboolean rollbackStatus = true;\n\t\tMap<String, PhysicalDBPool> cNodes = conf.getDataHosts();\n\t\tfor (PhysicalDBPool dn : dataHosts.values()) {\n\t\t\tdn.init(dn.getActivedIndex());\n\t\t\tif (!dn.isInitSuccess()) {\n\t\t\t\trollbackStatus = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t// 如果回滚不成功，则清理已初始化的资源。\n\t\tif (!rollbackStatus) {\n\t\t\tfor (PhysicalDBPool dn : dataHosts.values()) {\n\t\t\t\tdn.clearDataSources(\"rollbackup config\");\n\t\t\t\tdn.stopHeartbeat();\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// 应用回滚\n\t\tconf.rollback(users, schemas, dataNodes, dataHosts, cluster, firewall);\n\n\t\t// 处理旧的资源\n\t\tfor (PhysicalDBPool dn : cNodes.values()) {\n\t\t\tdn.clearDataSources(\"clear old config \");\n\t\t\tdn.stopHeartbeat();\n\t\t}\n\n\t\t//清理缓存\n\t\t MycatServer.getInstance().getCacheService().clearCache();\n\t\treturn true;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/RollbackUser.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.OkPacket;\n\n/**\n * @author mycat\n */\npublic final class RollbackUser {\n\n    private static final Logger logger = LoggerFactory.getLogger(RollbackUser.class);\n\n    public static void execute(ManagerConnection c) {\n        boolean status = false;\n        if (status) {\n            StringBuilder s = new StringBuilder();\n            s.append(c).append(\"Rollback user success by manager\");\n            logger.warn(s.toString());\n            OkPacket ok = new OkPacket();\n            ok.packetId = 1;\n            ok.affectedRows = 1;\n            ok.serverStatus = 2;\n            ok.message = \"Rollback user success\".getBytes();\n            ok.write(c);\n        } else {\n            c.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement\");\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/SelectSessionAutoIncrement.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.LongUtil;\n\n/**\n * @author mycat\n */\npublic final class SelectSessionAutoIncrement {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"SESSION.AUTOINCREMENT\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.packetId = ++packetId;\n        row.add(LongUtil.toBytes(1));\n        buffer = row.write(buffer, c,true);\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/SelectSessionTxReadOnly.java",
    "content": "package io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.LongUtil;\n\npublic final class SelectSessionTxReadOnly {\n\t\n\tprivate static final String SESSION_TX_READ_ONLY = \"@@SESSION.TX_READ_ONLY\";\n\tprivate static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(SESSION_TX_READ_ONLY, Fields.FIELD_TYPE_INT24);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.packetId = ++packetId;\n        row.add(LongUtil.toBytes(0));\n        buffer = row.write(buffer, c,true);\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/SelectVersionComment.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\n\n/**\n * @author mycat\n */\npublic final class SelectVersionComment {\n\n    private static final byte[] VERSION_COMMENT = \"MyCat Server (monitor)\".getBytes();\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"@@VERSION_COMMENT\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(VERSION_COMMENT);\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowBackend.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.jdbc.JDBCConnection;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.backend.mysql.nio.MySQLConnection;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.BackendAIOConnection;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.IntegerUtil;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\nimport io.mycat.util.TimeUtil;\n\n/**\n * 查询后端连接\n * \n * @author mycat\n */\npublic class ShowBackend {\n\n\tprivate static final int FIELD_COUNT = 17;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil\n\t\t\t.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"processor\",\n\t\t\t\tFields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"id\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"mysqlId\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"host\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"port\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"l_port\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"net_in\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"net_out\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"life\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"closed\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t// fields[i] = PacketUtil.getField(\"run\", Fields.FIELD_TYPE_VAR_STRING);\n\t\t// fields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"borrowed\",\n\t\t\t\tFields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"SEND_QUEUE\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"schema\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil\n\t\t\t\t.getField(\"charset\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil\n\t\t\t\t.getField(\"txlevel\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"autocommit\",\n\t\t\t\tFields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"tx_readonly\",\n\t\t\t\tFields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void execute(ManagerConnection c) {\n\t\tByteBuffer buffer = c.allocate();\n\t\tbuffer = header.write(buffer, c, true);\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c, true);\n\t\t}\n\t\tbuffer = eof.write(buffer, c, true);\n\t\tbyte packetId = eof.packetId;\n\t\tString charset = c.getCharset();\n\t\tfor (NIOProcessor p : MycatServer.getInstance().getProcessors()) {\n\t\t\tfor (BackendConnection bc : p.getBackends().values()) {\n\t\t\t\tif (bc != null) {\n\t\t\t\t\tRowDataPacket row = getRow(bc, charset);\n\t\t\t\t\trow.packetId = ++packetId;\n\t\t\t\t\tbuffer = row.write(buffer, c, true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c, true);\n\t\tc.write(buffer);\n\t}\n\n\tprivate static RowDataPacket getRow(BackendConnection c, String charset) {\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\tif (c instanceof BackendAIOConnection) {\n\t\t\trow.add(((BackendAIOConnection) c).getProcessor().getName()\n\t\t\t\t\t.getBytes());\n\t\t} else if(c instanceof JDBCConnection){\n\t\t    row.add(((JDBCConnection)c).getProcessor().getName().getBytes());\n\t\t}else{\n\t\t    row.add(\"N/A\".getBytes());\n\t\t}\n\t\trow.add(LongUtil.toBytes(c.getId()));\n\t\tlong threadId = 0;\n\t\tif (c instanceof MySQLConnection) {\n\t\t\tthreadId = ((MySQLConnection) c).getThreadId();\n\t\t}\n\t\trow.add(LongUtil.toBytes(threadId));\n\t\trow.add(StringUtil.encode(c.getHost(), charset));\n\t\trow.add(IntegerUtil.toBytes(c.getPort()));\n\t\trow.add(IntegerUtil.toBytes(c.getLocalPort()));\n\t\trow.add(LongUtil.toBytes(c.getNetInBytes()));\n\t\trow.add(LongUtil.toBytes(c.getNetOutBytes()));\n\t\trow.add(LongUtil.toBytes((TimeUtil.currentTimeMillis() - c\n\t\t\t\t.getStartupTime()) / 1000L));\n\t\trow.add(c.isClosed() ? \"true\".getBytes() : \"false\".getBytes());\n\t\t// boolean isRunning = c.isRunning();\n\t\t// row.add(isRunning ? \"true\".getBytes() : \"false\".getBytes());\n\t\tboolean isBorrowed = c.isBorrowed();\n\t\trow.add(isBorrowed ? \"true\".getBytes() : \"false\".getBytes());\n\t\tint writeQueueSize = 0;\n\t\tString schema = \"\";\n\t\tString charsetInf = \"\";\n\t\tString txLevel = \"\";\n\t\tString txAutommit = \"\";\n\t\tString txReadonly = \"\";\n\n\t\tif (c instanceof MySQLConnection) {\n\t\t\tMySQLConnection mysqlC = (MySQLConnection) c;\n\t\t\twriteQueueSize = mysqlC.getWriteQueue().size();\n\t\t\tschema = mysqlC.getSchema();\n\t\t\tcharsetInf = mysqlC.getCharset() + \":\" + mysqlC.getCharsetIndex();\n\t\t\ttxLevel = mysqlC.getTxIsolation() + \"\";\n\t\t\ttxAutommit = mysqlC.isAutocommit() + \"\";\n\t\t\ttxReadonly = mysqlC.isTxReadonly() + \"\";\n\t\t}\n\t\trow.add(IntegerUtil.toBytes(writeQueueSize));\n\t\trow.add(schema.getBytes());\n\t\trow.add(charsetInf.getBytes());\n\t\trow.add(txLevel.getBytes());\n\t\trow.add(txAutommit.getBytes());\n\t\trow.add(txReadonly.getBytes());\n\t\treturn row;\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowBackendOld.java",
    "content": "package io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.backend.mysql.nio.MySQLConnection;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.IntegerUtil;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\nimport io.mycat.util.TimeUtil;\n\n/**\n * 查询 reload @@config_all 后产生的后端连接（待回收）\n * \n * @author zhuam\n */\npublic class ShowBackendOld {\n\t\n\tprivate static final int FIELD_COUNT = 10;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\t\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"id\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"mysqlId\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"host\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"port\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"l_port\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"net_in\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"net_out\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"life\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"lasttime\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"borrowed\",Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void execute(ManagerConnection c) {\n\t\tByteBuffer buffer = c.allocate();\n\t\tbuffer = header.write(buffer, c, true);\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c, true);\n\t\t}\n\t\tbuffer = eof.write(buffer, c, true);\n\t\tbyte packetId = eof.packetId;\n\t\tString charset = c.getCharset();\n\t\t\n\t\tfor (BackendConnection bc : NIOProcessor.backends_old) {\n\t\t\tif ( bc != null) {\n\t\t\t\tRowDataPacket row = getRow(bc, charset);\n\t\t\t\trow.packetId = ++packetId;\n\t\t\t\tbuffer = row.write(buffer, c, true);\n\t\t\t}\n\t\t}\n\t\t\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c, true);\n\t\tc.write(buffer);\n\t}\n\n\tprivate static RowDataPacket getRow(BackendConnection c, String charset) {\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(LongUtil.toBytes(c.getId()));\n\t\tlong threadId = 0;\n\t\tif (c instanceof MySQLConnection) {\n\t\t\tthreadId = ((MySQLConnection) c).getThreadId();\n\t\t}\n\t\trow.add(LongUtil.toBytes(threadId));\n\t\trow.add(StringUtil.encode(c.getHost(), charset));\n\t\trow.add(IntegerUtil.toBytes(c.getPort()));\n\t\trow.add(IntegerUtil.toBytes(c.getLocalPort()));\n\t\trow.add(LongUtil.toBytes(c.getNetInBytes()));\n\t\trow.add(LongUtil.toBytes(c.getNetOutBytes()));\n\t\trow.add(LongUtil.toBytes((TimeUtil.currentTimeMillis() - c.getStartupTime()) / 1000L));\n\t\trow.add(LongUtil.toBytes( c.getLastTime() ));\n\t\tboolean isBorrowed = c.isBorrowed();\n\t\trow.add(isBorrowed ? \"true\".getBytes() : \"false\".getBytes());\t\n\t\treturn row;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowCollation.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.IntegerUtil;\nimport io.mycat.util.LongUtil;\n\n/**\n * @author mycat\n * @author mycat\n */\npublic final class ShowCollation {\n\n    private static final int FIELD_COUNT = 6;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"COLLATION\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"CHARSET\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"ID\", Fields.FIELD_TYPE_LONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"DEFAULT\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"COMPILED\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"SORTLEN\", Fields.FIELD_TYPE_LONG);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        RowDataPacket row = getRow(c.getCharset());\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n\n        // write lastEof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(String charset) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(\"utf8_general_ci\".getBytes());\n        row.add(\"utf8\".getBytes());\n        row.add(IntegerUtil.toBytes(33));\n        row.add(\"Yes\".getBytes());\n        row.add(\"Yes\".getBytes());\n        row.add(LongUtil.toBytes(1));\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowCommand.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.statistic.CommandCount;\nimport io.mycat.util.LongUtil;\n\n/**\n * 统计各类数据包的执行次数\n * \n * @author mycat\n * @author mycat\n */\npublic final class ShowCommand {\n\n    private static final int FIELD_COUNT = 10;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"PROCESSOR\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"INIT_DB\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"QUERY\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"STMT_PREPARE\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"STMT_EXECUTE\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"STMT_CLOSE\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"PING\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"KILL\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"QUIT\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"OTHER\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        for (NIOProcessor p : MycatServer.getInstance().getProcessors()) {\n            RowDataPacket row = getRow(p, c.getCharset());\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n        }\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(NIOProcessor processor, String charset) {\n        CommandCount cc = processor.getCommands();\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(processor.getName().getBytes());\n        row.add(LongUtil.toBytes(cc.initDBCount()));\n        row.add(LongUtil.toBytes(cc.queryCount()));\n        row.add(LongUtil.toBytes(cc.stmtPrepareCount()));\n        row.add(LongUtil.toBytes(cc.stmtExecuteCount()));\n        row.add(LongUtil.toBytes(cc.stmtCloseCount()));\n        row.add(LongUtil.toBytes(cc.pingCount()));\n        row.add(LongUtil.toBytes(cc.killCount()));\n        row.add(LongUtil.toBytes(cc.quitCount()));\n        row.add(LongUtil.toBytes(cc.otherCount()));\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowConnection.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.IntegerUtil;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\nimport io.mycat.util.TimeUtil;\n\n/**\n * 查看当前有效连接信息\n * \n * @author mycat\n * @author mycat\n */\npublic final class ShowConnection {\n\n\tprivate static final int FIELD_COUNT = 15;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil\n\t\t\t.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"PROCESSOR\",\n\t\t\t\tFields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"ID\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"HOST\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"PORT\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"LOCAL_PORT\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"USER\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"SCHEMA\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil\n\t\t\t\t.getField(\"CHARSET\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"NET_IN\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"NET_OUT\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"ALIVE_TIME(S)\",\n\t\t\t\tFields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"RECV_BUFFER\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"SEND_QUEUE\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil\n\t\t\t\t.getField(\"txlevel\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"autocommit\",\n\t\t\t\tFields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void execute(ManagerConnection c) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c, true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c, true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c, true);\n\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\tString charset = c.getCharset();\n\t\tNIOProcessor[] processors = MycatServer.getInstance().getProcessors();\n\t\tfor (NIOProcessor p : processors) {\n\t\t\tfor (FrontendConnection fc : p.getFrontends().values()) {\n\t\t\t\tif (fc != null) {\n\t\t\t\t\tRowDataPacket row = getRow(fc, charset);\n\t\t\t\t\trow.packetId = ++packetId;\n\t\t\t\t\tbuffer = row.write(buffer, c, true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c, true);\n\n\t\t// write buffer\n\t\tc.write(buffer);\n\t}\n\n\tprivate static RowDataPacket getRow(FrontendConnection c, String charset) {\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(c.getProcessor().getName().getBytes());\n\t\trow.add(LongUtil.toBytes(c.getId()));\n\t\trow.add(StringUtil.encode(c.getHost(), charset));\n\t\trow.add(IntegerUtil.toBytes(c.getPort()));\n\t\trow.add(IntegerUtil.toBytes(c.getLocalPort()));\n\t\trow.add(StringUtil.encode(c.getUser(), charset));\n\t\trow.add(StringUtil.encode(c.getSchema(), charset));\n\t\trow.add(StringUtil.encode(c.getCharset()+\":\"+c.getCharsetIndex(), charset));\n\t\trow.add(LongUtil.toBytes(c.getNetInBytes()));\n\t\trow.add(LongUtil.toBytes(c.getNetOutBytes()));\n\t\trow.add(LongUtil.toBytes((TimeUtil.currentTimeMillis() - c.getStartupTime()) / 1000L));\n\t\tByteBuffer bb = c.getReadBuffer();\n\t\trow.add(IntegerUtil.toBytes(bb == null ? 0 : bb.capacity()));\n\t\trow.add(IntegerUtil.toBytes(c.getWriteQueue().size()));\n\n\t\tString txLevel = \"\";\n\t\tString txAutommit = \"\";\n\t\tif (c instanceof ServerConnection) {\n\t\t\tServerConnection mysqlC = (ServerConnection) c;\n\t\t\ttxLevel = mysqlC.getTxIsolation() + \"\";\n\t\t\ttxAutommit = mysqlC.isAutocommit() + \"\";\n\t\t}\n\t\trow.add(txLevel.getBytes());\n\t\trow.add(txAutommit.getBytes());\n\n\t\treturn row;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowConnectionSQL.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\nimport io.mycat.util.TimeUtil;\n\n/**\n * @author mycat\n */\npublic final class ShowConnectionSQL {\n\n    private static final int FIELD_COUNT = 7;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"ID\", Fields.FIELD_TYPE_LONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"HOST\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"USER\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"SCHEMA\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"START_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"EXECUTE_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"SQL\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        String charset = c.getCharset();\n        for (NIOProcessor p : MycatServer.getInstance().getProcessors()) {\n            for (FrontendConnection fc : p.getFrontends().values()) {\n                if (!fc.isClosed()) {\n                \tif(fc.getExecuteSql()==null){\n                \t\tcontinue;\n                \t}\n                \tif(fc instanceof ServerConnection){\n                \t\tRowDataPacket row = getRow(fc, charset);\n                        row.packetId = ++packetId;\n                        buffer = row.write(buffer, c,true);\n                \t}\n                }\n            }\n        }\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(FrontendConnection c, String charset) {\n    \tString executeSql = c.getExecuteSql();\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(LongUtil.toBytes(c.getId()));\n        row.add(StringUtil.encode(c.getHost(), charset));\n        row.add(StringUtil.encode(c.getUser(), charset));\n        row.add(StringUtil.encode(c.getSchema(), charset));\n        row.add(LongUtil.toBytes(c.getLastReadTime()));\n        long rt = c.getLastReadTime();\n        long wt = c.getLastWriteTime();\n        row.add(LongUtil.toBytes(executeSql==null?0:((wt > rt) ? (wt - rt) : (TimeUtil.currentTimeMillis() - rt))));\n        row.add(StringUtil.encode(executeSql==null?\"\":executeSql, charset) );\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowDataNode.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.MycatPrivileges;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.parser.util.Pair;\nimport io.mycat.route.parser.util.PairUtil;\nimport io.mycat.util.IntegerUtil;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\nimport io.mycat.util.TimeUtil;\n\n/**\n * 查看数据节点信息\n * \n * @author mycat\n * @author mycat\n */\npublic final class ShowDataNode {\n\n\tprivate static final NumberFormat nf = DecimalFormat.getInstance();\n\tprivate static final int FIELD_COUNT = 12;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil\n\t\t\t.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tstatic {\n\t\tnf.setMaximumFractionDigits(3);\n\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"NAME\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil\n\t\t\t\t.getField(\"DATHOST\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"INDEX\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"TYPE\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"ACTIVE\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"IDLE\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"SIZE\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"EXECUTE\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"TOTAL_TIME\", Fields.FIELD_TYPE_DOUBLE);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"MAX_TIME\", Fields.FIELD_TYPE_DOUBLE);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"MAX_SQL\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"RECOVERY_TIME\",\n\t\t\t\tFields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void execute(ManagerConnection c, String name) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c, true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c, true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c, true);\n\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\tMap<String, PhysicalDBNode> dataNodes = conf.getDataNodes();\n\t\tList<String> keys = new ArrayList<String>();\n\t\tif (StringUtil.isEmpty(name)) {\n\t\t\tfor(String key : dataNodes.keySet()){\n\t\t\t\tMycatPrivileges myCatprivileges = (MycatPrivileges)( c.getPrivileges());\n\t\t\t\tif(myCatprivileges.checkDataNodeDmlPrivilege(c.getUser(), key, c.getExecuteSql())) {\n\t\t\t\t\tkeys.add(key);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tSchemaConfig sc = conf.getSchemas().get(name);\n\t\t\tif (null != sc) {\n\t\t\t\tkeys.addAll(sc.getAllDataNodes());\n\t\t\t}\n\t\t}\n\t\tCollections.sort(keys, new Comparators<String>());\n\t\tfor (String key : keys) {\n\t\t\tRowDataPacket row = getRow(dataNodes.get(key), c.getCharset());\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c, true);\n\t\t}\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c, true);\n\n\t\t// post write\n\t\tc.write(buffer);\n\t}\n\n\tprivate static RowDataPacket getRow(PhysicalDBNode node, String charset) {\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(StringUtil.encode(node.getName(), charset));\n\t\trow.add(StringUtil.encode(\n\t\t\t\tnode.getDbPool().getHostName() + '/' + node.getDatabase(),\n\t\t\t\tcharset));\n\t\tPhysicalDBPool pool = node.getDbPool();\n\t\tPhysicalDatasource ds = pool.getSource();\n\t\tif (ds != null) {\n\t\t\tint active = ds.getActiveCountForSchema(node.getDatabase());\n\t\t\tint idle = ds.getIdleCountForSchema(node.getDatabase());\n\t\t\trow.add(IntegerUtil.toBytes(pool.getActivedIndex()));\n\t\t\trow.add(StringUtil.encode(ds.getConfig().getDbType(), charset));\n\t\t\trow.add(IntegerUtil.toBytes(active));\n\t\t\trow.add(IntegerUtil.toBytes(idle));\n\t\t\trow.add(IntegerUtil.toBytes(ds.getSize()));\n\t\t} else {\n\t\t\trow.add(null);\n\t\t\trow.add(null);\n\t\t\trow.add(null);\n\t\t\trow.add(null);\n\t\t\trow.add(null);\n\t\t}\n\t\trow.add(LongUtil.toBytes(ds.getExecuteCountForSchema(node.getDatabase())));\n\t\trow.add(StringUtil.encode(nf.format(0), charset));\n\t\trow.add(StringUtil.encode(nf.format(0), charset));\n\t\trow.add(LongUtil.toBytes(0));\n\t\tlong recoveryTime = pool.getSource().getHeartbeatRecoveryTime()\n\t\t\t\t- TimeUtil.currentTimeMillis();\n\t\trow.add(LongUtil.toBytes(recoveryTime > 0 ? recoveryTime / 1000L : -1L));\n\t\treturn row;\n\t}\n\n\tprivate static final class Comparators<T> implements Comparator<String> {\n\t\t@Override\n\t\tpublic int compare(String s1, String s2) {\n\t\t\tPair<String, Integer> p1 = PairUtil.splitIndex(s1, '[', ']');\n\t\t\tPair<String, Integer> p2 = PairUtil.splitIndex(s2, '[', ']');\n\t\t\tif (p1.getKey().compareTo(p2.getKey()) == 0) {\n\t\t\t\treturn p1.getValue() - p2.getValue();\n\t\t\t} else {\n\t\t\t\treturn p1.getKey().compareTo(p2.getKey());\n\t\t\t}\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowDataSource.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.IntegerUtil;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * 查看数据源信息\n * \n * @author mycat\n * @author mycat\n */\npublic final class ShowDataSource {\n\n\tprivate static final int FIELD_COUNT = 12;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil\n\t\t\t.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"DATANODE\",\n\t\t\t\tFields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"NAME\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"TYPE\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"HOST\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"PORT\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"W/R\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"ACTIVE\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"IDLE\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"SIZE\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"EXECUTE\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"READ_LOAD\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"WRITE_LOAD\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void execute(ManagerConnection c, String name) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c,true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c,true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c,true);\n\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\tMap<String, List<PhysicalDatasource>> dataSources = new HashMap<String, List<PhysicalDatasource>>();\n\t\tif (null != name) {\n\t\t\tPhysicalDBNode dn = conf.getDataNodes().get(name);\n\t\t\tif (dn != null) {\n\t\t\t\tList<PhysicalDatasource> dslst = new LinkedList<PhysicalDatasource>();\n\t\t\t\tdslst.addAll(dn.getDbPool().getAllDataSources());\n\t\t\t\tdataSources.put(dn.getName(), dslst);\n\t\t\t}\n\n\t\t} else {\n\t\t\t// add all\n\n\t\t\tfor (PhysicalDBNode dn : conf.getDataNodes().values()) {\n\t\t\t\tList<PhysicalDatasource> dslst = new LinkedList<PhysicalDatasource>();\n\t\t\t\tdslst.addAll(dn.getDbPool().getAllDataSources());\n\t\t\t\tdataSources.put(dn.getName(), dslst);\n\t\t\t}\n\n\t\t}\n\n\t\tfor (Map.Entry<String, List<PhysicalDatasource>> dsEntry : dataSources\n\t\t\t\t.entrySet()) {\n\t\t\tString dnName = dsEntry.getKey();\n\t\t\tfor (PhysicalDatasource ds : dsEntry.getValue()) {\n\t\t\t\tRowDataPacket row = getRow(dnName, ds, c.getCharset());\n\t\t\t\trow.packetId = ++packetId;\n\t\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\t}\n\t\t}\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c,true);\n\n\t\t// post write\n\t\tc.write(buffer);\n\t}\n\n\tprivate static RowDataPacket getRow(String dataNode, PhysicalDatasource ds,\n\t\t\tString charset) {\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(StringUtil.encode(dataNode, charset));\n\t\trow.add(StringUtil.encode(ds.getName(), charset));\n\t\trow.add(StringUtil.encode(ds.getConfig().getDbType(), charset));\n\t\trow.add(StringUtil.encode(ds.getConfig().getIp(), charset));\n\t\trow.add(IntegerUtil.toBytes(ds.getConfig().getPort()));\n\t\trow.add(StringUtil.encode(ds.isReadNode() ? \"R\" : \"W\", charset));\n\t\trow.add(IntegerUtil.toBytes(ds.getActiveCount()));\n\t\trow.add(IntegerUtil.toBytes(ds.getIdleCount()));\n\t\trow.add(IntegerUtil.toBytes(ds.getSize()));\n\t\trow.add(LongUtil.toBytes(ds.getExecuteCount()));\n\t\trow.add(LongUtil.toBytes(ds.getReadCount()));\n\t\trow.add(LongUtil.toBytes(ds.getWriteCount()));\n\t\treturn row;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowDatabase.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.Map;\nimport java.util.TreeSet;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.StringUtil;\n\n/**\n * 查看schema信息\n * \n * @author mycat\n * @author mycat\n */\npublic final class ShowDatabase {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"DATABASE\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        Map<String, SchemaConfig> schemas = MycatServer.getInstance().getConfig().getSchemas();\n        for (String name : new TreeSet<String>(schemas.keySet())) {\n            RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n            row.add(StringUtil.encode(name, c.getCharset()));\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n        }\n\n        // write lastEof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowDatasourceCluster.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.text.SimpleDateFormat;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.heartbeat.DBHeartbeat;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.statistic.DataSourceSyncRecorder;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n\n/**\n * @author songwie\n */\npublic class ShowDatasourceCluster {\n\n\tprivate static final int FIELD_COUNT = 17;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tprivate static final SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n\n\t/*private static final String[] MYSQL_CLUSTER_STAUTS_COLMS = new String[] {\n\t\"wsrep_incoming_addresses\",\"wsrep_cluster_size\",\"wsrep_cluster_status\", \"wsrep_connected\", \"wsrep_flow_control_paused\",\n\t\"wsrep_local_state_comment\",\"wsrep_ready\",\"wsrep_flow_control_paused_ns\",\"wsrep_flow_control_recv\",\"wsrep_local_bf_aborts\", \n\t\"wsrep_local_recv_queue_avg\",\"wsrep_local_send_queue_avg\",\"wsrep_apply_oool\",\"wsrep_apply_oooe\"};*/\n\n\t\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"name\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"host\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"port\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"wsrep_incoming_addresses\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"wsrep_cluster_size\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"wsrep_cluster_status\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"wsrep_connected\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"wsrep_flow_control_paused\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"wsrep_local_state_comment\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"wsrep_ready\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"wsrep_flow_control_paused_ns\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"wsrep_flow_control_recv\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"wsrep_local_bf_aborts\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"wsrep_local_recv_queue_avg\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"wsrep_local_send_queue_avg\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"wsrep_apply_oool\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"wsrep_apply_oooe\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void response(ManagerConnection c,String stmt) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c,true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c,true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c,true);\n\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\t\n\t\tfor (RowDataPacket row : getRows(c.getCharset())) {\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c,true);\n\t\t}\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c,true);\n\n\t\t// post write\n\t\tc.write(buffer);\n\t}\n \n\tprivate static List<RowDataPacket> getRows(String charset) {\n\t\tList<RowDataPacket> list = new LinkedList<RowDataPacket>();\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\t// host nodes\n\t\tMap<String, PhysicalDBPool> dataHosts = conf.getDataHosts();\n\t\tfor (PhysicalDBPool pool : dataHosts.values()) {\n\t\t\tfor (PhysicalDatasource ds : pool.getAllDataSources()) {\n\t\t\t\tDBHeartbeat hb = ds.getHeartbeat();\n\t\t\t\tDataSourceSyncRecorder record = hb.getAsynRecorder();\n\t\t\t\tMap<String, String> states = record.getRecords();\n\t\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\t\tif(!states.isEmpty()){\n\t\t\t\t\trow.add(StringUtil.encode(ds.getName(),charset));\n\t\t\t\t\trow.add(StringUtil.encode(ds.getConfig().getIp(),charset));\n\t\t\t\t\trow.add(LongUtil.toBytes(ds.getConfig().getPort()));\n\t\t\t\t\t\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_incoming_addresses\")==null?\"\":states.get(\"wsrep_incoming_addresses\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_cluster_size\")==null?\"\":states.get(\"wsrep_cluster_size\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_cluster_status\")==null?\"\":states.get(\"wsrep_cluster_status\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_connected\")==null?\"\":states.get(\"wsrep_connected\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_flow_control_paused\")==null?\"\":states.get(\"wsrep_flow_control_paused\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_local_state_comment\")==null?\"\":states.get(\"wsrep_local_state_comment\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_ready\")==null?\"\":states.get(\"wsrep_ready\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_flow_control_paused_ns\")==null?\"\":states.get(\"wsrep_flow_control_paused_ns\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_flow_control_recv\")==null?\"\":states.get(\"wsrep_flow_control_recv\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_local_bf_aborts\")==null?\"\":states.get(\"wsrep_local_bf_aborts\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_local_recv_queue_avg\")==null?\"\":states.get(\"wsrep_local_recv_queue_avg\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_local_send_queue_avg\")==null?\"\":states.get(\"wsrep_local_recv_queue_avg\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_apply_oool\")==null?\"\":states.get(\"wsrep_apply_oool\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"wsrep_apply_oooe\")==null?\"\":states.get(\"wsrep_apply_oooe\"),charset));\n\n\n\t\t\t\t\tlist.add(row);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowDatasourceSyn.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.text.SimpleDateFormat;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.heartbeat.DBHeartbeat;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.statistic.DataSourceSyncRecorder;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n\n/**\n * @author songwie\n */\npublic class ShowDatasourceSyn {\n\n\tprivate static final int FIELD_COUNT = 12;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tprivate static final SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n\n\t\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"name\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"host\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"port\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"Master_Host\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"Master_Port\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"Master_Use\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"Seconds_Behind_Master\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"Slave_IO_Running\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"Slave_SQL_Running\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"Slave_IO_State\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"Connect_Retry\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"Last_IO_Error\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"Last_SQL_Error\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"Last_SQL_Errno\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void response(ManagerConnection c,String stmt) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c,true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c,true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c,true);\n\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\t\n\t\tfor (RowDataPacket row : getRows(c.getCharset())) {\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c,true);\n\t\t}\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c,true);\n\n\t\t// post write\n\t\tc.write(buffer);\n\t}\n \n\tprivate static List<RowDataPacket> getRows(String charset) {\n\t\tList<RowDataPacket> list = new LinkedList<RowDataPacket>();\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\t// host nodes\n\t\tMap<String, PhysicalDBPool> dataHosts = conf.getDataHosts();\n\t\tfor (PhysicalDBPool pool : dataHosts.values()) {\n\t\t\tfor (PhysicalDatasource ds : pool.getAllDataSources()) {\n\t\t\t\tDBHeartbeat hb = ds.getHeartbeat();\n\t\t\t\tDataSourceSyncRecorder record = hb.getAsynRecorder();\n\t\t\t\tMap<String, String> states = record.getRecords();\n\t\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\t\tif(!states.isEmpty()){\n\t\t\t\t\trow.add(StringUtil.encode(ds.getName(),charset));\n\t\t\t\t\trow.add(StringUtil.encode(ds.getConfig().getIp(),charset));\n\t\t\t\t\trow.add(LongUtil.toBytes(ds.getConfig().getPort()));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"Master_Host\"),charset));\n\t\t\t\t\trow.add(LongUtil.toBytes(Long.valueOf(states.get(\"Master_Port\"))));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"Master_Use\"),charset));\n\t\t\t\t\tString secords = states.get(\"Seconds_Behind_Master\");\n\t\t\t\t\trow.add(secords==null?null:LongUtil.toBytes(Long.valueOf(secords)));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"Slave_IO_Running\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"Slave_SQL_Running\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"Slave_IO_State\"),charset));\n\t\t\t\t\trow.add(LongUtil.toBytes(Long.valueOf(states.get(\"Connect_Retry\"))));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"Last_IO_Error\"),charset));\n\t\t\t\t\trow.add(StringUtil.encode(states.get(\"Last_SQL_Error\"),charset));\n\t\t\t\t\trow.add(LongUtil.toBytes(Long.valueOf(states.get(\"Last_SQL_Errno\"))));\n\n\t\t\t\t\tlist.add(row);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowDatasourceSynDetail.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.heartbeat.DBHeartbeat;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.parser.ManagerParseShow;\nimport io.mycat.statistic.DataSourceSyncRecorder;\nimport io.mycat.statistic.DataSourceSyncRecorder.Record;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n\n/**\n * @author songwie\n */\npublic class ShowDatasourceSynDetail {\n\n\tprivate static final int FIELD_COUNT = 8;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\n\t\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"name\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"host\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"port\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"Master_Host\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"Master_Port\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"Master_Use\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"TIME\", Fields.FIELD_TYPE_DATETIME);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"Seconds_Behind_Master\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void response(ManagerConnection c,String stmt) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c,true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c,true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c,true);\n\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\t\n\t\tString name = ManagerParseShow.getWhereParameter(stmt);\n\t\tfor (RowDataPacket row : getRows(name,c.getCharset())) {\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c,true);\n\t\t}\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c,true);\n\n\t\t// post write\n\t\tc.write(buffer);\n\t}\n\t \n\tprivate static List<RowDataPacket> getRows(String name,String charset) {\n\t\tList<RowDataPacket> list = new LinkedList<RowDataPacket>();\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\t// host nodes\n\t\tMap<String, PhysicalDBPool> dataHosts = conf.getDataHosts();\n\t\tfor (PhysicalDBPool pool : dataHosts.values()) {\n\t\t\tfor (PhysicalDatasource ds : pool.getAllDataSources()) {\n\t\t\t\tDBHeartbeat hb = ds.getHeartbeat();\n\t\t\t\tDataSourceSyncRecorder record = hb.getAsynRecorder();\n\t\t\t\tMap<String, String> states = record.getRecords();\n\t\t\t\tif(name.equals(ds.getName())){\n\t\t\t\t\tList<Record> data = record.getAsynRecords();\n\t\t\t\t\tfor(Record r : data){\n\t\t\t\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\n\t\t\t\t\t\trow.add(StringUtil.encode(ds.getName(),charset));\n\t\t\t\t\t\trow.add(StringUtil.encode(ds.getConfig().getIp(),charset));\n\t\t\t\t\t\trow.add(LongUtil.toBytes(ds.getConfig().getPort()));\n\t\t\t\t\t\trow.add(StringUtil.encode(states.get(\"Master_Host\"),charset));\n\t\t\t\t\t\trow.add(LongUtil.toBytes(Long.valueOf(states.get(\"Master_Port\"))));\n\t\t\t\t\t\trow.add(StringUtil.encode(states.get(\"Master_Use\"),charset));\n\t\t\t\t\t\t//DateFormat非线程安全\n\t\t\t\t\t\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n\t\t\t\t\t\tString time = sdf.format(new Date(r.getTime()));\n\t\t\t\t\t\trow.add(StringUtil.encode(time,charset));\n\t\t\t\t\t\trow.add(LongUtil.toBytes((Long)r.getValue()));\n\n\t\t\t\t\t\tlist.add(row);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowDirectMemory.java",
    "content": "package io.mycat.manager.response;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.buffer.BufferPool;\nimport io.mycat.buffer.NettyBufferPool;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.memory.MyCatMemory;\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.utils.JavaUtils;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.netty.buffer.PoolArenaMetric;\nimport io.netty.buffer.PoolChunkListMetric;\nimport io.netty.buffer.PoolChunkMetric;\nimport io.netty.buffer.PoolSubpageMetric;\nimport sun.rmi.runtime.Log;\n\nimport java.io.UnsupportedEncodingException;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 实现show@@directmemory功能\n *\n * @author zagnix\n * @version 1.0\n * @create 2016-09-21 17:35\n */\n\npublic class ShowDirectMemory {\n    private static final int DETAILl_FIELD_COUNT = 3;\n    private static final ResultSetHeaderPacket detailHeader = PacketUtil.getHeader(DETAILl_FIELD_COUNT);\n    private static final FieldPacket[] detailFields = new FieldPacket[DETAILl_FIELD_COUNT];\n    private static final EOFPacket detailEof = new EOFPacket();\n\n\n    private static final int TOTAL_FIELD_COUNT = 5;\n    private static final ResultSetHeaderPacket totalHeader = PacketUtil.getHeader(TOTAL_FIELD_COUNT);\n    private static final FieldPacket[] totalFields = new FieldPacket[TOTAL_FIELD_COUNT];\n    private static final EOFPacket totalEof = new EOFPacket();\n\n    private static int useOffHeapForMerge ;\n    private static int processorBufferPoolType;\n    private static BufferPool bufferPool ;\n\n    static {\n        int i = 0;\n        byte packetId = 0;\n        detailHeader.packetId = ++packetId;\n\n        detailFields[i] = PacketUtil.getField(\"THREAD_ID\", Fields.FIELD_TYPE_VAR_STRING);\n        detailFields[i++].packetId = ++packetId;\n\n        detailFields[i] = PacketUtil.getField(\"MEM_USE_TYPE\", Fields.FIELD_TYPE_VAR_STRING);\n        detailFields[i++].packetId = ++packetId;\n\n        detailFields[i] = PacketUtil.getField(\"  SIZE  \", Fields.FIELD_TYPE_VAR_STRING);\n        detailFields[i++].packetId = ++packetId;\n        detailEof.packetId = ++packetId;\n\n\n        i = 0;\n        packetId = 0;\n\n        totalHeader.packetId = ++packetId;\n\n        totalFields[i] = PacketUtil.getField(\"MDIRECT_MEMORY_MAXED\", Fields.FIELD_TYPE_VAR_STRING);\n        totalFields[i++].packetId = ++packetId;\n\n        totalFields[i] = PacketUtil.getField(\"DIRECT_MEMORY_USED\", Fields.FIELD_TYPE_VAR_STRING);\n        totalFields[i++].packetId = ++packetId;\n\n        totalFields[i] = PacketUtil.getField(\"DIRECT_MEMORY_AVAILABLE\", Fields.FIELD_TYPE_VAR_STRING);\n        totalFields[i++].packetId = ++packetId;\n\n        totalFields[i] = PacketUtil.getField(\"SAFETY_FRACTION\", Fields.FIELD_TYPE_VAR_STRING);\n        totalFields[i++].packetId = ++packetId;\n\n        totalFields[i] = PacketUtil.getField(\"DIRECT_MEMORY_RESERVED\", Fields.FIELD_TYPE_VAR_STRING);\n        totalFields[i++].packetId = ++packetId;\n        totalEof.packetId = ++packetId;\n\n    }\n\n\n    public static void execute(ManagerConnection c, int showtype) {\n        useOffHeapForMerge = MycatServer.getInstance().getConfig().\n                getSystem().getUseOffHeapForMerge();\n        processorBufferPoolType = MycatServer.getInstance().getConfig().\n                getSystem().getProcessorBufferPoolType();\n        bufferPool = MycatServer.getInstance().getBufferPool();\n\n        if (showtype == 1) {\n            showDirectMemoryTotal(c);\n        } else if (showtype == 2) {\n            showDirectMemoryDetail(c);\n        }\n    }\n\n\n    public static void showDirectMemoryDetail(ManagerConnection c) {\n\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = detailHeader.write(buffer, c, true);\n\n        // write fields\n        for (FieldPacket field : detailFields) {\n            buffer = field.write(buffer, c, true);\n        }\n\n        // write eof\n        buffer = detailEof.write(buffer, c, true);\n\n        // write rows\n        byte packetId = detailEof.packetId;\n\n        ConcurrentHashMap<Long, Long> bufferpoolUsageMap = bufferPool.getNetDirectMemoryUsage();\n\n        try {\n\n            if (useOffHeapForMerge == 1) {\n                ConcurrentHashMap<Long, Long> concurrentHashMap = MycatServer.getInstance().\n                        getMyCatMemory().\n                        getResultMergeMemoryManager().getDirectMemorUsage();\n                for (Long key : concurrentHashMap.keySet()) {\n\n\n                    RowDataPacket row = new RowDataPacket(DETAILl_FIELD_COUNT);\n                    Long value = concurrentHashMap.get(key);\n                    row.add(String.valueOf(key).getBytes(c.getCharset()));\n                    /**\n                     * 该DIRECTMEMORY内存被结果集处理使用了\n                     */\n                    row.add(\"MergeMemoryPool\".getBytes(c.getCharset()));\n                    row.add(JavaUtils.bytesToString2(value).getBytes(c.getCharset()));\n                    row.packetId = ++packetId;\n                    buffer = row.write(buffer, c, true);\n                }\n            }\n\n            if(processorBufferPoolType == 2){\n\n\n            } else  {\n                for (Long key : bufferpoolUsageMap.keySet()) {\n                    RowDataPacket row = new RowDataPacket(DETAILl_FIELD_COUNT);\n                    Long value = bufferpoolUsageMap.get(key);\n                    row.add(String.valueOf(key).getBytes(c.getCharset()));\n                    /**\n                     * 该DIRECTMEMORY内存属于Buffer Pool管理的！\n                     */\n                    row.add(\"NetWorkBufferPool\".getBytes(c.getCharset()));\n                    row.add(JavaUtils.bytesToString2(value).getBytes(c.getCharset()));\n                    row.packetId = ++packetId;\n                    buffer = row.write(buffer, c, true);\n                }\n            }\n\n        } catch (UnsupportedEncodingException e) {\n            e.printStackTrace();\n        }\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c, true);\n\n        // write buffer\n        c.write(buffer);\n\n    }\n\n\n    public static void showDirectMemoryTotal(ManagerConnection c) {\n\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = totalHeader.write(buffer, c, true);\n\n        // write fields\n        for (FieldPacket field : totalFields) {\n            buffer = field.write(buffer, c, true);\n        }\n        // write eof\n        buffer = totalEof.write(buffer, c, true);\n        // write rows\n        byte packetId = totalEof.packetId;\n\n        long usedforMerge = 0;\n        long usedforNetwork = 0;\n        long chunkSizeBytes = 0;\n        int chunkCount = 0;\n\n        if (processorBufferPoolType == 2 && bufferPool instanceof NettyBufferPool) {\n\n            /**计算逻辑就是，1.先计算PoolChunk分配的页,表示已经消耗的内存，\n             2.然后计算小于一页情况，记录小于一页内存使用情况，\n             上面二者合起来就是整个netty 使用的内存，\n             已经分配了，但是没有使用的内存的情况*/\n            List<PoolArenaMetric> list = ((NettyBufferPool) bufferPool).getAllocator().getAlloc().directArenas();\n            chunkSizeBytes = ((NettyBufferPool) bufferPool).getAllocator().getChunkSize();\n            long pageSize = ((NettyBufferPool) bufferPool).getAllocator().getPageSize();\n\n            long chunksUsedBytes = 0;\n\n            /**PoolArenas*/\n            for (PoolArenaMetric pool : list) {\n                List<PoolChunkListMetric> pcks = pool.chunkLists();\n\n                /**针对PoolChunkList*/\n                for (PoolChunkListMetric pck : pcks) {\n                    Iterator<PoolChunkMetric> it = pck.iterator();\n                    while (it.hasNext()) {\n                        chunkCount++;\n                        PoolChunkMetric p = it.next();\n                        chunksUsedBytes += (chunkSizeBytes - p.freeBytes());\n                    }\n                }\n\n                List<PoolSubpageMetric> tinySubpages = pool.tinySubpages();\n                for (PoolSubpageMetric tiny : tinySubpages) {\n                    chunksUsedBytes -= (pageSize - (tiny.maxNumElements() - tiny.numAvailable()) * tiny.elementSize());\n                }\n                List<PoolSubpageMetric> smallSubpages = pool.smallSubpages();\n                for (PoolSubpageMetric small : smallSubpages) {\n                    chunksUsedBytes -= (pageSize - (small.maxNumElements() - small.numAvailable()) * small.elementSize());\n                }\n            }\n\n            usedforNetwork = chunkCount * chunkSizeBytes;\n        }\n\n        ConcurrentHashMap<Long, Long> bufferpoolUsageMap = bufferPool.getNetDirectMemoryUsage();\n\n        RowDataPacket row = new RowDataPacket(TOTAL_FIELD_COUNT);\n\n        try {\n\n            /**\n             * 通过-XX:MaxDirectMemorySize=2048m设置的值\n             */\n            row.add(JavaUtils.bytesToString2(Platform.getMaxDirectMemory()).getBytes(c.getCharset()));\n\n            if (useOffHeapForMerge == 1) {\n\n                /**\n                 * 结果集合并时，总共消耗的DirectMemory内存\n                 */\n                ConcurrentHashMap<Long, Long> concurrentHashMap = MycatServer.getInstance().\n                        getMyCatMemory().\n                        getResultMergeMemoryManager().getDirectMemorUsage();\n                for (Map.Entry<Long, Long> entry : concurrentHashMap.entrySet()) {\n                    usedforMerge += entry.getValue();\n                }\n            }\n\n            /**\n             * 网络packet处理，在buffer pool 已经使用DirectMemory内存\n             */\n            if (processorBufferPoolType == 2) {\n                usedforNetwork = chunkSizeBytes * chunkCount;\n            } else {\n                for (Map.Entry<Long, Long> entry : bufferpoolUsageMap.entrySet()) {\n                    usedforNetwork += entry.getValue();\n                }\n            }\n\n            row.add(JavaUtils.bytesToString2(usedforMerge + usedforNetwork).getBytes(c.getCharset()));\n\n\n            long totalAvailable = 0;\n\n            if (useOffHeapForMerge == 1) {\n                /**\n                 * 设置使用off-heap内存处理结果集时，防止客户把MaxDirectMemorySize设置到物理内存的极限。\n                 * Mycat能使用的DirectMemory是MaxDirectMemorySize*DIRECT_SAFETY_FRACTION大小，\n                 * DIRECT_SAFETY_FRACTION为安全系数，为OS，Heap预留空间，避免因大结果集造成系统物理内存被耗尽！\n                 */\n                totalAvailable = (long) (Platform.getMaxDirectMemory() * MyCatMemory.DIRECT_SAFETY_FRACTION);\n            } else {\n                totalAvailable = Platform.getMaxDirectMemory();\n            }\n\n            row.add(JavaUtils.bytesToString2(totalAvailable - usedforMerge - usedforNetwork)\n                    .getBytes(c.getCharset()));\n\n            if (useOffHeapForMerge == 1) {\n                /**\n                 * 输出安全系统DIRECT_SAFETY_FRACTION\n                 */\n                row.add((\"\" + MyCatMemory.DIRECT_SAFETY_FRACTION)\n                        .getBytes(c.getCharset()));\n            } else {\n                row.add((\"1.0\")\n                        .getBytes(c.getCharset()));\n            }\n\n\n            long resevedForOs = 0;\n\n            if (useOffHeapForMerge == 1) {\n                /**\n                 * 预留OS系统部分内存！！！\n                 */\n                resevedForOs = (long) ((1 - MyCatMemory.DIRECT_SAFETY_FRACTION) *\n                        (Platform.getMaxDirectMemory() -\n                                2 * MycatServer.getInstance().getTotalNetWorkBufferSize()));\n            }\n\n            row.add(resevedForOs > 0 ? JavaUtils.bytesToString2(resevedForOs).getBytes(c.getCharset()) : \"0\".getBytes(c.getCharset()));\n\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c, true);\n\n        } catch (UnsupportedEncodingException e) {\n            e.printStackTrace();\n        }\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c, true);\n\n        // write buffer\n        c.write(buffer);\n\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowHeartbeat.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.heartbeat.DBHeartbeat;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.IntegerUtil;\nimport io.mycat.util.LongUtil;\n\n/**\n * @author mycat\n */\npublic class ShowHeartbeat {\n\n\tprivate static final int FIELD_COUNT = 11;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"NAME\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"TYPE\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"HOST\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"PORT\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"RS_CODE\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"RETRY\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"STATUS\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"TIMEOUT\", Fields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"EXECUTE_TIME\",Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"LAST_ACTIVE_TIME\",Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"STOP\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void response(ManagerConnection c) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c,true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c,true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c,true);\n\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\tfor (RowDataPacket row : getRows()) {\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c,true);\n\t\t}\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c,true);\n\n\t\t// post write\n\t\tc.write(buffer);\n\t}\n\n\tprivate static List<RowDataPacket> getRows() {\n\t\tList<RowDataPacket> list = new LinkedList<RowDataPacket>();\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\t// host nodes\n\t\tMap<String, PhysicalDBPool> dataHosts = conf.getDataHosts();\n\t\tfor (PhysicalDBPool pool : dataHosts.values()) {\n\t\t\tfor (PhysicalDatasource ds : pool.getAllDataSources()) {\n\t\t\t\tDBHeartbeat hb = ds.getHeartbeat();\n\t\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\t\trow.add(ds.getName().getBytes());\n\t\t\t\trow.add(ds.getConfig().getDbType().getBytes());\n\t\t\t\tif (hb != null) {\n\t\t\t\t\trow.add(ds.getConfig().getIp().getBytes());\n\t\t\t\t\trow.add(IntegerUtil.toBytes(ds.getConfig().getPort()));\n\t\t\t\t\trow.add(IntegerUtil.toBytes(hb.getStatus()));\n\t\t\t\t\trow.add(IntegerUtil.toBytes(hb.getErrorCount()));\n\t\t\t\t\trow.add(hb.isChecking() ? \"checking\".getBytes() : \"idle\".getBytes());\n\t\t\t\t\trow.add(LongUtil.toBytes(hb.getTimeout()));\n\t\t\t\t\trow.add(hb.getRecorder().get().getBytes());\n\t\t\t\t\tString lat = hb.getLastActiveTime();\n\t\t\t\t\trow.add(lat == null ? null : lat.getBytes());\n\t\t\t\t\trow.add(hb.isStop() ? \"true\".getBytes() : \"false\".getBytes());\n\t\t\t\t} else {\n\t\t\t\t\trow.add(null);\n\t\t\t\t\trow.add(null);\n\t\t\t\t\trow.add(null);\n\t\t\t\t\trow.add(null);\n\t\t\t\t\trow.add(null);\n\t\t\t\t\trow.add(null);\n\t\t\t\t\trow.add(null);\n\t\t\t\t\trow.add(null);\n\t\t\t\t\trow.add(null);\n\t\t\t\t}\n\t\t\t\tlist.add(row);\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowHeartbeatDetail.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.heartbeat.DBHeartbeat;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.parser.ManagerParseHeartbeat;\nimport io.mycat.route.parser.util.Pair;\nimport io.mycat.statistic.HeartbeatRecorder;\nimport io.mycat.util.IntegerUtil;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n\n/**\n * @author songwie\n */\npublic class ShowHeartbeatDetail {\n\n\tprivate static final int FIELD_COUNT = 6;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\t\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"NAME\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"TYPE\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"HOST\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"PORT\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"TIME\", Fields.FIELD_TYPE_DATETIME);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"EXECUTE_TIME\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void response(ManagerConnection c,String stmt) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c,true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c,true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c,true);\n\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\tPair<String,String> pair = ManagerParseHeartbeat.getPair(stmt);\n\t\tString name = pair.getValue();\n\t\tfor (RowDataPacket row : getRows(name,c.getCharset())) {\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c,true);\n\t\t}\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c,true);\n\n\t\t// post write\n\t\tc.write(buffer);\n\t}\n\tprivate static List<RowDataPacket> getRows(String name,String charset) {\n\t\tList<RowDataPacket> list = new LinkedList<RowDataPacket>();\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\t// host nodes\n\t\tString type = \"\";\n\t\tString ip = \"\";\n\t\tint port = 0;\n\t\tDBHeartbeat hb = null;\n\n\t\tMap<String, PhysicalDBPool> dataHosts = conf.getDataHosts();\n\t\tfor (PhysicalDBPool pool : dataHosts.values()) {\n\t\t\tfor (PhysicalDatasource ds : pool.getAllDataSources()) {\n\t\t\t\tif(name.equals(ds.getName())){\n\t\t\t\t\thb = ds.getHeartbeat();\n\t\t\t\t\ttype = ds.getConfig().getDbType();\n\t\t\t\t\tip = ds.getConfig().getIp();\n\t\t\t\t\tport = ds.getConfig().getPort();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif(hb!=null){\n\t\t\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n\t\t\tQueue<HeartbeatRecorder.Record> heatbeartRecorders = hb.getRecorder().getRecordsAll();  \n\t\t\tfor(HeartbeatRecorder.Record record : heatbeartRecorders){\n\t\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\t\trow.add(StringUtil.encode(name,charset));\n\t\t\t\trow.add(StringUtil.encode(type,charset));\n\t\t\t\trow.add(StringUtil.encode(ip,charset));\n\t\t\t\trow.add(IntegerUtil.toBytes(port));\n\t\t\t\tlong time = record.getTime();\n\t\t\t\tString timeStr = sdf.format(new Date(time));\n\t\t\t\trow.add(StringUtil.encode(timeStr,charset));\n\t\t\t\trow.add(LongUtil.toBytes(record.getValue()));\n\n\t\t\t\tlist.add(row);\n\t\t\t}\n\t\t}else{\n\t\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\t\trow.add(null);\n\t\t\trow.add(null);\n\t\t\trow.add(null);\n\t\t\trow.add(null);\n\t\t\trow.add(null);\n\t\t\trow.add(null);\n\t\t\tlist.add(row);\n\t\t}\n\t\t\n\t\treturn list;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowHelp.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.manager.response;\r\n\r\nimport java.nio.ByteBuffer;\r\nimport java.util.ArrayList;\r\nimport java.util.Collections;\r\nimport java.util.LinkedHashMap;\r\nimport java.util.LinkedList;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\nimport io.mycat.backend.mysql.PacketUtil;\r\nimport io.mycat.config.Fields;\r\nimport io.mycat.manager.ManagerConnection;\r\nimport io.mycat.net.mysql.EOFPacket;\r\nimport io.mycat.net.mysql.FieldPacket;\r\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\r\nimport io.mycat.net.mysql.RowDataPacket;\r\nimport io.mycat.util.StringUtil;\r\n\r\n/**\r\n * 打印MycatServer所支持的语句\r\n * \r\n * @author mycat\r\n * @author mycat\r\n */\r\npublic final class ShowHelp {\r\n\r\n    private static final int FIELD_COUNT = 2;\r\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\r\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\r\n    private static final EOFPacket eof = new EOFPacket();\r\n    static {\r\n        int i = 0;\r\n        byte packetId = 0;\r\n        header.packetId = ++packetId;\r\n\r\n        fields[i] = PacketUtil.getField(\"STATEMENT\", Fields.FIELD_TYPE_VAR_STRING);\r\n        fields[i++].packetId = ++packetId;\r\n\r\n        fields[i] = PacketUtil.getField(\"DESCRIPTION\", Fields.FIELD_TYPE_VAR_STRING);\r\n        fields[i++].packetId = ++packetId;\r\n\r\n        eof.packetId = ++packetId;\r\n    }\r\n\r\n    public static void execute(ManagerConnection c) {\r\n        ByteBuffer buffer = c.allocate();\r\n\r\n        // write header\r\n        buffer = header.write(buffer, c,true);\r\n\r\n        // write fields\r\n        for (FieldPacket field : fields) {\r\n            buffer = field.write(buffer, c,true);\r\n        }\r\n\r\n        // write eof\r\n        buffer = eof.write(buffer, c,true);\r\n\r\n        // write rows\r\n        byte packetId = eof.packetId;\r\n        for (String key : keys) {\r\n            RowDataPacket row = getRow(key, helps.get(key), c.getCharset());\r\n            row.packetId = ++packetId;\r\n            buffer = row.write(buffer, c,true);\r\n        }\r\n\r\n        // write last eof\r\n        EOFPacket lastEof = new EOFPacket();\r\n        lastEof.packetId = ++packetId;\r\n        buffer = lastEof.write(buffer, c,true);\r\n\r\n        // post write\r\n        c.write(buffer);\r\n    }\r\n\r\n    private static RowDataPacket getRow(String stmt, String desc, String charset) {\r\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\r\n        row.add(StringUtil.encode(stmt, charset));\r\n        row.add(StringUtil.encode(desc, charset));\r\n        return row;\r\n    }\r\n\r\n    private static final Map<String, String> helps = new LinkedHashMap<String, String>();\r\n    private static final List<String> keys = new LinkedList<String>();\r\n    static {\r\n        // show\r\n        helps.put(\"show @@time.current\", \"Report current timestamp\");\r\n        helps.put(\"show @@time.startup\", \"Report startup timestamp\");\r\n        helps.put(\"show @@version\", \"Report Mycat Server version\");\r\n        helps.put(\"show @@server\", \"Report server status\");\r\n        helps.put(\"show @@threadpool\", \"Report threadPool status\");\r\n        helps.put(\"show @@database\", \"Report databases\");\r\n        helps.put(\"show @@datanode\", \"Report dataNodes\");\r\n        helps.put(\"show @@datanode where schema = ?\", \"Report dataNodes\");\r\n        helps.put(\"show @@datasource\", \"Report dataSources\");\r\n        helps.put(\"show @@datasource where dataNode = ?\", \"Report dataSources\");\r\n        helps.put(\"show @@datasource.synstatus\", \"Report datasource data synchronous\");\r\n        helps.put(\"show @@datasource.syndetail where name=?\", \"Report datasource data synchronous detail\");\r\n        helps.put(\"show @@datasource.cluster\", \"Report datasource galary cluster variables\");\r\n        helps.put(\"show @@processor\", \"Report processor status\");\r\n        helps.put(\"show @@command\", \"Report commands status\");\r\n        helps.put(\"show @@connection\", \"Report connection status\");\r\n        helps.put(\"show @@cache\", \"Report system cache usage\");\r\n        helps.put(\"show @@backend\", \"Report backend connection status\");\r\n        helps.put(\"show @@session\", \"Report front session details\");\r\n        helps.put(\"show @@connection.sql\", \"Report connection sql\");\r\n        helps.put(\"show @@sql.execute\", \"Report execute status\");\r\n        helps.put(\"show @@sql.detail where id = ?\", \"Report execute detail status\");\r\n        helps.put(\"show @@sql\", \"Report SQL list\");\r\n       // helps.put(\"show @@sql where id = ?\", \"Report  specify SQL\");\r\n        helps.put(\"show @@sql.high\", \"Report Hight Frequency SQL\");\r\n        helps.put(\"show @@sql.slow\", \"Report slow SQL\");\r\n        helps.put(\"show @@sql.resultset\", \"Report BIG RESULTSET SQL\");\r\n        helps.put(\"show @@sql.sum\", \"Report  User RW Stat \");\r\n        helps.put(\"show @@sql.sum.user\", \"Report  User RW Stat \");\r\n        helps.put(\"show @@sql.sum.table\", \"Report  Table RW Stat \");\r\n        helps.put(\"show @@parser\", \"Report parser status\");\r\n        helps.put(\"show @@router\", \"Report router status\");\r\n        helps.put(\"show @@heartbeat\", \"Report heartbeat status\");\r\n        helps.put(\"show @@heartbeat.detail where name=?\", \"Report heartbeat current detail\");\r\n        helps.put(\"show @@slow where schema = ?\", \"Report schema slow sql\");\r\n        helps.put(\"show @@slow where datanode = ?\", \"Report datanode slow sql\");\r\n        helps.put(\"show @@sysparam\", \"Report system param\");\r\n        helps.put(\"show @@syslog limit=?\", \"Report system mycat.log\");\r\n        helps.put(\"show @@white\", \"show mycat white host \");\r\n        helps.put(\"show @@white.set=?,?\", \"set mycat white host,[ip,user]\");\r\n\t\thelps.put(\"show @@directmemory=1 or 2\", \"show mycat direct memory usage\");\r\n\t\thelps.put(\"show @@check_global -SCHEMA= ? -TABLE=? -retry=? -interval=?\", \"check mycat global table consistency \");\r\n\r\n        // switch\r\n        helps.put(\"switch @@datasource name:index\", \"Switch dataSource\");\r\n\r\n        // kill\r\n        helps.put(\"kill @@connection id1,id2,...\", \"Kill the specified connections\");\r\n\r\n        // stop\r\n        helps.put(\"stop @@heartbeat name:time\", \"Pause dataNode heartbeat\");\r\n\r\n        // reload\r\n        helps.put(\"reload @@config\", \"Reload basic config from file\");\r\n        helps.put(\"reload @@config_all\", \"Reload all config from file\");\r\n        helps.put(\"reload @@route\", \"Reload route config from file\");\r\n        helps.put(\"reload @@user\", \"Reload user config from file\");\r\n        helps.put(\"reload @@sqlslow=\", \"Set Slow SQL Time(ms)\");\r\n        helps.put(\"reload @@user_stat\", \"Reset show @@sql  @@sql.sum @@sql.slow\");\r\n        // rollback\r\n        helps.put(\"rollback @@config\", \"Rollback all config from memory\");\r\n        helps.put(\"rollback @@route\", \"Rollback route config from memory\");\r\n        helps.put(\"rollback @@user\", \"Rollback user config from memory\");\r\n        \r\n        // open/close sql stat\r\n        helps.put(\"reload @@sqlstat=open\", \"Open real-time sql stat analyzer\");\r\n        helps.put(\"reload @@sqlstat=close\", \"Close real-time sql stat analyzer\");\r\n        \r\n        // offline/online\r\n        helps.put(\"offline\", \"Change MyCat status to OFF\");\r\n        helps.put(\"online\", \"Change MyCat status to ON\");\r\n\r\n        // clear\r\n        helps.put(\"clear @@slow where schema = ?\", \"Clear slow sql by schema\");\r\n        helps.put(\"clear @@slow where datanode = ?\", \"Clear slow sql by datanode\");\r\n\r\n        // list sort\r\n        keys.addAll(helps.keySet());\r\n    }\r\n\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowParser.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\n\n/**\n * @author mycat\n */\npublic final class ShowParser {\n\n    private static final int FIELD_COUNT = 7;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"PROCESSOR_NAME\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"PARSE_COUNT\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"TIME_COUNT\", Fields.FIELD_TYPE_DOUBLE);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"MAX_PARSE_TIME\", Fields.FIELD_TYPE_DOUBLE);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"MAX_PARSE_SQL_ID\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"CACHED_COUNT\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"CACHE_SIZE\", Fields.FIELD_TYPE_LONG);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        for (int i = 0; i < 1; i++) {\n            RowDataPacket row = getRow(c.getCharset());\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n        }\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(String charset) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(null);\n        row.add(null);\n        row.add(null);\n        row.add(null);\n        row.add(null);\n        row.add(null);\n        row.add(null);\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowProcessor.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.buffer.BufferPool;\nimport io.mycat.buffer.ByteBufferArena;\nimport io.mycat.buffer.DirectByteBufferPool;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.IntegerUtil;\nimport io.mycat.util.LongUtil;\nimport java.nio.ByteBuffer;\n\n/**\n * 查看处理器状态\n *\n * @author mycat\n * @author mycat\n */\npublic final class ShowProcessor {\n\n  private static final int FIELD_COUNT = 12;\n  private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n  private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n  private static final EOFPacket eof = new EOFPacket();\n\n  static {\n    int i = 0;\n    byte packetId = 0;\n    header.packetId = ++packetId;\n\n    fields[i] = PacketUtil.getField(\"NAME\", Fields.FIELD_TYPE_VAR_STRING);\n    fields[i++].packetId = ++packetId;\n\n    fields[i] = PacketUtil.getField(\"NET_IN\", Fields.FIELD_TYPE_LONGLONG);\n    fields[i++].packetId = ++packetId;\n\n    fields[i] = PacketUtil.getField(\"NET_OUT\", Fields.FIELD_TYPE_LONGLONG);\n    fields[i++].packetId = ++packetId;\n\n    fields[i] = PacketUtil.getField(\"REACT_COUNT\", Fields.FIELD_TYPE_LONGLONG);\n    fields[i++].packetId = ++packetId;\n\n    fields[i] = PacketUtil.getField(\"R_QUEUE\", Fields.FIELD_TYPE_LONG);\n    fields[i++].packetId = ++packetId;\n\n    fields[i] = PacketUtil.getField(\"W_QUEUE\", Fields.FIELD_TYPE_LONG);\n    fields[i++].packetId = ++packetId;\n\n    fields[i] = PacketUtil.getField(\"FREE_BUFFER\", Fields.FIELD_TYPE_LONGLONG);\n    fields[i++].packetId = ++packetId;\n\n    fields[i] = PacketUtil.getField(\"TOTAL_BUFFER\", Fields.FIELD_TYPE_LONGLONG);\n    fields[i++].packetId = ++packetId;\n\n    fields[i] = PacketUtil.getField(\"BU_PERCENT\", Fields.FIELD_TYPE_TINY);\n    fields[i++].packetId = ++packetId;\n\n    fields[i] = PacketUtil.getField(\"BU_WARNS\", Fields.FIELD_TYPE_LONG);\n    fields[i++].packetId = ++packetId;\n\n    fields[i] = PacketUtil.getField(\"FC_COUNT\", Fields.FIELD_TYPE_LONG);\n    fields[i++].packetId = ++packetId;\n\n    fields[i] = PacketUtil.getField(\"BC_COUNT\", Fields.FIELD_TYPE_LONG);\n    fields[i++].packetId = ++packetId;\n\n    eof.packetId = ++packetId;\n  }\n\n  public static void execute(ManagerConnection c) {\n    ByteBuffer buffer = c.allocate();\n\n    // write header\n    buffer = header.write(buffer, c, true);\n\n    // write fields\n    for (FieldPacket field : fields) {\n      buffer = field.write(buffer, c, true);\n    }\n\n    // write eof\n    buffer = eof.write(buffer, c, true);\n\n    // write rows\n    byte packetId = eof.packetId;\n    for (NIOProcessor p : MycatServer.getInstance().getProcessors()) {\n      RowDataPacket row = getRow(p, c.getCharset());\n      row.packetId = ++packetId;\n      buffer = row.write(buffer, c, true);\n    }\n\n    // write last eof\n    EOFPacket lastEof = new EOFPacket();\n    lastEof.packetId = ++packetId;\n    buffer = lastEof.write(buffer, c, true);\n\n    // write buffer\n    c.write(buffer);\n  }\n\n  private static RowDataPacket getRow(NIOProcessor processor, String charset) {\n    BufferPool pool = processor.getBufferPool();\n    RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n    long bufferSize = 0;//总数\n    long bufferCapacity = 0;//使用\n    long free = 0;\n    long bufferUsagePercent = 0;\n    long bufferSharedOpts = pool.getSharedOptsCount();\n    try {\n      if (pool instanceof ByteBufferArena) {\n        ByteBufferArena bufferPool = (ByteBufferArena) pool;\n        bufferSize = bufferPool.size();//总大小\n        bufferCapacity = bufferPool.capacity();//使用\n        free = bufferSize - bufferCapacity;\n        bufferUsagePercent = (long) ((bufferCapacity) * 100.0 / bufferSize);\n      } else if (pool instanceof DirectByteBufferPool) {\n        DirectByteBufferPool bufferPool = (DirectByteBufferPool) pool;\n        bufferSize = bufferPool.size();//总大小\n        bufferCapacity = bufferPool.getNetDirectMemoryUsage().values().stream()\n            .mapToLong(i -> i).count();\n        free = bufferSize - bufferCapacity;\n        bufferSharedOpts = bufferPool.getSharedOptsCount();\n        bufferUsagePercent = (long) ((bufferCapacity) * 100.0 / bufferSize);\n      } else {\n        return row;\n      }\n      row.add(processor.getName().getBytes());\n      row.add(LongUtil.toBytes(processor.getNetInBytes()));\n      row.add(LongUtil.toBytes(processor.getNetOutBytes()));\n      row.add(LongUtil.toBytes(0));\n      row.add(IntegerUtil.toBytes(0));\n      row.add(IntegerUtil.toBytes(processor.getWriteQueueSize()));\n      row.add(LongUtil.toBytes(free));\n      row.add(LongUtil.toBytes(bufferSize));\n      row.add(LongUtil.toBytes(bufferUsagePercent));\n      row.add(LongUtil.toBytes(bufferSharedOpts));\n      row.add(IntegerUtil.toBytes(processor.getFrontends().size()));\n      row.add(IntegerUtil.toBytes(processor.getBackends().size()));\n    } finally {\n      return row;\n    }\n  }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowRouter.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\n\n/**\n * @author mycat\n */\npublic final class ShowRouter {\n\n    private static final int FIELD_COUNT = 5;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"PROCESSOR_NAME\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"ROUTE_COUNT\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"TIME_COUNT\", Fields.FIELD_TYPE_FLOAT);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"MAX_ROUTE_TIME\", Fields.FIELD_TYPE_FLOAT);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"MAX_ROUTE_SQL_ID\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        for (NIOProcessor p : MycatServer.getInstance().getProcessors()) {\n            RowDataPacket row = getRow(p, c.getCharset());\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n        }\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static final NumberFormat nf = DecimalFormat.getInstance();\n    static {\n        nf.setMaximumFractionDigits(3);\n    }\n\n    private static RowDataPacket getRow(NIOProcessor processor, String charset) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(processor.getName().getBytes());\n        row.add(null);\n        row.add(null);\n        row.add(null);\n        row.add(null);\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSQL.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.statistic.stat.UserSqlLastStat;\nimport io.mycat.statistic.stat.UserStat;\nimport io.mycat.statistic.stat.UserStatAnalyzer;\n\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n\n/**\n * 查询用户最近执行的SQL记录\n * \n * @author mycat\n * @author zhuam\n */\npublic final class ShowSQL {\n\n    private static final int FIELD_COUNT = 6;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    \n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"ID\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"USER\", Fields.FIELD_TYPE_VARCHAR);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"START_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;        \n        \n        fields[i] = PacketUtil.getField(\"EXECUTE_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"SQL\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"IP\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c, boolean isClear) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;        \n        Map<String, UserStat> statMap = UserStatAnalyzer.getInstance().getUserStatMap();\n    \tfor (UserStat userStat : statMap.values()) {\n        \tString user = userStat.getUser();\n            List<UserSqlLastStat.SqlLast> sqls = userStat.getSqlLastStat().getSqls();\n            int i = 1;\n            for (UserSqlLastStat.SqlLast sqlLast : sqls) {\n                if (sqlLast != null) {\n                    RowDataPacket row = getRow(user, sqlLast, i, c.getCharset());\n                    row.packetId = ++packetId;\n                    i++;\n                    buffer = row.write(buffer, c,true);\n                }\n            }\n            \n            //读取SQL监控后清理\n            if ( isClear ) {\n            \tuserStat.getSqlLastStat().clear();\n            }\n        }\n\n        \n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(String user, UserSqlLastStat.SqlLast sql, int idx, String charset) {\n        \n    \tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(LongUtil.toBytes(idx));          \n        row.add( StringUtil.encode( user, charset) );\n        row.add( LongUtil.toBytes( sql.getStartTime() ) );\n        row.add( LongUtil.toBytes( sql.getExecuteTime() ) );\n        row.add( StringUtil.encode( sql.getSql(), charset) );\n        row.add(StringUtil.encode( sql.getHost(),charset ));\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSQLCondition.java",
    "content": "package io.mycat.manager.response;\r\n\r\nimport java.nio.ByteBuffer;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.concurrent.atomic.AtomicLong;\r\n\r\nimport io.mycat.backend.mysql.PacketUtil;\r\nimport io.mycat.config.Fields;\r\nimport io.mycat.manager.ManagerConnection;\r\nimport io.mycat.net.mysql.EOFPacket;\r\nimport io.mycat.net.mysql.FieldPacket;\r\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\r\nimport io.mycat.net.mysql.RowDataPacket;\r\nimport io.mycat.statistic.stat.QueryConditionAnalyzer;\r\nimport io.mycat.util.LongUtil;\r\nimport io.mycat.util.StringUtil;\r\n\r\n/**\r\n * SQL 查询条件 值统计\r\n * \r\n * @author zhuam\r\n *\r\n */\r\npublic class ShowSQLCondition {\r\n\t\r\n\tprivate static final int FIELD_COUNT = 4;\r\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\r\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\r\n    private static final EOFPacket eof = new EOFPacket();\r\n    \r\n    static {\r\n        int i = 0;\r\n        byte packetId = 0;\r\n        header.packetId = ++packetId;\r\n        \r\n        fields[i] = PacketUtil.getField(\"ID\", Fields.FIELD_TYPE_LONGLONG);        \r\n        fields[i++].packetId = ++packetId;\r\n        \r\n        fields[i] = PacketUtil.getField(\"KEY\", Fields.FIELD_TYPE_VAR_STRING);        \r\n        fields[i++].packetId = ++packetId;        \r\n        \r\n        fields[i] = PacketUtil.getField(\"VALUE\", Fields.FIELD_TYPE_VAR_STRING);        \r\n        fields[i++].packetId = ++packetId;  \r\n        \r\n        fields[i] = PacketUtil.getField(\"COUNT\", Fields.FIELD_TYPE_LONGLONG);\r\n        fields[i++].packetId = ++packetId;\r\n        \r\n        eof.packetId = ++packetId;\r\n    }\r\n\r\n    public static void execute(ManagerConnection c) {\r\n        ByteBuffer buffer = c.allocate();\r\n\r\n        // write header\r\n        buffer = header.write(buffer, c,true);\r\n\r\n        // write fields\r\n        for (FieldPacket field : fields) {\r\n            buffer = field.write(buffer, c,true);\r\n        }\r\n\r\n        // write eof\r\n        buffer = eof.write(buffer, c,true);\r\n\r\n        // write rows\r\n        byte packetId = eof.packetId;        \r\n        \r\n        String key = QueryConditionAnalyzer.getInstance().getKey();\r\n        List<Map.Entry<Object, AtomicLong>> list = QueryConditionAnalyzer.getInstance().getValues();\r\n        if ( list != null  ) {       \r\n        \t\r\n        \tint size = list.size();\r\n        \tlong total = 0L;\r\n        \t\r\n\t        for (int i = 0; i < size; i++) {\r\n\t        \tMap.Entry<Object, AtomicLong> entry = list.get(i);\r\n\t        \tObject value = entry.getKey();\r\n\t        \tLong count = entry.getValue().get();\r\n\t        \ttotal += count;\r\n\t        \t\r\n\t        \tRowDataPacket row = getRow(i, key, value.toString(), count, c.getCharset());\r\n\t            row.packetId = ++packetId;\r\n\t            buffer = row.write(buffer, c,true);\r\n\t        }\r\n\t        \r\n        \tRowDataPacket vk_row = getRow(size + 1, key + \".valuekey\", \"size\", size, c.getCharset());\r\n        \tvk_row.packetId = ++packetId;\r\n            buffer = vk_row.write(buffer, c,true);\r\n            \r\n        \tRowDataPacket vc_row = getRow(size + 2, key + \".valuecount\", \"total\", total, c.getCharset());\r\n        \tvc_row.packetId = ++packetId;\r\n            buffer = vc_row.write(buffer, c,true);\r\n\t       \r\n        }\r\n\r\n        // write last eof\r\n        EOFPacket lastEof = new EOFPacket();\r\n        lastEof.packetId = ++packetId;\r\n        buffer = lastEof.write(buffer, c,true);\r\n\r\n        // write buffer\r\n        c.write(buffer);\r\n    }\r\n\r\n    private static RowDataPacket getRow(int i, String key, String value, long count, String charset) {\r\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\r\n        row.add( LongUtil.toBytes( i ) );\r\n        row.add( StringUtil.encode(key, charset) );\r\n        row.add( StringUtil.encode(value, charset) );\r\n        row.add( LongUtil.toBytes( count ) );\r\n        return row;\r\n    }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSQLDetail.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * 查询指定SQL在各个pool中的执行情况\n * \n * @author mycat\n * @author mycat\n */\npublic final class ShowSQLDetail {\n\n    private static final NumberFormat nf = DecimalFormat.getInstance();\n    private static final int FIELD_COUNT = 5;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        nf.setMaximumFractionDigits(3);\n\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"DATA_SOURCE\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"EXECUTE\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"TIME\", Fields.FIELD_TYPE_DOUBLE);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"LAST_EXECUTE_TIMESTAMP\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"LAST_TIME\", Fields.FIELD_TYPE_DOUBLE);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c, long sql) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        for (int i = 0; i < 3; i++) {\n            RowDataPacket row = getRow(sql, c.getCharset());\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n        }\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(long sql, String charset) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(\"mysql_1\".getBytes());\n        row.add(LongUtil.toBytes(123L));\n        row.add(StringUtil.encode(nf.format(2.3), charset));\n        row.add(LongUtil.toBytes(1279188420682L));\n        row.add(StringUtil.encode(nf.format(3.42), charset));\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSQLExecute.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * 查询各SQL在所有pool中的执行情况\n * \n * @author mycat\n */\npublic final class ShowSQLExecute {\n\n    private static final NumberFormat nf = DecimalFormat.getInstance();\n    private static final int FIELD_COUNT = 5;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        nf.setMaximumFractionDigits(3);\n\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"SQL_ID\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"EXECUTE\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"TIME\", Fields.FIELD_TYPE_DOUBLE);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"MAX_TIME\", Fields.FIELD_TYPE_DOUBLE);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"MIN_TIME\", Fields.FIELD_TYPE_DOUBLE);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        for (int i = 0; i < 3; i++) {\n            RowDataPacket row = getRow(1000 * (i + 1), c.getCharset());\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n        }\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(long id, String charset) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(LongUtil.toBytes(id));\n        row.add(LongUtil.toBytes(100L));\n        row.add(StringUtil.encode(nf.format(898.9), charset));\n        row.add(StringUtil.encode(nf.format(8.8), charset));\n        row.add(StringUtil.encode(nf.format(1.0), charset));\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSQLHigh.java",
    "content": "package io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.statistic.stat.SqlFrequency;\nimport io.mycat.statistic.stat.UserStat;\nimport io.mycat.statistic.stat.UserStatAnalyzer;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * 查询高频 SQL\n * \n * @author zhuam\n *\n */\npublic final class ShowSQLHigh {\n\t\n\tprivate static final int FIELD_COUNT = 10;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    \n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"ID\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"USER\", Fields.FIELD_TYPE_VARCHAR);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"FREQUENCY\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"AVG_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;  \n        fields[i] = PacketUtil.getField(\"MAX_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"MIN_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"EXECUTE_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;        \n        \n        fields[i] = PacketUtil.getField(\"LAST_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"SQL\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"IP\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c, boolean isClear) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;     \n\n        Map<String, UserStat> statMap = UserStatAnalyzer.getInstance().getUserStatMap();\n    \tfor (UserStat userStat : statMap.values()) {\n        \tString user = userStat.getUser();\n            List<SqlFrequency> list=userStat.getSqlHigh().getSqlFrequency( isClear );\n             if ( list != null ) {\n                int i = 1;\n     \t        for (SqlFrequency sqlFrequency : list) {\n\t\t\t\t\tif(sqlFrequency != null){\n                        RowDataPacket row = getRow(i, user, sqlFrequency.getSql(), sqlFrequency.getCount(),\n\t\t\t\t\t\t\tsqlFrequency.getAvgTime(), sqlFrequency.getMaxTime(), sqlFrequency.getMinTime(),\n\t\t\t\t\t\t\tsqlFrequency.getExecuteTime(), sqlFrequency.getLastTime(), c.getCharset(),sqlFrequency.getHost());\n     \t                row.packetId = ++packetId;\n     \t                buffer = row.write(buffer, c,true);\n     \t                i++;\n                    }\n                }\n             }\n    \t}    \n    \t\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n\tprivate static RowDataPacket getRow(int i, String user, String sql, long count, long avgTime, long maxTime,\n\t\t\tlong minTime, long executTime, long lastTime, String charset,String host) {\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(LongUtil.toBytes(i));\n\t\trow.add(StringUtil.encode(user, charset));\n\t\trow.add(LongUtil.toBytes(count));\n\t\trow.add(LongUtil.toBytes(avgTime));\n\t\trow.add(LongUtil.toBytes(maxTime));\n\t\trow.add(LongUtil.toBytes(minTime));\n\t\trow.add(LongUtil.toBytes(executTime));\n\t\trow.add(LongUtil.toBytes(lastTime));\n\t\trow.add(StringUtil.encode(sql, charset));\n        row.add(StringUtil.encode(host,charset ));\n\t\treturn row;\n\t}\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSQLLarge.java",
    "content": "package io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.statistic.stat.UserSqlLargeStat;\nimport io.mycat.statistic.stat.UserStat;\nimport io.mycat.statistic.stat.UserStatAnalyzer;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * \n * 查询每个用户大集合返回的 SQL\n * \n * @author zhuam\n *\n */\npublic class ShowSQLLarge {\n\n\t private static final int FIELD_COUNT = 6;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    \n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"USER\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"ROWS\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"START_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"EXECUTE_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"SQL\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"IP\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c, boolean isClear) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;        \n        Map<String, UserStat> statMap = UserStatAnalyzer.getInstance().getUserStatMap();\n        for (UserStat userStat : statMap.values()) {\n        \tString user = userStat.getUser();\n\n            List<UserSqlLargeStat.SqlLarge> sqls = userStat.getSqlLargeRowStat().getSqls();\n            for (UserSqlLargeStat.SqlLarge sql : sqls) {\n                if (sql != null) {\n                    RowDataPacket row = getRow(user, sql, c.getCharset(),sql.getHost());\n                    row.packetId = ++packetId;\n                    buffer = row.write(buffer, c,true);\n                }\n            }\n            \n            if ( isClear ) {\n            \tuserStat.getSqlLargeRowStat().clear();//读取大结果集SQL后，清理\n            }\n        }\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(String user, UserSqlLargeStat.SqlLarge sql, String charset, String host) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add( StringUtil.encode(user, charset) );\n        row.add( LongUtil.toBytes( sql.getSqlRows() ) );\n        row.add( LongUtil.toBytes(sql.getStartTime() ) );\n        row.add( LongUtil.toBytes(sql.getExecuteTime() ) );\n        row.add( StringUtil.encode(sql.getSql(), charset) );\n        row.add(StringUtil.encode(host,charset) );\n        return row;\n    }\n}\n\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSQLSlow.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.statistic.SQLRecord;\nimport io.mycat.statistic.stat.UserStat;\nimport io.mycat.statistic.stat.UserStatAnalyzer;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * 查询每个用户的执行时间超过设定阈值的SQL, 默认TOP10\n * \n * @author mycat\n * @author zhuam\n */\npublic final class ShowSQLSlow {\n\n    private static final int FIELD_COUNT = 7;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    \n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"USER\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"DATASOURCE\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"START_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"EXECUTE_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"SQL\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"IP\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"SCHEMA\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    /**\n     * \n     * @param c\n     * @param isClear   查询完成后是否清理掉内存里面的所有慢SQL数据\n     * @param schema    逻辑库的schema，如果不为空 ，仅仅展示此schema的慢SQL\n     */\n    public static void execute(ManagerConnection c, boolean isClear, String schema) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;        \n        Map<String, UserStat> statMap = UserStatAnalyzer.getInstance().getUserStatMap();\n        for (UserStat userStat : statMap.values()) {\n        \tString user = userStat.getUser();\n            List<SQLRecord> keyList = userStat.getSqlRecorder().getRecords();\n            for (SQLRecord key : keyList) {\n                if (key != null) {\n                    // 如果带schema条件，过滤下信息\n                    if (schema != null && !schema.equalsIgnoreCase(key.schema)) {\n                        continue;\n                    }\n\n                    RowDataPacket row = getRow(user, key, c.getCharset());\n                    row.packetId = ++packetId;\n                    buffer = row.write(buffer, c,true);\n                }\n            }\n            \n            if ( isClear ) {\n            \tuserStat.getSqlRecorder().clear();//读取慢SQL后，清理\n            }\n        }\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(String user, SQLRecord sql, String charset) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add( StringUtil.encode(user, charset) );\n        row.add( StringUtil.encode(sql.dataNode, charset) );\n        row.add( LongUtil.toBytes(sql.startTime) );\n        row.add( LongUtil.toBytes(sql.executeTime) );\n        row.add( StringUtil.encode(sql.statement, charset) );\n        row.add(StringUtil.encode(sql.host,charset ));\n        row.add(StringUtil.encode(sql.schema, charset));\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSQLSumTable.java",
    "content": "package io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.text.DecimalFormat;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.statistic.stat.TableStat;\nimport io.mycat.statistic.stat.TableStatAnalyzer;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\npublic class ShowSQLSumTable {\n\t\n\tprivate static DecimalFormat decimalFormat = new DecimalFormat(\"0.00\");\n\n    private static final int FIELD_COUNT = 8;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    \n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"ID\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"TABLE\", Fields.FIELD_TYPE_VARCHAR);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"R\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"W\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"R%\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"RELATABLE\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"RELACOUNT\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"LAST_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c, boolean isClear) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;          \n        \n        /*\n        int i=0;\n        Map<String, TableStat> statMap = TableStatAnalyzer.getInstance().getTableStatMap();\n        for (TableStat tableStat : statMap.values()) {\n        \ti++;\n           RowDataPacket row = getRow(tableStat,i, c.getCharset());//getRow(sqlStat,sql, c.getCharset());\n           row.packetId = ++packetId;\n           buffer = row.write(buffer, c,true);\n        }\n        */\n        List<TableStat> list = TableStatAnalyzer.getInstance().getTableStats(isClear);\n        if ( list != null ) {\n            int i = 1;\n\t        for (TableStat tableStat : list) {\n                if(tableStat!=null){\n\t                RowDataPacket row = getRow(tableStat,i, c.getCharset());\n                    i++;\n\t                row.packetId = ++packetId;\n\t                buffer = row.write(buffer, c,true);\n                }\n\t        }\n        }\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(TableStat tableStat, long idx, String charset) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(LongUtil.toBytes(idx));\n        if (tableStat == null){\n        \trow.add(StringUtil.encode((\"not fond\"), charset));\n        \treturn row;\n        }\n        \n        String table = tableStat.getTable();\n        long R = tableStat.getRCount();\n        long W = tableStat.getWCount();\n        String __R = decimalFormat.format( 1.0D * R / (R + W) );\n        \n        \n        StringBuffer relaTableNameBuffer = new StringBuffer();\n        StringBuffer relaTableCountBuffer = new StringBuffer();\n        List<TableStat.RelaTable> relaTables = tableStat.getRelaTables();\n        if ( !relaTables.isEmpty() ) { \n        \t\n\t        for(TableStat.RelaTable relaTable: relaTables) {\n\t        \trelaTableNameBuffer.append( relaTable.getTableName() ).append(\", \");\n\t        \trelaTableCountBuffer.append( relaTable.getCount() ).append(\", \");\n\t        }\n\t        \n        } else {\n        \trelaTableNameBuffer.append(\"NULL\");\n        \trelaTableCountBuffer.append(\"NULL\");\n        }\n        \n        row.add( StringUtil.encode( table, charset) );\n        row.add( LongUtil.toBytes( R ) );\n        row.add( LongUtil.toBytes( W ) );\n        row.add( StringUtil.encode( String.valueOf( __R ), charset) );\n        row.add( StringUtil.encode( relaTableNameBuffer.toString(), charset) );\n        row.add( StringUtil.encode( relaTableCountBuffer.toString(), charset) );\n        row.add( LongUtil.toBytes( tableStat.getLastExecuteTime() ) );\n        \n        return row;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSQLSumUser.java",
    "content": "package io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.text.DecimalFormat;\nimport java.util.Map;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.statistic.stat.UserSqlRWStat;\nimport io.mycat.statistic.stat.UserStat;\nimport io.mycat.statistic.stat.UserStatAnalyzer;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * 查询用户的 SQL 执行情况\n * \n * 1、用户 R/W数、读占比、并发数\n * 2、请求时间范围\n * 3、请求的耗时范围\n * 4、Net 进/出 字节数\n * \n * @author zhuam\n */\npublic class ShowSQLSumUser {\n\t\n\tprivate static DecimalFormat decimalFormat = new DecimalFormat(\"0.00\");\n\n\tprivate static final int FIELD_COUNT = 11;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    \n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"ID\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"USER\", Fields.FIELD_TYPE_VARCHAR);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"R\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"W\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"R%\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"MAX\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"NET_IN\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"NET_OUT\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        //22-06h, 06-13h, 13-18h, 18-22h\n        fields[i] = PacketUtil.getField(\"TIME_COUNT\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;        \n        \n        //<10ms, 10ms-200ms, 200ms-1s, >1s\n        fields[i] = PacketUtil.getField(\"TTL_COUNT\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        \n        fields[i] = PacketUtil.getField(\"LAST_TIME\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c, boolean isClear) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        int i=0;  \n        \n        Map<String, UserStat> statMap = UserStatAnalyzer.getInstance().getUserStatMap();\n        for (UserStat userStat : statMap.values()) {\n        \ti++;\n           RowDataPacket row = getRow(userStat,i, c.getCharset());//getRow(sqlStat,sql, c.getCharset());\n           row.packetId = ++packetId;\n           buffer = row.write(buffer, c,true);\n           if ( isClear ) {\n        \t   userStat.clearRwStat(); \n           }\n        }\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(UserStat userStat, long idx, String charset) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(LongUtil.toBytes(idx));\n        if (userStat == null){\n        \trow.add(StringUtil.encode((\"not fond\"), charset));\n        \treturn row;\n        }\n        \n        String user = userStat.getUser();\n        UserSqlRWStat rwStat = userStat.getRWStat();\n        long R = rwStat.getRCount();\n        long W = rwStat.getWCount();\n        String __R = decimalFormat.format( 1.0D * R / (R + W) );\n        int MAX = rwStat.getConcurrentMax();\n        \n        row.add( StringUtil.encode( user, charset) );\n        row.add( LongUtil.toBytes( R ) );\n        row.add( LongUtil.toBytes( W ) );\n        row.add( StringUtil.encode( String.valueOf( __R ), charset) );\n        row.add( StringUtil.encode( String.valueOf( MAX ), charset) );\n        row.add( LongUtil.toBytes( rwStat.getNetInBytes() ) );\n        row.add( LongUtil.toBytes( rwStat.getNetOutBytes() ) );\n        row.add( StringUtil.encode( rwStat.getExecuteHistogram().toString(), charset) );\n        row.add( StringUtil.encode( rwStat.getTimeHistogram().toString(), charset) );\n        row.add( LongUtil.toBytes( rwStat.getLastExecuteTime() ) );\n        \n        return row;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowServer.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.FormatUtil;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\nimport io.mycat.util.TimeUtil;\n\n/**\n * 服务器状态报告\n * \n * @author mycat\n * @author mycat\n */\npublic final class ShowServer {\n\n\tprivate static final int FIELD_COUNT = 8;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil\n\t\t\t.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"UPTIME\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"USED_MEMORY\",\n\t\t\t\tFields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"TOTAL_MEMORY\",\n\t\t\t\tFields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"MAX_MEMORY\",\n\t\t\t\tFields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"RELOAD_TIME\",\n\t\t\t\tFields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"ROLLBACK_TIME\",\n\t\t\t\tFields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil\n\t\t\t\t.getField(\"CHARSET\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"STATUS\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void execute(ManagerConnection c) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c, true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c, true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c, true);\n\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\tRowDataPacket row = getRow(c.getCharset());\n\t\trow.packetId = ++packetId;\n\t\tbuffer = row.write(buffer, c, true);\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c, true);\n\n\t\t// write buffer\n\t\tc.write(buffer);\n\t}\n\n\tprivate static RowDataPacket getRow(String charset) {\n\t\tMycatServer server = MycatServer.getInstance();\n\t\tlong startupTime = server.getStartupTime();\n\t\tlong now = TimeUtil.currentTimeMillis();\n\t\tlong uptime = now - startupTime;\n\t\tRuntime rt = Runtime.getRuntime();\n\t\tlong total = rt.totalMemory();\n\t\tlong max = rt.maxMemory();\n\t\tlong used = (total - rt.freeMemory());\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(StringUtil.encode(FormatUtil.formatTime(uptime, 3), charset));\n\t\trow.add(LongUtil.toBytes(used));\n\t\trow.add(LongUtil.toBytes(total));\n\t\trow.add(LongUtil.toBytes(max));\n\t\trow.add(LongUtil.toBytes(server.getConfig().getReloadTime()));\n\t\trow.add(LongUtil.toBytes(server.getConfig().getRollbackTime()));\n\t\trow.add(StringUtil.encode(charset, charset));\n\t\trow.add(StringUtil.encode(MycatServer.getInstance().isOnline() ? \"ON\"\n\t\t\t\t: \"OFF\", charset));\n\t\treturn row;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSession.java",
    "content": "package io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.Collection;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.NonBlockingSession;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.StringUtil;\n\n/**\n * show front session detail info\n * \n * @author wuzhih\n * \n */\npublic class ShowSession {\n\tprivate static final int FIELD_COUNT = 3;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil\n\t\t\t.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"SESSION\", Fields.FIELD_TYPE_VARCHAR);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"DN_COUNT\", Fields.FIELD_TYPE_VARCHAR);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"DN_LIST\", Fields.FIELD_TYPE_VARCHAR);\n\t\tfields[i++].packetId = ++packetId;\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void execute(ManagerConnection c) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c, true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c, true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c, true);\n\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\tfor (NIOProcessor process : MycatServer.getInstance().getProcessors()) {\n\t\t\tfor (FrontendConnection front : process.getFrontends().values()) {\n\n\t\t\t\tif (!(front instanceof ServerConnection)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tServerConnection sc = (ServerConnection) front;\n\t\t\t\tRowDataPacket row = getRow(sc, c.getCharset());\n\t\t\t\tif (row != null) {\n\t\t\t\t\trow.packetId = ++packetId;\n\t\t\t\t\tbuffer = row.write(buffer, c, true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c, true);\n\n\t\t// write buffer\n\t\tc.write(buffer);\n\t}\n\n\tprivate static RowDataPacket getRow(ServerConnection sc, String charset) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tNonBlockingSession ssesion = sc.getSession2();\n\t\tCollection<BackendConnection> backConnections = ssesion.getTargetMap()\n\t\t\t\t.values();\n\t\tint cncount = backConnections.size();\n\t\tif (cncount == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tfor (BackendConnection backCon : backConnections) {\n\t\t\tsb.append(backCon).append(\"\\r\\n\");\n\t\t}\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(StringUtil.encode(sc.getId() + \"\", charset));\n\t\trow.add(StringUtil.encode(cncount + \"\", charset));\n\t\trow.add(StringUtil.encode(sb.toString(), charset));\n\t\treturn row;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSqlResultSet.java",
    "content": "package io.mycat.manager.response;\r\n\r\nimport io.mycat.backend.mysql.PacketUtil;\r\nimport io.mycat.config.Fields;\r\nimport io.mycat.manager.ManagerConnection;\r\nimport io.mycat.net.mysql.EOFPacket;\r\nimport io.mycat.net.mysql.FieldPacket;\r\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\r\nimport io.mycat.net.mysql.RowDataPacket;\r\nimport io.mycat.statistic.stat.SqlResultSet;\r\nimport io.mycat.statistic.stat.UserStat;\r\nimport io.mycat.statistic.stat.UserStatAnalyzer;\r\nimport io.mycat.util.IntegerUtil;\r\nimport io.mycat.util.LongUtil;\r\nimport io.mycat.util.StringUtil;\r\n\r\nimport java.nio.ByteBuffer;\r\nimport java.util.Map;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\n\r\n/**\r\n * show 大结果集 SQL\r\n * \r\n * @author songgw\r\n *\r\n */\r\npublic final class ShowSqlResultSet {\r\n\t\r\n\tprivate static final int FIELD_COUNT = 5;\r\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\r\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\r\n    private static final EOFPacket eof = new EOFPacket();\r\n    \r\n    static {\r\n        int i = 0;\r\n        byte packetId = 0;\r\n        header.packetId = ++packetId;\r\n        \r\n        fields[i] = PacketUtil.getField(\"ID\", Fields.FIELD_TYPE_LONGLONG);\r\n        fields[i++].packetId = ++packetId;\r\n        \r\n        fields[i] = PacketUtil.getField(\"USER\", Fields.FIELD_TYPE_VARCHAR);\r\n        fields[i++].packetId = ++packetId;\r\n        \r\n        fields[i] = PacketUtil.getField(\"FREQUENCY\", Fields.FIELD_TYPE_LONGLONG);\r\n        fields[i++].packetId = ++packetId;\r\n        \r\n        fields[i] = PacketUtil.getField(\"SQL\", Fields.FIELD_TYPE_VAR_STRING);\r\n        fields[i++].packetId = ++packetId;\r\n        \r\n        fields[i] = PacketUtil.getField(\"RESULTSET_SIZE\", Fields.FIELD_TYPE_INT24);\r\n        fields[i++].packetId = ++packetId;\r\n        \r\n        eof.packetId = ++packetId;\r\n    }\r\n\r\n    public static void execute(ManagerConnection c) {\r\n        ByteBuffer buffer = c.allocate();\r\n\r\n        // write header\r\n        buffer = header.write(buffer, c,true);\r\n\r\n        // write fields\r\n        for (FieldPacket field : fields) {\r\n            buffer = field.write(buffer, c,true);\r\n        }\r\n\r\n        // write eof\r\n        buffer = eof.write(buffer, c,true);\r\n\r\n        // write rows\r\n        byte packetId = eof.packetId;     \r\n        int i=0;\r\n        Map<String, UserStat> statMap = UserStatAnalyzer.getInstance().getUserStatMap();\r\n    \tfor (UserStat userStat : statMap.values()) {\r\n        \tString user = userStat.getUser();        \r\n        \tConcurrentHashMap<String, SqlResultSet> map=userStat.getSqlResultSizeRecorder().getSqlResultSet();\r\n             if ( map != null ) { \r\n     \t        for (SqlResultSet sqlResultSet:map.values()) {\r\n     \t        \tRowDataPacket row = getRow(++i, user,sqlResultSet.getSql(), sqlResultSet.getCount(), sqlResultSet.getResultSetSize(),c.getCharset());\r\n     \t            row.packetId = ++packetId;\r\n     \t            buffer = row.write(buffer, c,true);\r\n     \t        }\r\n             }\r\n    \t}    \r\n        // write last eof\r\n        EOFPacket lastEof = new EOFPacket();\r\n        lastEof.packetId = ++packetId;\r\n        buffer = lastEof.write(buffer, c,true);\r\n\r\n        // write buffer\r\n        c.write(buffer);\r\n    }\r\n\r\n    private static RowDataPacket getRow(int i, String user,String sql, int count, int resultSetSize,String charset) {\r\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\r\n        row.add( LongUtil.toBytes( i ) );\r\n        row.add( StringUtil.encode( user, charset) );\r\n        row.add( LongUtil.toBytes( count ) );\r\n        row.add( StringUtil.encode(sql, charset) );\r\n        row.add( IntegerUtil.toBytes(resultSetSize) );\r\n        return row;\r\n    }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSysLog.java",
    "content": "package io.mycat.manager.response;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.StringUtil;\n\n/**\n * Show @@SYSLOG LIMIT=50\n * \n * @author zhuam\n * \n */\npublic class ShowSysLog {\n\t\n\tprivate static final int FIELD_COUNT = 2;\n\t\n\tprivate static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\t\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"DATE\", Fields.FIELD_TYPE_VARCHAR);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"LOG\", Fields.FIELD_TYPE_VARCHAR);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void execute(ManagerConnection c, int numLines) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c, true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c, true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c, true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        \n\t\tString filename = SystemConfig.getHomePath()  + File.separator  + \"logs\" + File.separator + \"mycat.log\";\n\t\t\n\t\tString[] lines = getLinesByLogFile(filename, numLines);    \n\t\t\n\t\tboolean linesIsEmpty = true;\n\t\tfor(int i = 0; i < lines.length ; i++){\n\t        String line = lines[i];\n\t        if ( line != null ) {\t \t        \t\n\t        \tRowDataPacket row =  new RowDataPacket(FIELD_COUNT);\n\t\t        row.add(StringUtil.encode( line.substring(0,19), c.getCharset()));\n\t\t        row.add(StringUtil.encode( line.substring(19,line.length()), c.getCharset()));\n\t\t        row.packetId = ++packetId;\n\t\t        buffer = row.write(buffer, c,true);\n\t\t        \n\t\t        linesIsEmpty = false;\n\t        }\n\t\t}\n\t\t\n\t\tif ( linesIsEmpty ) {\n\t\t\tRowDataPacket row =  new RowDataPacket(FIELD_COUNT);\n\t\t\trow.add(StringUtil.encode( \"NULL\", c.getCharset()));\n\t        row.add(StringUtil.encode( \"NULL\", c.getCharset()));\n\t        row.packetId = ++packetId;\n\t        buffer = row.write(buffer, c,true);\t\t\t\n\t\t}\n\t\t\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c, true);\n\n\t\t// write buffer\n\t\tc.write(buffer);\n\t}\n\t\n\tprivate static String[] getLinesByLogFile(String filename, int numLines) {\n\t\t\n\n\t\tString lines[] = new String[numLines];\n\t\t\n\t\tBufferedReader in = null;\n\t    try {\n\t    \t//获取长度\n\t    \tint start = 0;\n\t    \tint totalNumLines = 0;\n\t    \t \n\t    \tFile logFile = new File(filename);  \n\t\t    in = new BufferedReader(new InputStreamReader(new FileInputStream(logFile), \"UTF-8\"));\n\t\t    \n\t\t    String line;\n\t\t    while ((line=in.readLine()) != null) {\n\t\t        totalNumLines++;\n\t\t    }\n\t\t    in.close();\n\t\t    \n\t\t    //\n\t\t    in = new BufferedReader(new InputStreamReader(new FileInputStream(logFile), \"UTF-8\"));\n\t\t   \n\t\t    // 跳过行\n\t\t    start = totalNumLines - numLines;\n\t\t    if (start < 0) { start = 0; }\n\t\t    for (int i=0; i<start; i++) {\n\t\t        in.readLine();\n\t\t    }\n\t\t    \n\t\t    // DESC\t\t    \n\t\t    int i = 0;\n\t\t    int end = lines.length-1;\n\t\t    while ((line=in.readLine()) != null && i<numLines) {\n\t\t    \tlines[end-i] = line;            \n\t        \ti++;\n\t        }\t        \n\t\t    numLines = start + i;\n\t\t    \n\t    } catch (FileNotFoundException ex) {\n\t    } catch (UnsupportedEncodingException e) {\n\t\t} catch (IOException e) {\n\t\t} finally {\n\t\t\tif ( in != null ) {\n\t\t\t\ttry {\n\t\t\t\t\tin.close();\n\t\t\t\t\tin = null;\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn lines;\n\t}\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowSysParam.java",
    "content": "package io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.StringUtil;\n\n/**\n * show Sysconfig param detail info\n * \n * @author rainbow\n * \n */\npublic class ShowSysParam {\n\tprivate static final int FIELD_COUNT = 3;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil\n\t\t\t.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"PARAM_NAME\", Fields.FIELD_TYPE_VARCHAR);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"PARAM_VALUE\", Fields.FIELD_TYPE_VARCHAR);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\tfields[i] = PacketUtil.getField(\"PARAM_DESCR\", Fields.FIELD_TYPE_VARCHAR);\n\t\tfields[i++].packetId = ++packetId;\n\t\t\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void execute(ManagerConnection c) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c, true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c, true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c, true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        \n        SystemConfig sysConfig = MycatServer.getInstance().getConfig().getSystem();\n        \n        List<String> paramValues = new ArrayList<String>();\n        paramValues.add(sysConfig.getProcessors() + \"\");\n        paramValues.add(sysConfig.getBufferPoolChunkSize() + \"B\");\n        paramValues.add(sysConfig.getBufferPoolPageSize() + \"B\");\n        paramValues.add(sysConfig.getProcessorBufferLocalPercent() + \"\");\n        paramValues.add(sysConfig.getProcessorExecutor() + \"\");\n        paramValues.add(sysConfig.getSequenceHandlerType() == 1 ? \"数据库方式\" : \"本地文件方式\");\n        paramValues.add(sysConfig.getPacketHeaderSize() + \"B\");\n        paramValues.add(sysConfig.getMaxPacketSize()/1024/1024 + \"M\");\n        paramValues.add(sysConfig.getIdleTimeout()/1000/60 + \"分钟\");\n        paramValues.add(sysConfig.getCharset() + \"\");\n        paramValues.add(ISOLATIONS[sysConfig.getTxIsolation()]);\n        paramValues.add(sysConfig.getSqlExecuteTimeout() + \"秒\");\n        paramValues.add(sysConfig.getProcessorCheckPeriod()/1000 + \"秒\");\n        paramValues.add(sysConfig.getDataNodeIdleCheckPeriod()/1000 + \"秒\");\n        paramValues.add(sysConfig.getDataNodeHeartbeatPeriod()/1000 + \"秒\");\n        paramValues.add(sysConfig.getBindIp() + \"\");\n        paramValues.add(sysConfig.getServerPort()+ \"\");\n        paramValues.add(sysConfig.getManagerPort() + \"\");\n\t\tparamValues.add(sysConfig.getUseSqlStat() + \"\");\n\n\t\tfor(int i = 0; i < PARAMNAMES.length ; i++){\n\t        RowDataPacket row =  new RowDataPacket(FIELD_COUNT);\n\t        row.add(StringUtil.encode(PARAMNAMES[i], c.getCharset()));\n\t        row.add(StringUtil.encode(paramValues.get(i), c.getCharset()));\n\t        row.add(StringUtil.encode(PARAM_DESCRIPTION[i], c.getCharset()));\n\t        row.packetId = ++packetId;\n\t        buffer = row.write(buffer, c,true);\n\t\t}\n\t\t\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c, true);\n\n\t\t// write buffer\n\t\tc.write(buffer);\n\t}\n\t\n\tprivate static final String[] PARAMNAMES = {\n\t\t\"processors\",\n\t\t\"processorBufferChunk\",\n\t\t\"processorBufferPool\",\n\t\t\"processorBufferLocalPercent\",\n\t\t\"processorExecutor\",\n\t\t\"sequnceHandlerType\",\n\t\t\"Mysql_packetHeaderSize\",\n\t\t\"Mysql_maxPacketSize\",\n\t\t\"Mysql_idleTimeout\",\n\t\t\"Mysql_charset\",\n\t\t\"Mysql_txIsolation\",\n\t\t\"Mysql_sqlExecuteTimeout\",\n\t\t\"Mycat_processorCheckPeriod\",\n\t\t\"Mycat_dataNodeIdleCheckPeriod\",\n\t\t\"Mycat_dataNodeHeartbeatPeriod\",\n\t\t\"Mycat_bindIp\",\n\t\t\"Mycat_serverPort\",\n\t\t\"Mycat_managerPort\",\n\t\t\"Mycat_useSqlStat\"};\n\t\n\tprivate static final String[] PARAM_DESCRIPTION = {\n\t\t\"主要用于指定系统可用的线程数，默认值为Runtime.getRuntime().availableProcessors()方法返回的值。主要影响processorBufferPool、processorBufferLocalPercent、processorExecutor属性。NIOProcessor的个数也是由这个属性定义的，所以调优的时候可以适当的调高这个属性。\",\n\t\t\"指定每次分配Socket Direct Buffer的大小，默认是4096个字节。这个属性也影响buffer pool的长度。\",\n\t\t\"指定bufferPool计算 比例值。由于每次执行NIO读、写操作都需要使用到buffer，系统初始化的时候会建立一定长度的buffer池来加快读、写的效率，减少建立buffer的时间\",\n\t\t\"就是用来控制分配这个pool的大小用的，但其也并不是一个准确的值，也是一个比例值。这个属性默认值为100。线程缓存百分比 = bufferLocalPercent / processors属性。\",\n\t\t\"主要用于指定NIOProcessor上共享的businessExecutor固定线程池大小。mycat在需要处理一些异步逻辑的时候会把任务提交到这个线程池中。新版本中这个连接池的使用频率不是很大了，可以设置一个较小的值。\",\n\t\t\"指定使用Mycat全局序列的类型。\",\n\t\t\"指定Mysql协议中的报文头长度。默认4\",\n\t\t\"指定Mysql协议可以携带的数据最大长度。默认16M\",\n\t\t\"指定连接的空闲超时时间。某连接在发起空闲检查下，发现距离上次使用超过了空闲时间，那么这个连接会被回收，就是被直接的关闭掉。默认30分钟\",\n\t\t\"连接的初始化字符集。默认为utf8\",\n\t\t\"前端连接的初始化事务隔离级别，只在初始化的时候使用，后续会根据客户端传递过来的属性对后端数据库连接进行同步。默认为REPEATED_READ\",\n\t\t\"SQL执行超时的时间，Mycat会检查连接上最后一次执行SQL的时间，若超过这个时间则会直接关闭这连接。默认时间为300秒\",\n\t\t\"清理NIOProcessor上前后端空闲、超时和关闭连接的间隔时间。默认是1秒\",\n\t\t\"对后端连接进行空闲、超时检查的时间间隔，默认是300秒\",\n\t\t\"对后端所有读、写库发起心跳的间隔时间，默认是10秒\",\n\t\t\"mycat服务监听的IP地址，默认值为0.0.0.0\",\n\t\t\"mycat的使用端口，默认值为8066\",\n\t\t\"mycat的管理端口，默认值为9066\",\n\t\t\"mycat是否开启SQL统计,1开启，0未开启\"};\n\t\n    public static final String[] ISOLATIONS = {\"\", \"READ_UNCOMMITTED\", \"READ_COMMITTED\", \"REPEATED_READ\", \"SERIALIZABLE\"};\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowThreadPool.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.IntegerUtil;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.NameableExecutor;\nimport io.mycat.util.StringUtil;\n\n/**\n * 查看线程池状态\n * \n * @author mycat\n * @author mycat\n */\npublic final class ShowThreadPool {\n\n\tprivate static final int FIELD_COUNT = 6;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil\n\t\t\t.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"NAME\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"POOL_SIZE\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"ACTIVE_COUNT\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"TASK_QUEUE_SIZE\",\n\t\t\t\tFields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"COMPLETED_TASK\",\n\t\t\t\tFields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"TOTAL_TASK\",\n\t\t\t\tFields.FIELD_TYPE_LONGLONG);\n\t\tfields[i++].packetId = ++packetId;\n\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void execute(ManagerConnection c) {\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c, true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c, true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c, true);\n\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\tList<NameableExecutor> executors = getExecutors();\n\t\tfor (NameableExecutor exec : executors) {\n\t\t\tif (exec != null) {\n\t\t\t\tRowDataPacket row = getRow(exec, c.getCharset());\n\t\t\t\trow.packetId = ++packetId;\n\t\t\t\tbuffer = row.write(buffer, c, true);\n\t\t\t}\n\t\t}\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c, true);\n\n\t\t// write buffer\n\t\tc.write(buffer);\n\t}\n\n\tprivate static RowDataPacket getRow(NameableExecutor exec, String charset) {\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(StringUtil.encode(exec.getName(), charset));\n\t\trow.add(IntegerUtil.toBytes(exec.getPoolSize()));\n\t\trow.add(IntegerUtil.toBytes(exec.getActiveCount()));\n\t\trow.add(IntegerUtil.toBytes(exec.getQueue().size()));\n\t\trow.add(LongUtil.toBytes(exec.getCompletedTaskCount()));\n\t\trow.add(LongUtil.toBytes(exec.getTaskCount()));\n\t\treturn row;\n\t}\n\n\tprivate static List<NameableExecutor> getExecutors() {\n\t\tList<NameableExecutor> list = new LinkedList<NameableExecutor>();\n\t\tMycatServer server = MycatServer.getInstance();\n\t\tlist.add(server.getTimerExecutor());\n\t\t// list.add(server.getAioExecutor());\n\t\tlist.add(server.getBusinessExecutor());\n\t\t// for (NIOProcessor pros : server.getProcessors()) {\n\t\t// list.add(pros.getExecutor());\n\t\t// }\n\t\treturn list;\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowTime.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.parser.ManagerParseShow;\nimport io.mycat.util.LongUtil;\n\n/**\n * @author mycat\n */\npublic final class ShowTime {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"TIMESTAMP\", Fields.FIELD_TYPE_LONGLONG);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c, int type) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        RowDataPacket row = getRow(type);\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(int type) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        switch (type) {\n        case ManagerParseShow.TIME_CURRENT:\n            row.add(LongUtil.toBytes(System.currentTimeMillis()));\n            break;\n        case ManagerParseShow.TIME_STARTUP:\n            row.add(LongUtil.toBytes(MycatServer.getInstance().getStartupTime()));\n            break;\n        default:\n            row.add(LongUtil.toBytes(0L));\n        }\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowVariables.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic final class ShowVariables {\n\n    private static final int FIELD_COUNT = 2;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"VARIABLE_NAME\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"VALUE\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        for (Map.Entry<String, String> e : variables.entrySet()) {\n            RowDataPacket row = getRow(e.getKey(), e.getValue(), c.getCharset());\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n        }\n\n        // write lastEof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static RowDataPacket getRow(String name, String value, String charset) {\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(StringUtil.encode(name, charset));\n        row.add(StringUtil.encode(value, charset));\n        return row;\n    }\n\n    private static final Map<String, String> variables = new HashMap<String, String>();\n    static {\n        variables.put(\"character_set_client\", \"utf8\");\n        variables.put(\"character_set_connection\", \"utf8\");\n        variables.put(\"character_set_results\", \"utf8\");\n        variables.put(\"character_set_server\", \"utf8\");\n        variables.put(\"init_connect\", \"\");\n        variables.put(\"interactive_timeout\", \"172800\");\n        variables.put(\"lower_case_table_names\", \"1\");\n        variables.put(\"max_allowed_packet\", \"16777216\");\n        variables.put(\"net_buffer_length\", \"8192\");\n        variables.put(\"net_write_timeout\", \"60\");\n        variables.put(\"query_cache_size\", \"0\");\n        variables.put(\"query_cache_type\", \"OFF\");\n        variables.put(\"sql_mode\", \"STRICT_TRANS_TABLES\");\n        variables.put(\"system_time_zone\", \"CST\");\n        variables.put(\"time_zone\", \"SYSTEM\");\n        variables.put(\"lower_case_table_names\", \"1\");\n        variables.put(\"tx_isolation\", \"REPEATABLE-READ\");\n        variables.put(\"wait_timeout\", \"172800\");\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowVersion.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.Versions;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\n\n/**\n * 查看CobarServer版本\n * \n * @author mycat\n */\npublic final class ShowVersion {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n\n        fields[i] = PacketUtil.getField(\"VERSION\", Fields.FIELD_TYPE_STRING);\n        fields[i++].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n    }\n\n    public static void execute(ManagerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(Versions.SERVER_VERSION);\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/ShowWhiteHost.java",
    "content": "package io.mycat.manager.response;\r\n\r\nimport java.nio.ByteBuffer;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.regex.Pattern;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport io.mycat.MycatServer;\r\nimport io.mycat.backend.mysql.PacketUtil;\r\nimport io.mycat.config.ErrorCode;\r\nimport io.mycat.config.Fields;\r\nimport io.mycat.config.model.FirewallConfig;\r\nimport io.mycat.config.model.UserConfig;\r\nimport io.mycat.config.util.ConfigException;\r\nimport io.mycat.manager.ManagerConnection;\r\nimport io.mycat.net.mysql.EOFPacket;\r\nimport io.mycat.net.mysql.FieldPacket;\r\nimport io.mycat.net.mysql.OkPacket;\r\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\r\nimport io.mycat.net.mysql.RowDataPacket;\r\nimport io.mycat.util.StringUtil;\r\n\r\npublic final class ShowWhiteHost {\r\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(ShowWhiteHost.class);\r\n\r\n    private static final int FIELD_COUNT = 2;\r\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\r\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\r\n    private static final EOFPacket eof = new EOFPacket();\r\n    static {\r\n        int i = 0;\r\n        byte packetId = 0;\r\n        header.packetId = ++packetId;\r\n        \r\n        fields[i] = PacketUtil.getField(\"IP\", Fields.FIELD_TYPE_VARCHAR);\r\n        fields[i++].packetId = ++packetId;\r\n\r\n        fields[i] = PacketUtil.getField(\"USER\", Fields.FIELD_TYPE_VARCHAR);\r\n        fields[i++].packetId = ++packetId;\r\n\r\n        \r\n        eof.packetId = ++packetId;\r\n    }\r\n    \r\n\tpublic static void execute(ManagerConnection c) {\r\n        ByteBuffer buffer = c.allocate();\r\n\r\n        // write header\r\n        buffer = header.write(buffer, c,true);\r\n\r\n        // write fields\r\n        for (FieldPacket field : fields) {\r\n            buffer = field.write(buffer, c,true);\r\n        }\r\n\r\n        // write eof\r\n        buffer = eof.write(buffer, c,true);\r\n\r\n        // write rows\r\n        byte packetId = eof.packetId;  \r\n        \r\n\t\tMap<String, List<UserConfig>> map=MycatServer.getInstance().getConfig().getFirewall().getWhitehost();\r\n\t\tfor (String key : map.keySet()) {  \r\n\t\t\tList<UserConfig> userConfigs=map.get(key);\r\n\t\t\tString users=\"\";\r\n\t\t\t for (int i = 0; i < userConfigs.size(); i++) {\r\n\t\t\t\t if(i>0) {\r\n                     users += \",\" + userConfigs.get(i).getName();\r\n                 }\r\n\t\t\t\t else {\r\n                     users += userConfigs.get(i).getName();\r\n                 }\r\n\t\t\t }\r\n            RowDataPacket row = getRow(key, users, c.getCharset());\r\n            row.packetId = ++packetId;\r\n            buffer = row.write(buffer, c,true);\t\t\t\r\n\t\t}\r\n        Map<Pattern, List<UserConfig>> map2=MycatServer.getInstance().getConfig().getFirewall().getWhitehostMask();\r\n        for (Pattern key : map2.keySet()) {\r\n            List<UserConfig> userConfigs=map2.get(key);\r\n            String users=\"\";\r\n            for (int i = 0; i < userConfigs.size(); i++) {\r\n                if(i>0) {\r\n                    users += \",\" + userConfigs.get(i).getName();\r\n                }\r\n                else {\r\n                    users += userConfigs.get(i).getName();\r\n                }\r\n            }\r\n            RowDataPacket row = getRow(FirewallConfig.getHost(key), users, c.getCharset());\r\n            row.packetId = ++packetId;\r\n            buffer = row.write(buffer, c,true);\r\n        }\r\n\t\t\r\n        // write last eof\r\n        EOFPacket lastEof = new EOFPacket();\r\n        lastEof.packetId = ++packetId;\r\n        buffer = lastEof.write(buffer, c,true);\r\n\r\n        // write buffer\r\n        c.write(buffer);\t\t\r\n\t}\r\n    private static RowDataPacket getRow(String ip, String user, String charset) {        \r\n    \tRowDataPacket row = new RowDataPacket(FIELD_COUNT);         \r\n        row.add( StringUtil.encode( ip, charset) );\r\n        row.add( StringUtil.encode( user, charset) );\r\n        return row;\r\n    }\r\n    public static String parseString(String stmt) {\r\n   \t int offset = stmt.indexOf(',');\r\n        if (offset != -1 && stmt.length() > ++offset) {\r\n            String txt = stmt.substring(offset).trim();\r\n            return txt;\r\n        }\r\n        return null;\r\n   }    \r\n\tpublic static synchronized void setHost(ManagerConnection c,String ips) {\r\n        OkPacket ok = new OkPacket();\t\t\r\n\t\tString []users = ips.split(\",\");\t\t\r\n        if (users.length<2){\r\n          c.writeErrMessage(ErrorCode.ER_YES, \"white host info error.\");\r\n          return;\r\n        }        \r\n        String host=\"\";\r\n        List<UserConfig> userConfigs = new ArrayList<UserConfig>();\r\n        int i=0;\r\n        for(String user : users){\r\n          if (i==0){\r\n        \t  host=user;\r\n        \t  i++;\r\n          }\r\n          else {\r\n        \ti++;  \r\n        \tUserConfig uc = MycatServer.getInstance().getConfig().getUsers().get(user);\r\n            if (null == uc) {\r\n            \tc.writeErrMessage(ErrorCode.ER_YES, \"user doesn't exist in host.\");\r\n                return; \r\n            }\r\n            if (uc.getSchemas() == null || uc.getSchemas().size() == 0) {\r\n            \tc.writeErrMessage(ErrorCode.ER_YES, \"host contains one root privileges user.\");\r\n                return;                 \r\n            }\r\n            userConfigs.add(uc);\r\n          }   \r\n        }  \r\n       if (MycatServer.getInstance().getConfig().getFirewall().addWhitehost(host, userConfigs)) {\r\n    \t   try{\r\n               FirewallConfig.updateToFile(host, userConfigs);\r\n           }catch(Exception e){\r\n        \t   LOGGER.warn(\"set while host error : \" + e.getMessage());\r\n        \t   c.writeErrMessage(ErrorCode.ER_YES, \"white host set success ,but write to file failed :\" + e.getMessage());\r\n           }\r\n    \t   \r\n           ok.packetId = 1;\r\n           ok.affectedRows = 1;\r\n           ok.serverStatus = 2;        \r\n    \t   ok.message = \"white host set to succeed.\".getBytes();\t\r\n           ok.write(c);  \t \r\n           \r\n       }\r\n       else {\r\n           c.writeErrMessage(ErrorCode.ER_YES, \"host duplicated.\");\r\n       }\r\n\t}\t\r\n\t\r\n\t\r\n\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/StopHeartbeat.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.util.Map;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.route.parser.ManagerParseStop;\nimport io.mycat.route.parser.util.Pair;\nimport io.mycat.util.FormatUtil;\nimport io.mycat.util.TimeUtil;\n\n/**\n * 暂停数据节点心跳检测\n * \n * @author mycat\n */\npublic final class StopHeartbeat {\n\n    private static final Logger logger = LoggerFactory.getLogger(StopHeartbeat.class);\n\n    public static void execute(String stmt, ManagerConnection c) {\n        int count = 0;\n        Pair<String[], Integer> keys = ManagerParseStop.getPair(stmt);\n        if (keys.getKey() != null && keys.getValue() != null) {\n            long time = keys.getValue().intValue() * 1000L;\n            Map<String, PhysicalDBPool> dns = MycatServer.getInstance().getConfig().getDataHosts();\n            for (String key : keys.getKey()) {\n            \tPhysicalDBPool dn = dns.get(key);\n                if (dn != null) {\n                    dn.getSource().setHeartbeatRecoveryTime(TimeUtil.currentTimeMillis() + time);\n                    ++count;\n                    StringBuilder s = new StringBuilder();\n                    s.append(dn.getHostName()).append(\" stop heartbeat '\");\n                    logger.warn(s.append(FormatUtil.formatTime(time, 3)).append(\"' by manager.\").toString());\n                }\n            }\n        }\n        OkPacket packet = new OkPacket();\n        packet.packetId = 1;\n        packet.affectedRows = count;\n        packet.serverStatus = 2;\n        packet.write(c);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/manager/response/SwitchDataSource.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.manager.response;\n\nimport java.util.Map;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.route.parser.ManagerParseSwitch;\nimport io.mycat.route.parser.util.Pair;\n\n/**\n * 切换数据节点的数据源\n * \n * @author mycat\n */\npublic final class SwitchDataSource {\n\n    public static void response(String stmt, ManagerConnection c) {\n        int count = 0;\n        Pair<String[], Integer> pair = ManagerParseSwitch.getPair(stmt);\n        Map<String, PhysicalDBPool> dns = MycatServer.getInstance().getConfig().getDataHosts();\n        Integer idx = pair.getValue();\n        for (String key : pair.getKey()) {\n        \tPhysicalDBPool dn = dns.get(key);\n            if (dn != null) {\n                int m = dn.getActivedIndex();\n                int n = (idx == null) ? dn.next(m) : idx.intValue();\n                if(!dn.notSwitchSource(n)) {\n                \t//todo 如果是zk集群 需要将结果写入到zk中再来切换.\n                    if(MycatServer.getInstance().isUseZkSwitch()) {\n                    \tMycatServer.getInstance().saveDataHostIndexToZk(dn.getHostName(), n);\n                        ++count;\n            \t\t\t//return switchSourceVoted( n,  isAlarm,  reason); \n            \t\t} else {\n            \t\t\t if (dn.switchSource(n, false, \"MANAGER\")) {\n                             ++count;\n                         }\n            \t\t}\n                }\n            }\n        }\n        OkPacket packet = new OkPacket();\n        packet.packetId = 1;\n        packet.affectedRows = count;\n        packet.serverStatus = 2;\n        packet.write(c);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/memory/MyCatMemory.java",
    "content": "package io.mycat.memory;\n\n\nimport com.google.common.annotations.VisibleForTesting;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.MemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.ResultMergeMemoryManager;\nimport io.mycat.memory.unsafe.storage.DataNodeDiskManager;\nimport io.mycat.memory.unsafe.storage.SerializerManager;\nimport io.mycat.memory.unsafe.utils.JavaUtils;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\nimport org.apache.log4j.Logger;\n\n/**\n * Created by zagnix on 2016/6/2.\n * Mycat内存管理工具类\n * 规划为三部分内存:结果集处理内存,系统预留内存,网络处理内存\n * 其中网络处理内存部分全部为Direct Memory\n * 结果集内存分为Direct Memory 和 Heap Memory，但目前仅使用Direct Memory\n * 系统预留内存为 Heap Memory。\n * 系统运行时，必须设置-XX:MaxDirectMemorySize 和 -Xmx JVM参数\n * -Xmx1024m -Xmn512m -XX:MaxDirectMemorySize=2048m -Xss256K -XX:+UseParallelGC\n */\n\npublic class MyCatMemory {\n\tprivate static Logger LOGGER = Logger.getLogger(MyCatMemory.class);\n\n\tpublic final  static double DIRECT_SAFETY_FRACTION  = 0.7;\n\tprivate final long systemReserveBufferSize;\n\n\tprivate final long memoryPageSize;\n\tprivate final long spillsFileBufferSize;\n\tprivate final long resultSetBufferSize;\n\tprivate final int numCores;\n\n\n\t/**\n\t * 内存管理相关关键类\n\t */\n\tprivate final MycatPropertyConf conf;\n\tprivate final MemoryManager resultMergeMemoryManager;\n\tprivate final DataNodeDiskManager blockManager;\n\tprivate final SerializerManager serializerManager;\n\tprivate final SystemConfig system;\n\n\n\tpublic MyCatMemory(SystemConfig system,long totalNetWorkBufferSize) throws NoSuchFieldException, IllegalAccessException {\n\n\t\tthis.system = system;\n\n\t\tLOGGER.info(\"useOffHeapForMerge = \" + system.getUseOffHeapForMerge());\n\t\tLOGGER.info(\"memoryPageSize = \" + system.getMemoryPageSize());\n\t\tLOGGER.info(\"spillsFileBufferSize = \" + system.getSpillsFileBufferSize());\n\t\tLOGGER.info(\"useStreamOutput = \" + system.getUseStreamOutput());\n\t\tLOGGER.info(\"systemReserveMemorySize = \" + system.getSystemReserveMemorySize());\n\t\tLOGGER.info(\"totalNetWorkBufferSize = \" + JavaUtils.bytesToString2(totalNetWorkBufferSize));\n\t\tLOGGER.info(\"dataNodeSortedTempDir = \" + system.getDataNodeSortedTempDir());\n\n\t\tthis.conf = new MycatPropertyConf();\n\t\tnumCores = Runtime.getRuntime().availableProcessors();\n\n\t\tthis.systemReserveBufferSize = JavaUtils.\n\t\t\t\tbyteStringAsBytes(system.getSystemReserveMemorySize());\n\t\tthis.memoryPageSize = JavaUtils.\n\t\t\t\tbyteStringAsBytes(system.getMemoryPageSize());\n\n\t\tthis.spillsFileBufferSize = JavaUtils.\n\t\t\t\tbyteStringAsBytes(system.getSpillsFileBufferSize());\n\n\t\t/**\n\t\t * 目前merge，order by ，limit 没有使用On Heap内存\n\t\t */\n\t\tlong maxOnHeapMemory =  (Platform.getMaxHeapMemory()-systemReserveBufferSize);\n\n\t\tassert maxOnHeapMemory > 0;\n\n\t\tresultSetBufferSize =\n\t\t\t\t(long)((Platform.getMaxDirectMemory()-2*totalNetWorkBufferSize)*DIRECT_SAFETY_FRACTION);\n\n\t\tassert resultSetBufferSize > 0;\n\n\t\t/**\n\t\t * mycat.merge.memory.offHeap.enabled\n\t\t * mycat.buffer.pageSize\n\t\t * mycat.memory.offHeap.size\n\t\t * mycat.merge.file.buffer\n\t\t * mycat.direct.output.result\n\t\t * mycat.local.dir\n\t\t */\n\n\t\tif(system.getUseOffHeapForMerge()== 1){\n\t\t\tconf.set(\"mycat.memory.offHeap.enabled\",\"true\");\n\t\t}else{\n\t\t\tconf.set(\"mycat.memory.offHeap.enabled\",\"false\");\n\t\t}\n\n\t\tif(system.getUseStreamOutput() == 1){\n\t\t\tconf.set(\"mycat.stream.output.result\",\"true\");\n\t\t}else{\n\t\t\tconf.set(\"mycat.stream.output.result\",\"false\");\n\t\t}\n\n\n\t\tif(system.getMemoryPageSize() != null){\n\t\t\tconf.set(\"mycat.buffer.pageSize\",system.getMemoryPageSize());\n\t\t}else{\n\t\t\tconf.set(\"mycat.buffer.pageSize\",\"32k\");\n\t\t}\n\n\n\t\tif(system.getSpillsFileBufferSize() != null){\n\t\t\tconf.set(\"mycat.merge.file.buffer\",system.getSpillsFileBufferSize());\n\t\t}else{\n\t\t\tconf.set(\"mycat.merge.file.buffer\",\"32k\");\n\t\t}\n\n\t\tconf.set(\"mycat.pointer.array.len\",\"1k\")\n\t\t\t.set(\"mycat.memory.offHeap.size\", JavaUtils.bytesToString2(resultSetBufferSize));\n\n\t\tLOGGER.info(\"mycat.memory.offHeap.size: \" +\n\t\t\t\tJavaUtils.bytesToString2(resultSetBufferSize));\n\n\t\tresultMergeMemoryManager =\n\t\t\t\tnew ResultMergeMemoryManager(conf,numCores,maxOnHeapMemory);\n\n\n\t\tserializerManager = new SerializerManager();\n\n\t\tblockManager = new DataNodeDiskManager(conf,true,serializerManager);\n\n\t}\n\n\n\t@VisibleForTesting\n\tpublic MyCatMemory() throws NoSuchFieldException, IllegalAccessException {\n\t\tthis.system = null;\n\t\tthis.systemReserveBufferSize = 0;\n\t\tthis.memoryPageSize = 0;\n\t\tthis.spillsFileBufferSize = 0;\n\t\tconf = new MycatPropertyConf();\n\t\tnumCores = Runtime.getRuntime().availableProcessors();\n\n\t\tlong maxOnHeapMemory =  (Platform.getMaxHeapMemory());\n\t\tassert maxOnHeapMemory > 0;\n\n\t\tresultSetBufferSize = (long)((Platform.getMaxDirectMemory())*DIRECT_SAFETY_FRACTION);\n\n\t\tassert resultSetBufferSize > 0;\n\t\t/**\n\t\t * mycat.memory.offHeap.enabled\n\t\t * mycat.buffer.pageSize\n\t\t * mycat.memory.offHeap.size\n\t\t * mycat.testing.memory\n\t\t * mycat.merge.file.buffer\n\t\t * mycat.direct.output.result\n\t\t * mycat.local.dir\n\t\t */\n\t\tconf.set(\"mycat.memory.offHeap.enabled\",\"true\")\n\t\t\t\t.set(\"mycat.pointer.array.len\",\"8K\")\n\t\t\t\t.set(\"mycat.buffer.pageSize\",\"1m\")\n\t\t\t\t.set(\"mycat.memory.offHeap.size\", JavaUtils.bytesToString2(resultSetBufferSize))\n\t\t\t\t.set(\"mycat.stream.output.result\",\"false\");\n\n\t\tLOGGER.info(\"mycat.memory.offHeap.size: \" + JavaUtils.bytesToString2(resultSetBufferSize));\n\n\t\tresultMergeMemoryManager =\n\t\t\t\tnew ResultMergeMemoryManager(conf,numCores,maxOnHeapMemory);\n\n\t\tserializerManager = new SerializerManager();\n\n\t\tblockManager = new DataNodeDiskManager(conf,true,serializerManager);\n\n\t}\n\n\t\tpublic MycatPropertyConf getConf() {\n\t\treturn conf;\n\t}\n\n\tpublic long getResultSetBufferSize() {\n\t\treturn resultSetBufferSize;\n\t}\n\n\tpublic MemoryManager getResultMergeMemoryManager() {\n\t\treturn resultMergeMemoryManager;\n\t}\n\n\tpublic SerializerManager getSerializerManager() {\n\t\treturn serializerManager;\n\t}\n\n\tpublic DataNodeDiskManager getBlockManager() {\n\t\treturn blockManager;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/environment/EnvironmentInformation.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.environment;\n\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.InputStream;\nimport java.lang.management.ManagementFactory;\nimport java.lang.management.OperatingSystemMXBean;\nimport java.lang.management.RuntimeMXBean;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Properties;\n\n/**\n * Utility class that gives access to the execution environment of the JVM, like\n * the executing user, startup options, or the JVM version.\n */\npublic class EnvironmentInformation {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(EnvironmentInformation.class);\n\n\tpublic static final String UNKNOWN = \"<unknown>\";\n\n\t/**\n\t * Returns the version of the code as String. If version == null, then the JobManager does not run from a\n\t * Maven build. An example is a source code checkout, compile, and run from inside an IDE.\n\t * \n\t * @return The version string.\n\t */\n\tpublic static String getVersion() {\n\t\tString version = EnvironmentInformation.class.getPackage().getImplementationVersion();\n\t\treturn version != null ? version : UNKNOWN;\n\t}\n\n\t/**\n\t * Returns the code revision (commit and commit date) of Flink, as generated by the Maven builds.\n\t * \n\t * @return The code revision.\n\t */\n\tpublic static RevisionInformation getRevisionInformation() {\n\t\tString revision = UNKNOWN;\n\t\tString commitDate = UNKNOWN;\n\t\ttry (InputStream propFile = EnvironmentInformation.class.getClassLoader().getResourceAsStream(\".version.properties\")) {\n\t\t\tif (propFile != null) {\n\t\t\t\tProperties properties = new Properties();\n\t\t\t\tproperties.load(propFile);\n\t\t\t\tString propRevision = properties.getProperty(\"git.commit.id.abbrev\");\n\t\t\t\tString propCommitDate = properties.getProperty(\"git.commit.time\");\n\t\t\t\trevision = propRevision != null ? propRevision : UNKNOWN;\n\t\t\t\tcommitDate = propCommitDate != null ? propCommitDate : UNKNOWN;\n\t\t\t}\n\t\t} catch (Throwable t) {\n\t\t\tif (LOG.isDebugEnabled()) {\n\t\t\t\tLOG.debug(\"Cannot determine code revision: Unable to read version property file.\", t);\n\t\t\t} else {\n\t\t\t\tLOG.info(\"Cannot determine code revision: Unable to read version property file.\");\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn new RevisionInformation(revision, commitDate);\n\t}\n\n\t/**\n\t * Gets the name of the user that is running the JVM.\n\t * \n\t * @return The name of the user that is running the JVM.\n\t */\n\tpublic static String getUserRunning() {\n\t\tString user = System.getProperty(\"user.name\");\n\t\tif (user == null) {\n\t\t\tuser = UNKNOWN;\n\t\t\tif (LOG.isDebugEnabled()) {\n\t\t\t\tLOG.debug(\"Cannot determine user/group information for the current user.\");\n\t\t\t}\n\t\t}\n\t\treturn user;\n\t}\n\n\t/**\n\t * The maximum JVM heap size, in bytes.\n\t * \n\t * @return The maximum JVM heap size, in bytes.\n\t */\n\tpublic static long getMaxJvmHeapMemory() {\n\t\tlong maxMemory = Runtime.getRuntime().maxMemory();\n\n\t\tif (maxMemory == Long.MAX_VALUE) {\n\t\t\t// amount of free memory unknown\n\t\t\ttry {\n\t\t\t\t// workaround for Oracle JDK\n\t\t\t\tOperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();\n\t\t\t\tClass<?> clazz = Class.forName(\"com.sun.management.OperatingSystemMXBean\");\n\t\t\t\tMethod method = clazz.getMethod(\"getTotalPhysicalMemorySize\");\n\t\t\t\tmaxMemory = (Long) method.invoke(operatingSystemMXBean) / 4;\n\t\t\t}\n\t\t\tcatch (Throwable e) {\n\t\t\t\tthrow new RuntimeException(\"Could not determine the amount of free memory.\\n\" +\n\t\t\t\t\t\t\"Please set the maximum memory for the JVM, e.g. -Xmx512M for 512 megabytes.\");\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn maxMemory;\n\t}\n\n\t/**\n\t * Gets an estimate of the size of the free heap memory.\n\t * \n\t * NOTE: This method is heavy-weight. It triggers a garbage collection to reduce fragmentation and get\n\t * a better estimate at the size of free memory. It is typically more accurate than the plain version\n\t * {@link #getSizeOfFreeHeapMemory()}.\n\t * \n\t * @return An estimate of the size of the free heap memory, in bytes.\n\t */\n\tpublic static long getSizeOfFreeHeapMemoryWithDefrag() {\n\t\t// trigger a garbage collection, to reduce fragmentation\n\t\tSystem.gc();\n\t\t\n\t\treturn getSizeOfFreeHeapMemory();\n\t}\n\t\n\t/**\n\t * Gets an estimate of the size of the free heap memory. The estimate may vary, depending on the current\n\t * level of memory fragmentation and the number of dead objects. For a better (but more heavy-weight)\n\t * estimate, use {@link #getSizeOfFreeHeapMemoryWithDefrag()}.\n\t * \n\t * @return An estimate of the size of the free heap memory, in bytes.\n\t */\n\tpublic static long getSizeOfFreeHeapMemory() {\n\t\tRuntime r = Runtime.getRuntime();\n\t\tlong maxMemory = r.maxMemory();\n\n\t\tif (maxMemory == Long.MAX_VALUE) {\n\t\t\t// amount of free memory unknown\n\t\t\ttry {\n\t\t\t\t// workaround for Oracle JDK\n\t\t\t\tOperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();\n\t\t\t\tClass<?> clazz = Class.forName(\"com.sun.management.OperatingSystemMXBean\");\n\t\t\t\tMethod method = clazz.getMethod(\"getTotalPhysicalMemorySize\");\n\t\t\t\tmaxMemory = (Long) method.invoke(operatingSystemMXBean) / 4;\n\t\t\t} catch (Throwable e) {\n\t\t\t\tthrow new RuntimeException(\"Could not determine the amount of free memory.\\n\" +\n\t\t\t\t\t\t\"Please set the maximum memory for the JVM, e.g. -Xmx512M for 512 megabytes.\");\n\t\t\t}\n\t\t}\n\n\t\treturn maxMemory - r.totalMemory() + r.freeMemory();\n\t}\n\n\t/**\n\t * Gets the version of the JVM in the form \"VM_Name - Vendor  - Spec/Version\".\n\t *\n\t * @return The JVM version.\n\t */\n\tpublic static String getJvmVersion() {\n\t\ttry {\n\t\t\tfinal RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();\n\t\t\treturn bean.getVmName() + \" - \" + bean.getVmVendor() + \" - \" + bean.getSpecVersion() + '/' + bean.getVmVersion();\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\treturn UNKNOWN;\n\t\t}\n\t}\n\n\t/**\n\t * Gets the system parameters and environment parameters that were passed to the JVM on startup.\n\t *\n\t * @return The options passed to the JVM on startup.\n\t */\n\tpublic static String getJvmStartupOptions() {\n\t\ttry {\n\t\t\tfinal RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();\n\t\t\tfinal StringBuilder bld = new StringBuilder();\n\t\t\t\n\t\t\tfor (String s : bean.getInputArguments()) {\n\t\t\t\tbld.append(s).append(' ');\n\t\t\t}\n\n\t\t\treturn bld.toString();\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\treturn UNKNOWN;\n\t\t}\n\t}\n\n\t/**\n\t * Gets the system parameters and environment parameters that were passed to the JVM on startup.\n\t *\n\t * @return The options passed to the JVM on startup.\n\t */\n\tpublic static String[] getJvmStartupOptionsArray() {\n\t\ttry {\n\t\t\tRuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();\n\t\t\tList<String> options = bean.getInputArguments();\n\t\t\treturn options.toArray(new String[options.size()]);\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\treturn new String[0];\n\t\t}\n\t}\n\n\t/**\n\t * Gets the directory for temporary files, as returned by the JVM system property \"java.io.tmpdir\".\n\t *\n\t * @return The directory for temporary files.\n\t */\n\tpublic static String getTemporaryFileDirectory() {\n\t\treturn System.getProperty(\"java.io.tmpdir\");\n\t}\n\n\t/**\n\t * Tries to retrieve the maximum number of open file handles. This method will only work on\n\t * UNIX-based operating systems with Sun/Oracle Java versions.\n\t * \n\t * <p>If the number of max open file handles cannot be determined, this method returns {@code -1}.</p>\n\t * \n\t * @return The limit of open file handles, or {@code -1}, if the limit could not be determined.\n\t */\n\tpublic static long getOpenFileHandlesLimit() {\n\t\tClass<?> sunBeanClass;\n\t\ttry {\n\t\t\tsunBeanClass = Class.forName(\"com.sun.management.UnixOperatingSystemMXBean\");\n\t\t}\n\t\tcatch (ClassNotFoundException e) {\n\t\t\treturn -1L;\n\t\t}\n\t\t\n\t\ttry {\n\t\t\tMethod fhLimitMethod = sunBeanClass.getMethod(\"getMaxFileDescriptorCount\");\n\t\t\tObject result = fhLimitMethod.invoke(ManagementFactory.getOperatingSystemMXBean());\n\t\t\treturn (Long) result;\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tLOG.warn(\"Unexpected error when accessing file handle limit\", t);\n\t\t\treturn -1L;\n\t\t}\n\t}\n\t\n\t/**\n\t * Logs a information about the environment, like code revision, current user, java version,\n\t * and JVM parameters.\n\t *\n\t * @param log The logger to log the information to.\n\t * @param componentName The component name to mention in the log.\n\t * @param commandLineArgs The arguments accompanying the starting the component.\n\t */\n\tpublic static void logEnvironmentInfo(Logger log, String componentName, String[] commandLineArgs) {\n\t\tif (log.isInfoEnabled()) {\n\t\t\tRevisionInformation rev = getRevisionInformation();\n\t\t\tString version = getVersion();\n\t\t\t\n\t\t\tString user = getUserRunning();\n\t\t\t\n\t\t\tString jvmVersion = getJvmVersion();\n\t\t\tString[] options = getJvmStartupOptionsArray();\n\t\t\t\n\t\t\tString javaHome = System.getenv(\"JAVA_HOME\");\n\t\t\t\n\t\t\tlong maxHeapMegabytes = getMaxJvmHeapMemory() >>> 20;\n\t\t\t\n\t\t\tlog.info(\"--------------------------------------------------------------------------------\");\n\t\t\tlog.info(\" Starting \" + componentName + \" (Version: \" + version + \", \"\n\t\t\t\t\t+ \"Rev:\" + rev.commitId + \", \" + \"Date:\" + rev.commitDate + \")\");\n\t\t\tlog.info(\" Current user: \" + user);\n\t\t\tlog.info(\" JVM: \" + jvmVersion);\n\t\t\tlog.info(\" Maximum heap size: \" + maxHeapMegabytes + \" MiBytes\");\n\t\t\tlog.info(\" JAVA_HOME: \" + (javaHome == null ? \"(not set)\" : javaHome));\n\n\n\t\t\tif (options.length == 0) {\n\t\t\t\tlog.info(\" JVM Options: (none)\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlog.info(\" JVM Options:\");\n\t\t\t\tfor (String s: options) {\n\t\t\t\t\tlog.info(\"    \" + s);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (commandLineArgs == null || commandLineArgs.length == 0) {\n\t\t\t\tlog.info(\" Program Arguments: (none)\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlog.info(\" Program Arguments:\");\n\t\t\t\tfor (String s: commandLineArgs) {\n\t\t\t\t\tlog.info(\"    \" + s);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlog.info(\" Classpath: \" + System.getProperty(\"java.class.path\"));\n\n\t\t\tlog.info(\"--------------------------------------------------------------------------------\");\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------------------------------\n\n\t/** Don't instantiate this class */\n\tprivate EnvironmentInformation() {}\n\n\t// --------------------------------------------------------------------------------------------\n\n\t/**\n\t * Revision information encapsulates information about the source code revision of the Flink\n\t * code.\n\t */\n\tpublic static class RevisionInformation {\n\t\t\n\t\t/** The git commit id (hash) */\n\t\tpublic final String commitId;\n\t\t\n\t\t/** The git commit date */\n\t\tpublic final String commitDate;\n\n\t\tpublic RevisionInformation(String commitId, String commitDate) {\n\t\t\tthis.commitId = commitId;\n\t\t\tthis.commitDate = commitDate;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/environment/Hardware.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.environment;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.BufferedReader;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Convenience class to extract hardware specifics of the computer executing this class\n */\npublic class Hardware {\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(Hardware.class);\n\t\n\tprivate static final String LINUX_MEMORY_INFO_PATH = \"/proc/meminfo\";\n\n\tprivate static final Pattern LINUX_MEMORY_REGEX = Pattern.compile(\"^MemTotal:\\\\s*(\\\\d+)\\\\s+kB$\");\n\t\n\n\t\n\t/**\n\t * Gets the number of CPU cores (hardware contexts) that the JVM has access to.\n\t * \n\t * @return The number of CPU cores.\n\t */\n\tpublic static int getNumberCPUCores() {\n\t\treturn Runtime.getRuntime().availableProcessors();\n\t}\n\t\n\t/**\n\t * Returns the size of the physical memory in bytes.\n\t * \n\t * @return the size of the physical memory in bytes or <code>-1</code> if\n\t *         the size could not be determined\n\t */\n\tpublic static long getSizeOfPhysicalMemory() {\n\t\tswitch (OperatingSystem.getCurrentOperatingSystem()) {\n\t\t\tcase LINUX:\n\t\t\t\treturn getSizeOfPhysicalMemoryForLinux();\n\t\t\t\t\n\t\t\tcase WINDOWS:\n\t\t\t\treturn getSizeOfPhysicalMemoryForWindows();\n\t\t\t\t\n\t\t\tcase MAC_OS:\n\t\t\t\treturn getSizeOfPhysicalMemoryForMac();\n\t\t\t\t\n\t\t\tcase FREE_BSD:\n\t\t\t\treturn getSizeOfPhysicalMemoryForFreeBSD();\n\t\t\t\t\n\t\t\tcase UNKNOWN:\n\t\t\t\tLOG.error(\"Cannot determine size of physical memory for unknown operating system\");\n\t\t\t\treturn -1;\n\t\t\t\t\n\t\t\tdefault:\n\t\t\t\tLOG.error(\"Unrecognized OS: \" + OperatingSystem.getCurrentOperatingSystem());\n\t\t\t\treturn -1;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the size of the physical memory in bytes on a Linux-based\n\t * operating system.\n\t * \n\t * @return the size of the physical memory in bytes or <code>-1</code> if\n\t *         the size could not be determined\n\t */\n\tprivate static long getSizeOfPhysicalMemoryForLinux() {\n\t\ttry (BufferedReader lineReader = new BufferedReader(new FileReader(LINUX_MEMORY_INFO_PATH))) {\n\t\t\tString line;\n\t\t\twhile ((line = lineReader.readLine()) != null) {\n\t\t\t\tMatcher matcher = LINUX_MEMORY_REGEX.matcher(line);\n\t\t\t\tif (matcher.matches()) {\n\t\t\t\t\tString totalMemory = matcher.group(1);\n\t\t\t\t\treturn Long.parseLong(totalMemory) * 1024L; // Convert from kilobyte to byte\n\t\t\t\t}\n\t\t\t}\n\t\t\t// expected line did not come\n\t\t\tLOG.error(\"Cannot determine the size of the physical memory for Linux host (using '/proc/meminfo'). Unexpected format.\");\n\t\t\treturn -1;\n\t\t}\n\t\tcatch (NumberFormatException e) {\n\t\t\tLOG.error(\"Cannot determine the size of the physical memory for Linux host (using '/proc/meminfo'). Unexpected format.\");\n\t\t\treturn -1;\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tLOG.error(\"Cannot determine the size of the physical memory for Linux host (using '/proc/meminfo'): \" + t.getMessage(), t);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the size of the physical memory in bytes on a Mac OS-based\n\t * operating system\n\t * \n\t * @return the size of the physical memory in bytes or <code>-1</code> if\n\t *         the size could not be determined\n\t */\n\tprivate static long getSizeOfPhysicalMemoryForMac() {\n\n\t\tBufferedReader bi = null;\n\n\t\ttry {\n\t\t\tProcess proc = Runtime.getRuntime().exec(\"sysctl hw.memsize\");\n\n\t\t\tbi = new BufferedReader(\n\t\t\t\t\tnew InputStreamReader(proc.getInputStream()));\n\n\t\t\tString line;\n\n\t\t\twhile ((line = bi.readLine()) != null) {\n\t\t\t\tif (line.startsWith(\"hw.memsize\")) {\n\t\t\t\t\tlong memsize = Long.parseLong(line.split(\":\")[1].trim());\n\t\t\t\t\tbi.close();\n\t\t\t\t\tproc.destroy();\n\t\t\t\t\treturn memsize;\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (Throwable t) {\n\t\t\tLOG.error(\"Cannot determine physical memory of machine for MacOS host: \" + t.getMessage(), t);\n\t\t\treturn -1;\n\t\t} finally {\n\t\t\tif (bi != null) {\n\t\t\t\ttry {\n\t\t\t\t\tbi.close();\n\t\t\t\t} catch (IOException ignored) {}\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/**\n\t * Returns the size of the physical memory in bytes on FreeBSD.\n\t * \n\t * @return the size of the physical memory in bytes or <code>-1</code> if\n\t *         the size could not be determined\n\t */\n\tprivate static long getSizeOfPhysicalMemoryForFreeBSD() {\n\t\tBufferedReader bi = null;\n\t\ttry {\n\t\t\tProcess proc = Runtime.getRuntime().exec(\"sysctl hw.physmem\");\n\n\t\t\tbi = new BufferedReader(new InputStreamReader(proc.getInputStream()));\n\n\t\t\tString line;\n\n\t\t\twhile ((line = bi.readLine()) != null) {\n\t\t\t\tif (line.startsWith(\"hw.physmem\")) {\n\t\t\t\t\tlong memsize = Long.parseLong(line.split(\":\")[1].trim());\n\t\t\t\t\tbi.close();\n\t\t\t\t\tproc.destroy();\n\t\t\t\t\treturn memsize;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tLOG.error(\"Cannot determine the size of the physical memory for FreeBSD host (using 'sysctl hw.physmem').\");\n\t\t\treturn -1;\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tLOG.error(\"Cannot determine the size of the physical memory for FreeBSD host (using 'sysctl hw.physmem'): \" + t.getMessage(), t);\n\t\t\treturn -1;\n\t\t}\n\t\tfinally {\n\t\t\tif (bi != null) {\n\t\t\t\ttry {\n\t\t\t\t\tbi.close();\n\t\t\t\t} catch (IOException ignored) {}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Returns the size of the physical memory in bytes on Windows.\n\t * \n\t * @return the size of the physical memory in bytes or <code>-1</code> if\n\t *         the size could not be determined\n\t */\n\tprivate static long getSizeOfPhysicalMemoryForWindows() {\n\t\tBufferedReader bi = null;\n\t\ttry {\n\t\t\tProcess proc = Runtime.getRuntime().exec(\"wmic memorychip get capacity\");\n\n\t\t\tbi = new BufferedReader(new InputStreamReader(proc.getInputStream()));\n\n\t\t\tString line = bi.readLine();\n\t\t\tif (line == null) {\n\t\t\t\treturn -1L;\n\t\t\t}\n\n\t\t\tif (!line.startsWith(\"Capacity\")) {\n\t\t\t\treturn -1L;\n\t\t\t}\n\n\t\t\tlong sizeOfPhyiscalMemory = 0L;\n\t\t\twhile ((line = bi.readLine()) != null) {\n\t\t\t\tif (line.isEmpty()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tline = line.replaceAll(\" \", \"\");\n\t\t\t\tsizeOfPhyiscalMemory += Long.parseLong(line);\n\t\t\t}\n\t\t\treturn sizeOfPhyiscalMemory;\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tLOG.error(\"Cannot determine the size of the physical memory for Windows host (using 'wmic memorychip'): \" + t.getMessage(), t);\n\t\t\treturn -1L;\n\t\t}\n\t\tfinally {\n\t\t\tif (bi != null) {\n\t\t\t\ttry {\n\t\t\t\t\tbi.close();\n\t\t\t\t} catch (Throwable ignored) {}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t// --------------------------------------------------------------------------------------------\n\t\n\tprivate Hardware() {}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/environment/HardwareDescription.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.environment;\n\nimport java.io.Serializable;\n\n/**\n * A hardware description describes the resources available to a task manager.\n */\npublic final class HardwareDescription implements Serializable {\n\n\tprivate static final long serialVersionUID = 3380016608300325361L;\n\n\t/** The number of CPU cores available to the JVM on the compute node. */\n\tprivate int numberOfCPUCores;\n\n\t/** The size of physical memory in bytes available on the compute node. */\n\tprivate long sizeOfPhysicalMemory;\n\n\t/** The size of the JVM heap memory */\n\tprivate long sizeOfJvmHeap;\n\t\n\t/** The size of the memory managed by the system for caching, hashing, sorting, ... */\n\tprivate long sizeOfManagedMemory;\n\n\t\n\t/**\n\t * Public default constructor used for serialization process.\n\t */\n\tpublic HardwareDescription() {}\n\n\t/**\n\t * Constructs a new hardware description object.\n\t * \n\t * @param numberOfCPUCores The number of CPU cores available to the JVM on the compute node. \n\t * @param sizeOfPhysicalMemory The size of physical memory in bytes available on the compute node.\n\t * @param sizeOfJvmHeap The size of the JVM heap memory.\n\t * @param sizeOfManagedMemory The size of the memory managed by the system for caching, hashing, sorting, ...\n\t */\n\tpublic HardwareDescription(int numberOfCPUCores, long sizeOfPhysicalMemory, long sizeOfJvmHeap, long sizeOfManagedMemory) {\n\t\tthis.numberOfCPUCores = numberOfCPUCores;\n\t\tthis.sizeOfPhysicalMemory = sizeOfPhysicalMemory;\n\t\tthis.sizeOfJvmHeap = sizeOfJvmHeap;\n\t\tthis.sizeOfManagedMemory = sizeOfManagedMemory;\n\t}\n\n\t/**\n\t * Returns the number of CPU cores available to the JVM on the compute node.\n\t * \n\t * @return the number of CPU cores available to the JVM on the compute node\n\t */\n\tpublic int getNumberOfCPUCores() {\n\t\treturn this.numberOfCPUCores;\n\t}\n\n\t/**\n\t * Returns the size of physical memory in bytes available on the compute node.\n\t * \n\t * @return the size of physical memory in bytes available on the compute node\n\t */\n\tpublic long getSizeOfPhysicalMemory() {\n\t\treturn this.sizeOfPhysicalMemory;\n\t}\n\n\t/**\n\t * Returns the size of the JVM heap memory\n\t * \n\t * @return The size of the JVM heap memory\n\t */\n\tpublic long getSizeOfJvmHeap() {\n\t\treturn this.sizeOfJvmHeap;\n\t}\n\t\n\t/**\n\t * Returns the size of the memory managed by the system for caching, hashing, sorting, ...\n\t * \n\t * @return The size of the memory managed by the system.\n\t */\n\tpublic long getSizeOfManagedMemory() {\n\t\treturn this.sizeOfManagedMemory;\n\t}\n\t\n\t// --------------------------------------------------------------------------------------------\n\t// Utils\n\t// --------------------------------------------------------------------------------------------\n\t\n\t@Override\n\tpublic String toString() {\n\t\treturn String.format(\"cores=%d, physMem=%d, heap=%d, managed=%d\", \n\t\t\t\tnumberOfCPUCores, sizeOfPhysicalMemory, sizeOfJvmHeap, sizeOfManagedMemory);\n\t}\n\t\n\t// --------------------------------------------------------------------------------------------\n\t// Factory\n\t// --------------------------------------------------------------------------------------------\n\t\n\tpublic static HardwareDescription extractFromSystem(long managedMemory) {\n\t\tfinal int numberOfCPUCores = Hardware.getNumberCPUCores();\n\t\tfinal long sizeOfJvmHeap = Runtime.getRuntime().maxMemory();\n\t\tfinal long sizeOfPhysicalMemory = Hardware.getSizeOfPhysicalMemory();\n\t\t\n\t\treturn new HardwareDescription(numberOfCPUCores, sizeOfPhysicalMemory, sizeOfJvmHeap, managedMemory);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/environment/OperatingSystem.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.environment;\n\n\n\n/**\n * An enumeration indicating the operating system that the JVM runs on.\n */\n\npublic enum OperatingSystem {\n\t\n\tLINUX,\n\tWINDOWS,\n\tMAC_OS,\n\tFREE_BSD,\n\tUNKNOWN;\n\t\n\t// ------------------------------------------------------------------------\n\t\n\t/**\n\t * Gets the operating system that the JVM runs on from the java system properties.\n\t * this method returns <tt>UNKNOWN</tt>, if the operating system was not successfully determined.\n\t * \n\t * @return The enum constant for the operating system, or <tt>UNKNOWN</tt>, if it was not possible to determine.\n\t */\n\tpublic static OperatingSystem getCurrentOperatingSystem() {\n\t\treturn os;\n\t}\n\t\n\t/**\n\t * Checks whether the operating system this JVM runs on is Windows.\n\t * \n\t * @return <code>true</code> if the operating system this JVM runs on is\n\t *         Windows, <code>false</code> otherwise\n\t */\n\tpublic static boolean isWindows() {\n\t\treturn getCurrentOperatingSystem() == WINDOWS;\n\t}\n\n\t/**\n\t * Checks whether the operating system this JVM runs on is Linux.\n\t * \n\t * @return <code>true</code> if the operating system this JVM runs on is\n\t *         Linux, <code>false</code> otherwise\n\t */\n\tpublic static boolean isLinux() {\n\t\treturn getCurrentOperatingSystem() == LINUX;\n\t}\n\n\t/**\n\t * Checks whether the operating system this JVM runs on is Windows.\n\t * \n\t * @return <code>true</code> if the operating system this JVM runs on is\n\t *         Windows, <code>false</code> otherwise\n\t */\n\tpublic static boolean isMac() {\n\t\treturn getCurrentOperatingSystem() == MAC_OS;\n\t}\n\n\t/**\n\t * Checks whether the operating system this JVM runs on is FreeBSD.\n\t * \n\t * @return <code>true</code> if the operating system this JVM runs on is\n\t *         FreeBSD, <code>false</code> otherwise\n\t */\n\tpublic static boolean isFreeBSD() {\n\t\treturn getCurrentOperatingSystem() == FREE_BSD;\n\t}\n\t\n\t/**\n\t * The enum constant for the operating system.\n\t */\n\tprivate static final OperatingSystem os = readOSFromSystemProperties();\n\t\n\t/**\n\t * Parses the operating system that the JVM runs on from the java system properties.\n\t * If the operating system was not successfully determined, this method returns {@code UNKNOWN}.\n\t * \n\t * @return The enum constant for the operating system, or {@code UNKNOWN}, if it was not possible to determine.\n\t */\n\tprivate static OperatingSystem readOSFromSystemProperties() {\n\t\tString osName = System.getProperty(OS_KEY);\n\t\t\n\t\tif (osName.startsWith(LINUX_OS_PREFIX)) {\n\t\t\treturn LINUX;\n\t\t}\n\t\tif (osName.startsWith(WINDOWS_OS_PREFIX)) {\n\t\t\treturn WINDOWS;\n\t\t}\n\t\tif (osName.startsWith(MAC_OS_PREFIX)) {\n\t\t\treturn MAC_OS;\n\t\t}\n\t\tif (osName.startsWith(FREEBSD_OS_PREFIX)) {\n\t\t\treturn FREE_BSD;\n\t\t}\n\t\t\n\t\treturn UNKNOWN;\n\t}\n\t\n\t// --------------------------------------------------------------------------------------------\n\t//  Constants to extract the OS type from the java environment \n\t// --------------------------------------------------------------------------------------------\n\t\n\t/**\n\t * The key to extract the operating system name from the system properties.\n\t */\n\tprivate static final String OS_KEY = \"os.name\";\n\n\t/**\n\t * The expected prefix for Linux operating systems.\n\t */\n\tprivate static final String LINUX_OS_PREFIX = \"Linux\";\n\n\t/**\n\t * The expected prefix for Windows operating systems.\n\t */\n\tprivate static final String WINDOWS_OS_PREFIX = \"Windows\";\n\n\t/**\n\t * The expected prefix for Mac OS operating systems.\n\t */\n\tprivate static final String MAC_OS_PREFIX = \"Mac\";\n\n\t/**\n\t * The expected prefix for FreeBSD.\n\t */\n\tprivate static final String FREEBSD_OS_PREFIX = \"FreeBSD\";\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/KVIterator.java",
    "content": "\npackage io.mycat.memory.unsafe;\n\nimport java.io.IOException;\n\npublic abstract class KVIterator<K, V> {\n\n  public abstract boolean next() throws IOException;\n\n  public abstract K getKey();\n\n  public abstract V getValue();\n\n  public abstract void close();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/Platform.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe;\n\nimport io.mycat.MycatServer;\nimport io.mycat.memory.unsafe.utils.BytesTools;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport sun.misc.Cleaner;\nimport sun.misc.Unsafe;\nimport sun.nio.ch.DirectBuffer;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.security.AccessController;\nimport java.security.PrivilegedAction;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic final class Platform {\n\n    private final static Logger logger = LoggerFactory.getLogger(Platform.class);\n    private static final Pattern MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN =\n            Pattern.compile(\"\\\\s*-XX:MaxDirectMemorySize\\\\s*=\\\\s*([0-9]+)\\\\s*([kKmMgG]?)\\\\s*$\");\n    private static final Unsafe _UNSAFE;\n\n    public static final int BYTE_ARRAY_OFFSET;\n\n    public static final int SHORT_ARRAY_OFFSET;\n\n    public static final int INT_ARRAY_OFFSET;\n\n    public static final int LONG_ARRAY_OFFSET;\n\n    public static final int FLOAT_ARRAY_OFFSET;\n\n    public static final int DOUBLE_ARRAY_OFFSET;\n\n    private static final long MAX_DIRECT_MEMORY;\n\n    private static final boolean unaligned;\n\n    public static final boolean littleEndian = ByteOrder.nativeOrder()\n            .equals(ByteOrder.LITTLE_ENDIAN);\n\n    public static final long sqlTimeout =  MycatServer.getInstance().getConfig().getSystem().getSqlExecuteTimeout() * 1000L;\n    static {\n        boolean _unaligned;\n        // use reflection to access unaligned field\n        try {\n            Class<?> bitsClass =\n                    Class.forName(\"java.nio.Bits\", false, ClassLoader.getSystemClassLoader());\n            Method unalignedMethod = bitsClass.getDeclaredMethod(\"unaligned\");\n            unalignedMethod.setAccessible(true);\n            _unaligned = Boolean.TRUE.equals(unalignedMethod.invoke(null));\n        } catch (Throwable t) {\n            // We at least know x86 and x64 support unaligned access.\n            String arch = System.getProperty(\"os.arch\", \"\");\n            //noinspection DynamicRegexReplaceableByCompiledPattern\n            _unaligned = arch.matches(\"^(i[3-6]86|x86(_64)?|x64|amd64)$\");\n        }\n        unaligned = _unaligned;\n        MAX_DIRECT_MEMORY = maxDirectMemory();\n\n    }\n\n\n    private static ClassLoader getSystemClassLoader() {\n        return System.getSecurityManager() == null ? ClassLoader.getSystemClassLoader() : (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {\n            public ClassLoader run() {\n                return ClassLoader.getSystemClassLoader();\n            }\n        });\n    }\n\n    /**\n     * GET  MaxDirectMemory Size,from Netty Project!\n     */\n    private static long maxDirectMemory() {\n        long maxDirectMemory = 0L;\n        Class t;\n        try {\n            t = Class.forName(\"sun.misc.VM\", true, getSystemClassLoader());\n            Method runtimeClass = t.getDeclaredMethod(\"maxDirectMemory\", new Class[0]);\n            maxDirectMemory = ((Number) runtimeClass.invoke((Object) null, new Object[0])).longValue();\n        } catch (Throwable var8) {\n            ;\n        }\n\n        if (maxDirectMemory > 0L) {\n            return maxDirectMemory;\n        } else {\n            try {\n                t = Class.forName(\"java.lang.management.ManagementFactory\", true, getSystemClassLoader());\n                Class var10 = Class.forName(\"java.lang.management.RuntimeMXBean\", true, getSystemClassLoader());\n                Object runtime = t.getDeclaredMethod(\"getRuntimeMXBean\", new Class[0]).invoke((Object) null, new Object[0]);\n                List vmArgs = (List) var10.getDeclaredMethod(\"getInputArguments\", new Class[0]).invoke(runtime, new Object[0]);\n\n                label41:\n                for (int i = vmArgs.size() - 1; i >= 0; --i) {\n                    Matcher m = MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN.matcher((CharSequence) vmArgs.get(i));\n                    if (m.matches()) {\n                        maxDirectMemory = Long.parseLong(m.group(1));\n                        switch (m.group(2).charAt(0)) {\n                            case 'G':\n                            case 'g':\n                                maxDirectMemory *= 1073741824L;\n                                break label41;\n                            case 'K':\n                            case 'k':\n                                maxDirectMemory *= 1024L;\n                                break label41;\n                            case 'M':\n                            case 'm':\n                                maxDirectMemory *= 1048576L;\n                            default:\n                                break label41;\n                        }\n                    }\n                }\n            } catch (Throwable var9) {\n                logger.error(var9.getMessage());\n            }\n\n            if (maxDirectMemory <= 0L) {\n                maxDirectMemory = Runtime.getRuntime().maxMemory();\n                //System.out.println(\"maxDirectMemory: {} bytes (maybe)\" + Long.valueOf(maxDirectMemory));\n            } else {\n                //System.out.println(\"maxDirectMemory: {} bytes\" + Long.valueOf(maxDirectMemory));\n            }\n            return maxDirectMemory;\n        }\n    }\n\n    public static long getMaxDirectMemory() {\n        return MAX_DIRECT_MEMORY;\n    }\n\n    public static long getMaxHeapMemory() {\n        return Runtime.getRuntime().maxMemory();\n    }\n\n    /**\n     * @return true when running JVM is having sun's Unsafe package available in it and underlying\n     * system having unaligned-access capability.\n     */\n    public static boolean unaligned() {\n        return unaligned;\n    }\n\n    public static int getInt(Object object, long offset) {\n        return _UNSAFE.getInt(object, offset);\n    }\n\n    public static void putInt(Object object, long offset, int value) {\n        _UNSAFE.putInt(object, offset, value);\n    }\n\n    public static boolean getBoolean(Object object, long offset) {\n        return _UNSAFE.getBoolean(object, offset);\n    }\n\n    public static void putBoolean(Object object, long offset, boolean value) {\n        _UNSAFE.putBoolean(object, offset, value);\n    }\n\n    public static byte getByte(Object object, long offset) {\n        return _UNSAFE.getByte(object, offset);\n    }\n\n    public static void putByte(Object object, long offset, byte value) {\n        _UNSAFE.putByte(object, offset, value);\n    }\n\n    public static short getShort(Object object, long offset) {\n        return _UNSAFE.getShort(object, offset);\n    }\n\n    public static void putShort(Object object, long offset, short value) {\n        _UNSAFE.putShort(object, offset, value);\n    }\n\n    public static long getLong(Object object, long offset) {\n        return _UNSAFE.getLong(object, offset);\n    }\n\n    public static void putLong(Object object, long offset, long value) {\n        _UNSAFE.putLong(object, offset, value);\n    }\n\n    public static float getFloat(Object object, long offset) {\n        return _UNSAFE.getFloat(object, offset);\n    }\n\n    public static void putFloat(Object object, long offset, float value) {\n        _UNSAFE.putFloat(object, offset, value);\n    }\n\n    public static double getDouble(Object object, long offset) {\n        return _UNSAFE.getDouble(object, offset);\n    }\n\n    public static void putDouble(Object object, long offset, double value) {\n        _UNSAFE.putDouble(object, offset, value);\n    }\n\n\n    public static Object getObjectVolatile(Object object, long offset) {\n        return _UNSAFE.getObjectVolatile(object, offset);\n    }\n\n    public static void putObjectVolatile(Object object, long offset, Object value) {\n        _UNSAFE.putObjectVolatile(object, offset, value);\n    }\n\n    public static long allocateMemory(long size) {\n        return _UNSAFE.allocateMemory(size);\n    }\n\n    public static void freeMemory(long address) {\n        _UNSAFE.freeMemory(address);\n    }\n\n    public static long reallocateMemory(long address, long oldSize, long newSize) {\n        long newMemory = _UNSAFE.allocateMemory(newSize);\n        copyMemory(null, address, null, newMemory, oldSize);\n        freeMemory(address);\n        return newMemory;\n    }\n\n    /**\n     * Uses internal JDK APIs to allocate a DirectByteBuffer while ignoring the JVM's\n     * MaxDirectMemorySize limit (the default limit is too low and we do not want to require users\n     * to increase it).\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static ByteBuffer allocateDirectBuffer(int size) {\n        try {\n            Class cls = Class.forName(\"java.nio.DirectByteBuffer\");\n            Constructor constructor = cls.getDeclaredConstructor(Long.TYPE, Integer.TYPE);\n            constructor.setAccessible(true);\n            Field cleanerField = cls.getDeclaredField(\"cleaner\");\n            cleanerField.setAccessible(true);\n            final long memory = allocateMemory(size);\n            ByteBuffer buffer = (ByteBuffer) constructor.newInstance(memory, size);\n            Cleaner cleaner = Cleaner.create(buffer, new Runnable() {\n                @Override\n                public void run() {\n                    freeMemory(memory);\n                }\n            });\n            cleanerField.set(buffer, cleaner);\n            return buffer;\n        } catch (Exception e) {\n            throwException(e);\n        }\n        throw new IllegalStateException(\"unreachable\");\n    }\n\n    public static void setMemory(long address, byte value, long size) {\n        _UNSAFE.setMemory(address, size, value);\n    }\n\n    public static void copyMemory(\n            Object src, long srcOffset, Object dst, long dstOffset, long length) {\n        // Check if dstOffset is before or after srcOffset to determine if we should copy\n        // forward or backwards. This is necessary in case src and dst overlap.\n        if (dstOffset < srcOffset) {\n            while (length > 0) {\n                long size = Math.min(length, UNSAFE_COPY_THRESHOLD);\n                _UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size);\n                length -= size;\n                srcOffset += size;\n                dstOffset += size;\n            }\n        } else {\n            srcOffset += length;\n            dstOffset += length;\n            while (length > 0) {  // if backend db run time out ,this will be endless loop\n                long size = Math.min(length, UNSAFE_COPY_THRESHOLD);\n                long lbegin = System.currentTimeMillis();\n                srcOffset -= size;\n                dstOffset -= size;\n                _UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size);\n                length -= size;\n                long l = System.currentTimeMillis() - lbegin;\n                if(l > sqlTimeout) {   // when sql run timeout,break the loop\n                    logger.error(\"copyMemory timeout.loop(seconds):\" + String.valueOf(l / 1000));\n\n                    break;\n                }\n            }\n\n        }\n    }\n\n    /**\n     * Raises an exception bypassing compiler checks for checked exceptions.\n     */\n    public static void throwException(Throwable t) {\n        _UNSAFE.throwException(t);\n    }\n\n    /**\n     * Limits the number of bytes to copy per {@link Unsafe#copyMemory(long, long, long)} to\n     * allow safepoint polling during a large copy.\n     */\n    private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;\n\n    static {\n        Unsafe unsafe;\n        try {\n            Field unsafeField = Unsafe.class.getDeclaredField(\"theUnsafe\");\n            unsafeField.setAccessible(true);\n            unsafe = (Unsafe) unsafeField.get(null);\n        } catch (Throwable cause) {\n            unsafe = null;\n        }\n        _UNSAFE = unsafe;\n\n        if (_UNSAFE != null) {\n            BYTE_ARRAY_OFFSET = _UNSAFE.arrayBaseOffset(byte[].class);\n            SHORT_ARRAY_OFFSET = _UNSAFE.arrayBaseOffset(short[].class);\n            INT_ARRAY_OFFSET = _UNSAFE.arrayBaseOffset(int[].class);\n            LONG_ARRAY_OFFSET = _UNSAFE.arrayBaseOffset(long[].class);\n            FLOAT_ARRAY_OFFSET = _UNSAFE.arrayBaseOffset(float[].class);\n            DOUBLE_ARRAY_OFFSET = _UNSAFE.arrayBaseOffset(double[].class);\n        } else {\n            BYTE_ARRAY_OFFSET = 0;\n            SHORT_ARRAY_OFFSET = 0;\n            INT_ARRAY_OFFSET = 0;\n            LONG_ARRAY_OFFSET = 0;\n            FLOAT_ARRAY_OFFSET = 0;\n            DOUBLE_ARRAY_OFFSET = 0;\n        }\n    }\n\n    public static long objectFieldOffset(Field field) {\n        return _UNSAFE.objectFieldOffset(field);\n    }\n\n    public static void putOrderedLong(Object object, long valueOffset, long initialValue) {\n        _UNSAFE.putOrderedLong(object, valueOffset, initialValue);\n    }\n\n    public static void putLongVolatile(Object object, long valueOffset, long value) {\n        _UNSAFE.putLongVolatile(object, valueOffset, value);\n    }\n\n    public static boolean compareAndSwapLong(Object object, long valueOffset, long expectedValue, long newValue) {\n        return _UNSAFE.compareAndSwapLong(object, valueOffset, expectedValue, newValue);\n    }\n\n    public static int arrayBaseOffset(Class aClass) {\n        return _UNSAFE.arrayBaseOffset(aClass);\n    }\n\n    public static int arrayIndexScale(Class aClass) {\n        return _UNSAFE.arrayIndexScale(aClass);\n    }\n\n    public static void putOrderedInt(Object availableBuffer, long bufferAddress, int flag) {\n        _UNSAFE.putOrderedInt(availableBuffer, bufferAddress, flag);\n    }\n\n    public static int getIntVolatile(Object availableBuffer, long bufferAddress) {\n        return _UNSAFE.getIntVolatile(availableBuffer, bufferAddress);\n    }\n\n    public static Object getObject(Object entries, long l) {\n        return _UNSAFE.getObject(entries, l);\n    }\n\n    public static char getChar(Object baseObj, long l) {\n        return _UNSAFE.getChar(baseObj, l);\n    }\n\n    public static void putChar(Object baseObj, long l, char value) {\n        _UNSAFE.putChar(baseObj, l, value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/array/ByteArrayMethods.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.array;\n\n\nimport io.mycat.memory.unsafe.Platform;\n\npublic class ByteArrayMethods {\n\n  private ByteArrayMethods() {\n    // Private constructor, since this class only contains static methods.\n  }\n\n  /** Returns the next number greater or equal num that is power of 2. */\n  public static long nextPowerOf2(long num) {\n    final long highBit = Long.highestOneBit(num);\n    return (highBit == num) ? num : highBit << 1;\n  }\n\n  public static int roundNumberOfBytesToNearestWord(int numBytes) {\n    int remainder = numBytes & 0x07;  // This is equivalent to `numBytes % 8`\n    if (remainder == 0) {\n      return numBytes;\n    } else {\n      return numBytes + (8 - remainder);\n    }\n  }\n\n  /**\n   * Optimized byte array equality check for byte arrays.\n   * @return true if the arrays are equal, false otherwise\n   */\n  public static boolean arrayEquals(\n      Object leftBase, long leftOffset, Object rightBase, long rightOffset, final long length) {\n    int i = 0;\n    while (i <= length - 8) {\n      if (Platform.getLong(leftBase, leftOffset + i) !=\n        Platform.getLong(rightBase, rightOffset + i)) {\n        return false;\n      }\n      i += 8;\n    }\n    while (i < length) {\n      if (Platform.getByte(leftBase, leftOffset + i) !=\n        Platform.getByte(rightBase, rightOffset + i)) {\n        return false;\n      }\n      i += 1;\n    }\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/array/CharArray.java",
    "content": "package io.mycat.memory.unsafe.array;\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.memory.MemoryBlock;\nimport io.mycat.memory.unsafe.memory.mm.MemoryConsumer;\n\n/**\n * @author Hash Zhang\n * @version 1.0.0\n * @date 2016/8/8\n */\npublic class CharArray {\n    private static final long WIDTH = 2;\n    private final MemoryConsumer memoryConsumer;\n\n    private final MemoryBlock memory;\n    private final Object baseObj;\n    private final long baseOffset;\n\n    private final long length;\n\n    public CharArray(MemoryBlock memory,MemoryConsumer memoryConsumer) {\n        assert memory.size() < (long) Integer.MAX_VALUE * 2 : \"Array size > 4 billion elements\";\n        this.memory = memory;\n        this.baseObj = memory.getBaseObject();\n        this.baseOffset = memory.getBaseOffset();\n        this.length = memory.size() / WIDTH;\n        this.memoryConsumer = memoryConsumer;\n    }\n\n\n    public MemoryBlock memoryBlock() {\n        return memory;\n    }\n\n    public Object getBaseObject() {\n        return baseObj;\n    }\n\n    public long getBaseOffset() {\n        return baseOffset;\n    }\n\n    /**\n     * Returns the number of elements this array can hold.\n     */\n    public long size() {\n        return length;\n    }\n\n    /**\n     * Fill this all with 0L.\n     */\n    public void zeroOut() {\n        for (long off = baseOffset; off < baseOffset + length * WIDTH; off += WIDTH) {\n            Platform.putLong(baseObj, off, 0);\n        }\n    }\n\n    /**\n     * Sets the value at position {@code index}.\n     */\n    public void set(int index, char value) {\n        assert index >= 0 : \"index (\" + index + \") should >= 0\";\n        assert index < length : \"index (\" + index + \") should < length (\" + length + \")\";\n        Platform.putChar(baseObj, baseOffset + index * WIDTH, value);\n    }\n\n    /**\n     * Returns the value at position {@code index}.\n     */\n    public char get(int index) {\n        assert index >= 0 : \"index (\" + index + \") should >= 0\";\n        assert index < length : \"index (\" + index + \") should < length (\" + length + \")\";\n        return Platform.getChar(baseObj, baseOffset + index * WIDTH);\n    }\n\n    public String toString() {\n        StringBuilder stringBuilder = new StringBuilder((int) this.length);\n        for (int i = 0; i < this.length; i++) {\n            stringBuilder.append(get(i));\n        }\n        return stringBuilder.toString();\n    }\n\n    //todo:实现from string，使字符串数组可变\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/array/LongArray.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.array;\n\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.memory.MemoryBlock;\n\n/**\n * An array of long values. Compared with native JVM arrays, this:\n * <ul>\n *   <li>supports using both in-heap and off-heap memory</li>\n *   <li>has no bound checking, and thus can crash the JVM process when assert is turned off</li>\n * </ul>\n */\npublic final class LongArray {\n\n  // This is a long so that we perform long multiplications when computing offsets.\n  private static final long WIDTH = 8;\n\n  private final MemoryBlock memory;\n  private final Object baseObj;\n  private final long baseOffset;\n\n  private final long length;\n\n  public LongArray(MemoryBlock memory) {\n    assert memory.size() < (long) Integer.MAX_VALUE * 8: \"Array size > 4 billion elements\";\n    this.memory = memory;\n    this.baseObj = memory.getBaseObject();\n    this.baseOffset = memory.getBaseOffset();\n    this.length = memory.size() / WIDTH;\n  }\n\n\n\n  public MemoryBlock memoryBlock() {\n    return memory;\n  }\n\n  public Object getBaseObject() {\n    return baseObj;\n  }\n\n  public long getBaseOffset() {\n    return baseOffset;\n  }\n\n  /**\n   * Returns the number of elements this array can hold.\n   */\n  public long size() {\n    return length;\n  }\n\n  /**\n   * Fill this all with 0L.\n   */\n  public void zeroOut() {\n    for (long off = baseOffset; off < baseOffset + length * WIDTH; off += WIDTH) {\n      Platform.putLong(baseObj, off, 0);\n    }\n  }\n\n  /**\n   * Sets the value at position {@code index}.\n   */\n  public void set(int index, long value) {\n    assert index >= 0 : \"index (\" + index + \") should >= 0\";\n    assert index < length : \"index (\" + index + \") should < length (\" + length + \")\";\n    Platform.putLong(baseObj, baseOffset + index * WIDTH, value);\n  }\n\n  /**\n   * Returns the value at position {@code index}.\n   */\n  public long get(int index) {\n    assert index >= 0 : \"index (\" + index + \") should >= 0\";\n    assert index < length : \"index (\" + index + \") should < length (\" + length + \")\";\n    return Platform.getLong(baseObj, baseOffset + index * WIDTH);\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/bitset/BitSetMethods.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.bitset;\n\n\nimport io.mycat.memory.unsafe.Platform;\n\n/**\n * Methods for working with fixed-size uncompressed bitsets.\n *\n * We assume that the bitset data is word-aligned (that is, a multiple of 8 bytes in length).\n *\n * Each bit occupies exactly one bit of storage.\n */\npublic final class BitSetMethods {\n\n  private static final long WORD_SIZE = 8;\n\n  private BitSetMethods() {\n    // Make the default constructor private, since this only holds static methods.\n  }\n\n  /**\n   * Sets the bit at the specified index to {@code true}.\n   */\n  public static void set(Object baseObject, long baseOffset, int index) {\n    assert index >= 0 : \"index (\" + index + \") should >= 0\";\n    final long mask = 1L << (index & 0x3f);  // mod 64 and shift\n    final long wordOffset = baseOffset + (index >> 6) * WORD_SIZE;\n    final long word = Platform.getLong(baseObject, wordOffset);\n    Platform.putLong(baseObject, wordOffset, word | mask);\n  }\n\n  /**\n   * Sets the bit at the specified index to {@code false}.\n   */\n  public static void unset(Object baseObject, long baseOffset, int index) {\n    assert index >= 0 : \"index (\" + index + \") should >= 0\";\n    final long mask = 1L << (index & 0x3f);  // mod 64 and shift\n    final long wordOffset = baseOffset + (index >> 6) * WORD_SIZE;\n    final long word = Platform.getLong(baseObject, wordOffset);\n    Platform.putLong(baseObject, wordOffset, word & ~mask);\n  }\n\n  /**\n   * Returns {@code true} if the bit is set at the specified index.\n   */\n  public static boolean isSet(Object baseObject, long baseOffset, int index) {\n    assert index >= 0 : \"index (\" + index + \") should >= 0\";\n    final long mask = 1L << (index & 0x3f);  // mod 64 and shift\n    final long wordOffset = baseOffset + (index >> 6) * WORD_SIZE;\n    final long word = Platform.getLong(baseObject, wordOffset);\n    return (word & mask) != 0;\n  }\n\n  /**\n   * Returns {@code true} if any bit is set.\n   */\n  public static boolean anySet(Object baseObject, long baseOffset, long bitSetWidthInWords) {\n    long addr = baseOffset;\n    for (int i = 0; i < bitSetWidthInWords; i++, addr += WORD_SIZE) {\n      if (Platform.getLong(baseObject, addr) != 0) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  /**\n   * Returns the index of the first bit that is set to true that occurs on or after the\n   * specified starting index. If no such bit exists then {@code -1} is returned.\n   * <p>\n   * To iterate over the true bits in a BitSet, use the following loop:\n   * <pre>\n   * <code>\n   *  for (long i = bs.nextSetBit(0, sizeInWords); i &gt;= 0;\n   *    i = bs.nextSetBit(i + 1, sizeInWords)) {\n   *    // operate on index i here\n   *  }\n   * </code>\n   * </pre>\n   *\n   * @param fromIndex the index to start checking from (inclusive)\n   * @param bitsetSizeInWords the size of the bitset, measured in 8-byte words\n   * @return the index of the next set bit, or -1 if there is no such bit\n   */\n  public static int nextSetBit(\n      Object baseObject,\n      long baseOffset,\n      int fromIndex,\n      int bitsetSizeInWords) {\n    int wi = fromIndex >> 6;\n    if (wi >= bitsetSizeInWords) {\n      return -1;\n    }\n\n    // Try to find the next set bit in the current word\n    final int subIndex = fromIndex & 0x3f;\n    long word = Platform.getLong(baseObject, baseOffset + wi * WORD_SIZE) >> subIndex;\n    if (word != 0) {\n      return (wi << 6) + subIndex + Long.numberOfTrailingZeros(word);\n    }\n\n    // Find the next set bit in the rest of the words\n    wi += 1;\n    while (wi < bitsetSizeInWords) {\n      word = Platform.getLong(baseObject, baseOffset + wi * WORD_SIZE);\n      if (word != 0) {\n        return (wi << 6) + Long.numberOfTrailingZeros(word);\n      }\n      wi += 1;\n    }\n\n    return -1;\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/hash/Murmur3_x86_32.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.hash;\n\n\nimport io.mycat.memory.unsafe.Platform;\n\n/**\n * 32-bit Murmur3 hasher.  This is based on Guava's Murmur3_32HashFunction.\n */\npublic final class Murmur3_x86_32 {\n  private static final int C1 = 0xcc9e2d51;\n  private static final int C2 = 0x1b873593;\n\n  private final int seed;\n\n  public Murmur3_x86_32(int seed) {\n    this.seed = seed;\n  }\n\n  @Override\n  public String toString() {\n    return \"Murmur3_32(seed=\" + seed + \")\";\n  }\n\n  public int hashInt(int input) {\n    return hashInt(input, seed);\n  }\n\n  public static int hashInt(int input, int seed) {\n    int k1 = mixK1(input);\n    int h1 = mixH1(seed, k1);\n\n    return fmix(h1, 4);\n  }\n\n  public int hashUnsafeWords(Object base, long offset, int lengthInBytes) {\n    return hashUnsafeWords(base, offset, lengthInBytes, seed);\n  }\n\n  public static int hashUnsafeWords(Object base, long offset, int lengthInBytes, int seed) {\n    // This is based on Guava's `Murmur32_Hasher.processRemaining(ByteBuffer)` method.\n    assert (lengthInBytes % 8 == 0): \"lengthInBytes must be a multiple of 8 (word-aligned)\";\n    int h1 = hashBytesByInt(base, offset, lengthInBytes, seed);\n    return fmix(h1, lengthInBytes);\n  }\n\n  public static int hashUnsafeBytes(Object base, long offset, int lengthInBytes, int seed) {\n    assert (lengthInBytes >= 0): \"lengthInBytes cannot be negative\";\n    int lengthAligned = lengthInBytes - lengthInBytes % 4;\n    int h1 = hashBytesByInt(base, offset, lengthAligned, seed);\n    for (int i = lengthAligned; i < lengthInBytes; i++) {\n      int halfWord = Platform.getByte(base, offset + i);\n      int k1 = mixK1(halfWord);\n      h1 = mixH1(h1, k1);\n    }\n    return fmix(h1, lengthInBytes);\n  }\n\n  private static int hashBytesByInt(Object base, long offset, int lengthInBytes, int seed) {\n    assert (lengthInBytes % 4 == 0);\n    int h1 = seed;\n    for (int i = 0; i < lengthInBytes; i += 4) {\n      int halfWord = Platform.getInt(base, offset + i);\n      int k1 = mixK1(halfWord);\n      h1 = mixH1(h1, k1);\n    }\n    return h1;\n  }\n\n  public int hashLong(long input) {\n    return hashLong(input, seed);\n  }\n\n  public static int hashLong(long input, int seed) {\n    int low = (int) input;\n    int high = (int) (input >>> 32);\n\n    int k1 = mixK1(low);\n    int h1 = mixH1(seed, k1);\n\n    k1 = mixK1(high);\n    h1 = mixH1(h1, k1);\n\n    return fmix(h1, 8);\n  }\n\n  private static int mixK1(int k1) {\n    k1 *= C1;\n    k1 = Integer.rotateLeft(k1, 15);\n    k1 *= C2;\n    return k1;\n  }\n\n  private static int mixH1(int h1, int k1) {\n    h1 ^= k1;\n    h1 = Integer.rotateLeft(h1, 13);\n    h1 = h1 * 5 + 0xe6546b64;\n    return h1;\n  }\n\n  // Finalization mix - force all bits of a hash block to avalanche\n  private static int fmix(int h1, int length) {\n    h1 ^= length;\n    h1 ^= h1 >>> 16;\n    h1 *= 0x85ebca6b;\n    h1 ^= h1 >>> 13;\n    h1 *= 0xc2b2ae35;\n    h1 ^= h1 >>> 16;\n    return h1;\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/map/BytesToBytesMap.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.map;\n\nimport com.google.common.annotations.VisibleForTesting;\nimport com.google.common.io.Closeables;\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.array.ByteArrayMethods;\nimport io.mycat.memory.unsafe.array.LongArray;\nimport io.mycat.memory.unsafe.hash.Murmur3_x86_32;\nimport io.mycat.memory.unsafe.memory.MemoryBlock;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.MemoryConsumer;\nimport io.mycat.memory.unsafe.storage.DataNodeDiskManager;\nimport io.mycat.memory.unsafe.storage.SerializerManager;\nimport io.mycat.memory.unsafe.utils.sort.UnsafeSorterSpillReader;\nimport io.mycat.memory.unsafe.utils.sort.UnsafeSorterSpillWriter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.annotation.Nullable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Iterator;\nimport java.util.LinkedList;\n\n/**\n * An append-only hash map where keys and values are contiguous regions of bytes.\n *\n * This is backed by a power-of-2-sized hash table, using quadratic probing with triangular numbers,\n * which is guaranteed to exhaust the space.\n *\n * The map can support up to 2^29 keys. If the key cardinality is higher than this, you should\n * probably be using sorting instead of hashing for better cache locality.\n *\n * The key and values under the hood are stored together, in the following format:\n *   Bytes 0 to 4: len(k) (key length in bytes) + len(v) (value length in bytes) + 4\n *   Bytes 4 to 8: len(k)\n *   Bytes 8 to 8 + len(k): key data\n *   Bytes 8 + len(k) to 8 + len(k) + len(v): value data\n *   Bytes 8 + len(k) + len(v) to 8 + len(k) + len(v) + 8: pointer to next pair\n *\n * This means that the first four bytes store the entire record (key + value) length. This format\n * is compatible with {@link io.mycat.memory.unsafe.utils.sort.UnsafeExternalSorter},\n * so we can pass records from this map directly into the sorter to sort records in place.\n */\npublic final class BytesToBytesMap extends MemoryConsumer {\n\n  private final Logger logger = LoggerFactory.getLogger(BytesToBytesMap.class);\n\n  private static final HashMapGrowthStrategy growthStrategy = HashMapGrowthStrategy.DOUBLING;\n\n  private final DataNodeMemoryManager dataNodeMemoryManager;\n\n  /**\n   * A linked list for tracking all allocated data pages so that we can free all of our memory.\n   */\n  private final LinkedList<MemoryBlock> dataPages = new LinkedList<MemoryBlock>();\n\n  /**\n   * The data page that will be used to store keys and values for new hashtable entries. When this\n   * page becomes full, a new page will be allocated and this pointer will change to point to that\n   * new page.\n   */\n  private MemoryBlock currentPage = null;\n\n  /**\n   * Offset into `currentPage` that points to the location where new data can be inserted into\n   * the page. This does not incorporate the page's base offset.\n   */\n  private long pageCursor = 0;\n\n  /**\n   * The maximum number of keys that BytesToBytesMap supports. The hash table has to be\n   * power-of-2-sized and its backing Java array can contain at most (1 &lt;&lt; 30) elements,\n   * since that's the largest power-of-2 that's less than Integer.MAX_VALUE. We need two long array\n   * entries per key, giving us a maximum capacity of (1 &lt;&lt; 29).\n   */\n  @VisibleForTesting\n public static final int MAX_CAPACITY = (1 << 29);\n\n  // This choice of page table size and page size means that we can address up to 500 gigabytes\n  // of memory.\n\n  /**\n   * A single array to store the key and value.\n   *\n   * Position {@code 2 * i} in the array is used to track a pointer to the key at index {@code i},\n   * while position {@code 2 * i + 1} in the array holds key's full 32-bit hashcode.\n   */\n  @Nullable\n  private LongArray longArray;\n  // TODO: we're wasting 32 bits of space here; we can probably store fewer bits of the hashcode\n  // and exploit word-alignment to use fewer bits to hold the address.  This might let us store\n  // only one long per map entry, increasing the chance that this array will fit in cache at the\n  // expense of maybe performing more lookups if we have hash collisions.  Say that we stored only\n  // 27 bits of the hashcode and 37 bits of the address.  37 bits is enough to address 1 terabyte\n  // of RAM given word-alignment.  If we use 13 bits of this for our page table, that gives us a\n  // maximum page size of 2^24 * 8 = ~134 megabytes per page. This change will require us to store\n  // full base addresses in the page table for off-heap mode so that we can reconstruct the full\n  // absolute memory addresses.\n\n  /**\n   * Whether or not the longArray can grow. We will not insert more elements if it's false.\n   */\n  private boolean canGrowArray = true;\n\n  private final double loadFactor;\n\n  /**\n   * The size of the data pages that hold key and value data. Map entries cannot span multiple\n   * pages, so this limits the maximum entry size.\n   */\n  private final long pageSizeBytes;\n\n  /**\n   * Number of keys defined in the map.\n   */\n  private int numKeys;\n\n  /**\n   * Number of values defined in the map. A key could have multiple values.\n   */\n  private int numValues;\n\n  /**\n   * The map will be expanded once the number of keys exceeds this threshold.\n   */\n  private int growthThreshold;\n\n  /**\n   * Mask for truncating hashcodes so that they do not exceed the long array's size.\n   * This is a strength reduction optimization; we're essentially performing a modulus operation,\n   * but doing so with a bitmask because this is a power-of-2-sized hash map.\n   */\n  private int mask;\n\n  /**\n   * Return value of {@link BytesToBytesMap#lookup(Object, long, int)}.\n   */\n  private final Location loc;\n\n  private final boolean enablePerfMetrics;\n\n  private long timeSpentResizingNs = 0;\n\n  private long numProbes = 0;\n\n  private long numKeyLookups = 0;\n\n  private long numHashCollisions = 0;\n\n  private long peakMemoryUsedBytes = 0L;\n\n  private final DataNodeDiskManager blockManager;\n  private final SerializerManager serializerManager;\n  private volatile MapIterator destructiveIterator = null;\n  private LinkedList<UnsafeSorterSpillWriter> spillWriters = new LinkedList<UnsafeSorterSpillWriter>();\n\n  public BytesToBytesMap(\n      DataNodeMemoryManager dataNodeMemoryManager,\n      DataNodeDiskManager blockManager,\n      SerializerManager serializerManager,\n      int initialCapacity,\n      double loadFactor,\n      long pageSizeBytes,\n      boolean enablePerfMetrics) {\n    super(dataNodeMemoryManager, pageSizeBytes);\n    this.dataNodeMemoryManager = dataNodeMemoryManager;\n    this.blockManager = blockManager;\n    this.serializerManager = serializerManager;\n    this.loadFactor = loadFactor;\n    this.loc = new Location();\n    this.pageSizeBytes = pageSizeBytes;\n    this.enablePerfMetrics = enablePerfMetrics;\n    if (initialCapacity <= 0) {\n      throw new IllegalArgumentException(\"Initial capacity must be greater than 0\");\n    }\n    if (initialCapacity > MAX_CAPACITY) {\n      throw new IllegalArgumentException(\n        \"Initial capacity \" + initialCapacity + \" exceeds maximum capacity of \" + MAX_CAPACITY);\n    }\n    if (pageSizeBytes > DataNodeMemoryManager.MAXIMUM_PAGE_SIZE_BYTES) {\n      throw new IllegalArgumentException(\"Page size \" + pageSizeBytes + \" cannot exceed \" +\n        DataNodeMemoryManager.MAXIMUM_PAGE_SIZE_BYTES);\n    }\n    allocate(initialCapacity);\n  }\n\n  public BytesToBytesMap(\n      DataNodeMemoryManager dataNodeMemoryManager,\n      int initialCapacity,\n      long pageSizeBytes) {\n    this(dataNodeMemoryManager, initialCapacity, pageSizeBytes, false);\n  }\n\n  public BytesToBytesMap(\n      DataNodeMemoryManager dataNodeMemoryManager,\n      int initialCapacity,\n      long pageSizeBytes,\n      boolean enablePerfMetrics) {\n    this(\n            dataNodeMemoryManager,\n     null,\n        null,\n      initialCapacity,\n      0.70,\n      pageSizeBytes,\n      enablePerfMetrics);\n  }\n\n  /**\n   * Returns the number of keys defined in the map.\n   */\n  public int numKeys() { return numKeys; }\n\n  /**\n   * Returns the number of values defined in the map. A key could have multiple values.\n   */\n  public int numValues() { return numValues; }\n\n  public final class MapIterator implements Iterator<Location> {\n\n    private int numRecords;\n    private final Location loc;\n\n    private MemoryBlock currentPage = null;\n    private int recordsInPage = 0;\n    private Object pageBaseObject;\n    private long offsetInPage;\n\n    // If this iterator destructive or not. When it is true, it frees each page as it moves onto\n    // next one.\n    private boolean destructive = false;\n    private UnsafeSorterSpillReader reader = null;\n\n    private MapIterator(int numRecords, Location loc, boolean destructive) {\n      this.numRecords = numRecords;\n      this.loc = loc;\n      this.destructive = destructive;\n      if (destructive) {\n        destructiveIterator = this;\n      }\n    }\n\n    private void advanceToNextPage() {\n      synchronized (this) {\n        int nextIdx = dataPages.indexOf(currentPage) + 1;\n        if (destructive && currentPage != null) {\n          dataPages.remove(currentPage);\n          freePage(currentPage);\n          nextIdx --;\n        }\n        if (dataPages.size() > nextIdx) {\n          currentPage = dataPages.get(nextIdx);\n          pageBaseObject = currentPage.getBaseObject();\n          offsetInPage = currentPage.getBaseOffset();\n          recordsInPage = Platform.getInt(pageBaseObject, offsetInPage);\n          offsetInPage += 4;\n        } else {\n          currentPage = null;\n          if (reader != null) {\n            // remove the spill file from disk\n            File file = spillWriters.removeFirst().getFile();\n            if (file != null && file.exists()) {\n              if (!file.delete()) {\n                logger.error(\"Was unable to delete spill file {}\", file.getAbsolutePath());\n              }\n            }\n          }\n          try {\n            Closeables.close(reader, /* swallowIOException = */ false);\n            if(spillWriters.size()>0) {\n              reader = spillWriters.getFirst().getReader(serializerManager);\n            }\n            recordsInPage = -1;\n\n          } catch (IOException e) {\n            // Scala iterator does not handle exception\n            Platform.throwException(e);\n          }\n        }\n      }\n    }\n\n    @Override\n    public boolean hasNext() {\n      if (numRecords == 0) {\n        if (reader != null) {\n          // remove the spill file from disk\n          File file = spillWriters.removeFirst().getFile();\n          if (file != null && file.exists()) {\n            if (!file.delete()) {\n              logger.error(\"Was unable to delete spill file {}\", file.getAbsolutePath());\n            }\n          }\n        }\n      }\n      return numRecords > 0;\n    }\n\n    @Override\n    public Location next() {\n      if (recordsInPage == 0) {\n        advanceToNextPage();\n      }\n      numRecords--;\n      if (currentPage != null) {\n        int totalLength = Platform.getInt(pageBaseObject, offsetInPage);\n        loc.with(currentPage, offsetInPage);\n        // [total size] [key size] [key] [value] [pointer to next]\n        offsetInPage += 4 + totalLength + 8;\n        recordsInPage --;\n        return loc;\n      } else {\n\n\n        assert(reader != null);\n//        if(reader == null)\n//          return null;\n\n        if (!reader.hasNext()) {\n          advanceToNextPage();\n        }\n        try {\n          reader.loadNext();\n        } catch (IOException e) {\n          try {\n            reader.close();\n          } catch(IOException e2) {\n            logger.error(\"Error while closing spill reader\", e2);\n          }\n          // Scala iterator does not handle exception\n          Platform.throwException(e);\n        }\n        loc.with(reader.getBaseObject(), reader.getBaseOffset(), reader.getRecordLength());\n        return loc;\n      }\n    }\n\n    public long spill(long numBytes) throws IOException {\n      synchronized (this) {\n        if (!destructive || dataPages.size() == 1) {\n          return 0L;\n        }\n\n\n\n        long released = 0L;\n        while (dataPages.size() > 0) {\n          MemoryBlock block = dataPages.getLast();\n          // The currentPage is used, cannot be released\n          if (block == currentPage) {\n            break;\n          }\n\n          Object base = block.getBaseObject();\n          long offset = block.getBaseOffset();\n          int numRecords = Platform.getInt(base, offset);\n          offset += 4;\n          final UnsafeSorterSpillWriter writer =\n            new UnsafeSorterSpillWriter(blockManager, 32 * 1024, numRecords);\n          while (numRecords > 0) {\n            int length = Platform.getInt(base, offset);\n            writer.write(base, offset + 4, length, 0);\n            offset += 4 + length + 8;\n            numRecords--;\n          }\n          writer.close();\n          spillWriters.add(writer);\n\n          dataPages.removeLast();\n          released += block.size();\n          freePage(block);\n\n          if (released >= numBytes) {\n            break;\n          }\n        }\n\n        return released;\n      }\n    }\n\n    @Override\n    public void remove() {\n      throw new UnsupportedOperationException();\n    }\n  }\n\n  /**\n   * Returns an iterator for iterating over the entries of this map.\n   *\n   * For efficiency, all calls to `next()` will return the same {@link Location} object.\n   *\n   * If any other lookups or operations are performed on this map while iterating over it, including\n   * `lookup()`, the behavior of the returned iterator is undefined.\n   */\n  public MapIterator iterator() {\n    return new MapIterator(numValues, loc, false);\n  }\n\n  /**\n   * Returns a destructive iterator for iterating over the entries of this map. It frees each page\n   * as it moves onto next one. Notice: it is illegal to call any method on the map after\n   * `destructiveIterator()` has been called.\n   *\n   * For efficiency, all calls to `next()` will return the same {@link Location} object.\n   *\n   * If any other lookups or operations are performed on this map while iterating over it, including\n   * `lookup()`, the behavior of the returned iterator is undefined.\n   */\n  public MapIterator destructiveIterator() {\n    return new MapIterator(numValues, loc, true);\n  }\n\n  /**\n   * Looks up a key, and return a {@link Location} handle that can be used to map existence\n   * and read/write values.\n   *\n   * This function always return the same {@link Location} instance to avoid object allocation.\n   */\n  public Location lookup(Object keyBase, long keyOffset, int keyLength) {\n    safeLookup(keyBase, keyOffset, keyLength, loc,\n      Murmur3_x86_32.hashUnsafeWords(keyBase, keyOffset, keyLength, 42));\n    return loc;\n  }\n\n  /**\n   * Looks up a key, and return a {@link Location} handle that can be used to map existence\n   * and read/write values.\n   *\n   * This function always return the same {@link Location} instance to avoid object allocation.\n   */\n  public Location lookup(Object keyBase, long keyOffset, int keyLength, int hash) {\n    safeLookup(keyBase, keyOffset, keyLength, loc, hash);\n    return loc;\n  }\n\n  /**\n   * Looks up a key, and saves the result in provided `loc`.\n   *\n   * This is a thread-safe version of `lookup`, could be used by multiple threads.\n   */\n  public void safeLookup(Object keyBase, long keyOffset, int keyLength, Location loc, int hash) {\n    assert(longArray != null);\n\n    if (enablePerfMetrics) {\n      numKeyLookups++;\n    }\n\n    int pos = hash & mask;\n    int step = 1;\n\n    while (true) {\n      if (enablePerfMetrics) {\n        numProbes++;\n      }\n      if (longArray.get(pos * 2) == 0) {\n        // This is a new key.\n        loc.with(pos, hash, false);\n\n        return;\n\n      } else {\n\n        long stored = longArray.get(pos * 2 + 1);\n\n        /**\n         * hash相等\n         */\n        if ((int) (stored) == hash) {\n          // Full hash code matches.Let's compare the keys for equality.\n          loc.with(pos,hash,true);\n          /**\n           * 比较key的值\n           */\n          if (loc.getKeyLength() == keyLength) {\n            final boolean areEqual = ByteArrayMethods.arrayEquals(\n              keyBase,\n              keyOffset,\n              loc.getKeyBase(),\n              loc.getKeyOffset(),\n              keyLength\n            );\n\n            if (areEqual) {\n\n              return;\n            } else {\n              if (enablePerfMetrics) {\n                numHashCollisions++;\n              }\n            }\n          }\n        }\n      }\n      pos = (pos + step) & mask;\n      step++;\n    }\n  }\n\n  /**\n   * Handle returned by {@link BytesToBytesMap#lookup(Object, long, int)} function.\n   */\n  public final class Location {\n    /** An index into the hash map's Long array */\n    private int pos;\n    /** True if this location points to a position where a key is defined, false otherwise */\n    private boolean isDefined;\n    /**\n     * The hashcode of the most recent key passed to\n     * {@link BytesToBytesMap#lookup(Object, long, int, int)}. Caching this hashcode here allows us\n     * to avoid re-hashing the key when storing a value for that key.\n     */\n    private int keyHashcode;\n    private Object baseObject;  // the base object for key and value\n    private long keyOffset;\n    private int keyLength;\n    private long valueOffset;\n    private int valueLength;\n\n    /**\n     * Memory page containing the record. Only set if created by {@link BytesToBytesMap#iterator()}.\n     */\n    @Nullable\n    private MemoryBlock memoryPage;\n\n    private void updateAddressesAndSizes(long fullKeyAddress) {\n      updateAddressesAndSizes(\n        dataNodeMemoryManager.getPage(fullKeyAddress),\n        dataNodeMemoryManager.getOffsetInPage(fullKeyAddress));\n    }\n\n    private void updateAddressesAndSizes(final Object base, long offset) {\n      baseObject = base;\n      final int totalLength = Platform.getInt(base, offset);\n      offset += 4;\n      keyLength = Platform.getInt(base, offset);\n      offset += 4;\n      keyOffset = offset;\n      valueOffset = offset + keyLength;\n      valueLength = totalLength - keyLength - 4;\n    }\n\n    private Location with(int pos, int keyHashcode, boolean isDefined) {\n      assert(longArray != null);\n      this.pos = pos;\n      this.isDefined = isDefined;\n      this.keyHashcode = keyHashcode;\n      if (isDefined) {\n        final long fullKeyAddress = longArray.get(pos * 2);\n        updateAddressesAndSizes(fullKeyAddress);\n      }\n      return this;\n    }\n\n    private Location with(MemoryBlock page, long offsetInPage) {\n      this.isDefined = true;\n      this.memoryPage = page;\n      updateAddressesAndSizes(page.getBaseObject(), offsetInPage);\n      return this;\n    }\n\n    /**\n     * This is only used for spilling\n     */\n    private Location with(Object base, long offset, int length) {\n      this.isDefined = true;\n      this.memoryPage = null;\n      baseObject = base;\n      keyOffset = offset + 4;\n      keyLength = Platform.getInt(base, offset);\n      valueOffset = offset + 4 + keyLength;\n      valueLength = length - 4 - keyLength;\n      return this;\n    }\n\n    /**\n     * Find the next pair that has the same key as current one.\n     */\n    public boolean nextValue() {\n      assert isDefined;\n      long nextAddr = Platform.getLong(baseObject, valueOffset + valueLength);\n      if (nextAddr == 0) {\n        return false;\n      } else {\n        updateAddressesAndSizes(nextAddr);\n        return true;\n      }\n    }\n\n    /**\n     * Returns the memory page that contains the current record.\n     * This is only valid if this is returned by {@link BytesToBytesMap#iterator()}.\n     */\n    public MemoryBlock getMemoryPage() {\n      return this.memoryPage;\n    }\n\n    /**\n     * Returns true if the key is defined at this position, and false otherwise.\n     */\n    public boolean isDefined() {\n      return isDefined;\n    }\n\n    /**\n     * Returns the base object for key.\n     */\n    public Object getKeyBase() {\n      assert (isDefined);\n      return baseObject;\n    }\n\n    /**\n     * Returns the offset for key.\n     */\n    public long getKeyOffset() {\n      assert (isDefined);\n      return keyOffset;\n    }\n\n    /**\n     * Returns the base object for value.\n     */\n    public Object getValueBase() {\n      assert (isDefined);\n      return baseObject;\n    }\n\n    /**\n     * Returns the offset for value.\n     */\n    public long getValueOffset() {\n      assert (isDefined);\n      return valueOffset;\n    }\n\n    /**\n     * Returns the length of the key defined at this position.\n     * Unspecified behavior if the key is not defined.\n     */\n    public int getKeyLength() {\n      assert (isDefined);\n      return keyLength;\n    }\n\n    /**\n     * Returns the length of the value defined at this position.\n     * Unspecified behavior if the key is not defined.\n     */\n    public int getValueLength() {\n      assert (isDefined);\n      return valueLength;\n    }\n\n    /**\n     * Append a new value for the key. This method could be called multiple times for a given key.\n     * The return value indicates whether the put succeeded or whether it failed because additional\n     * memory could not be acquired.\n     * <p>\n     * It is only valid to call this method immediately after calling `lookup()` using the same key.\n     * </p>\n     * <p>\n     * The key and value must be word-aligned (that is, their sizes must multiples of 8).\n     * </p>\n     * <p>\n     * After calling this method, calls to `get[Key|Value]Address()` and `get[Key|Value]Length`\n     * will return information on the data stored by this `append` call.\n     * </p>\n     * <p>\n     * As an example usage, here's the proper way to store a new key:\n     * </p>\n     * <pre>\n     *   Location loc = map.lookup(keyBase, keyOffset, keyLength);\n     *   if (!loc.isDefined()) {\n     *     if (!loc.append(keyBase, keyOffset, keyLength, ...)) {\n     *       // handle failure to grow map (by spilling, for example)\n     *     }\n     *   }\n     * </pre>\n     * <p>\n     * Unspecified behavior if the key is not defined.\n     * </p>\n     *\n     * @return true if the put() was successful and false if the put() failed because memory could\n     *         not be acquired.\n     */\n    public boolean append(Object kbase, long koff, int klen, Object vbase, long voff, int vlen) {\n      assert (klen % 8 == 0);\n      assert (vlen % 8 == 0);\n      assert (longArray != null);\n\n      if (numKeys == MAX_CAPACITY\n        // The map could be reused from last spill (because of no enough memory to grow),\n        // then we don't try to grow again if hit the `growthThreshold`.\n        || !canGrowArray && numKeys > growthThreshold) {\n        return false;\n      }\n\n      // Here, we'll copy the data into our data pages. Because we only store a relative offset from\n      // the key address instead of storing the absolute address of the value, the key and value\n      // must be stored in the same memory page.\n      // (8 byte key length) (key) (value) (8 byte pointer to next value)\n      final long recordLength = 8 + klen + vlen + 8;\n      if (currentPage == null || currentPage.size() - pageCursor < recordLength) {\n        if (!acquireNewPage(recordLength + 4L)) {\n          return false;\n        }\n      }\n\n      // --- Append the key and value data to the current data page --------------------------------\n      final Object base = currentPage.getBaseObject();\n      long offset = currentPage.getBaseOffset() + pageCursor;\n      final long recordOffset = offset;\n      Platform.putInt(base, offset, klen + vlen + 4);\n      Platform.putInt(base, offset + 4, klen);\n      offset += 8;\n      Platform.copyMemory(kbase, koff, base, offset, klen);\n      offset += klen;\n      Platform.copyMemory(vbase, voff, base, offset, vlen);\n      offset += vlen;\n      // put this value at the beginning of the list\n      Platform.putLong(base, offset, isDefined ? longArray.get(pos * 2) : 0);\n\n      // --- Update bookkeeping data structures ----------------------------------------------------\n      offset = currentPage.getBaseOffset();\n      Platform.putInt(base, offset, Platform.getInt(base, offset) + 1);\n      pageCursor += recordLength;\n      final long storedKeyAddress = dataNodeMemoryManager.encodePageNumberAndOffset(\n        currentPage, recordOffset);\n      longArray.set(pos * 2, storedKeyAddress);\n      updateAddressesAndSizes(storedKeyAddress);\n      numValues++;\n      if (!isDefined) {\n        numKeys++;\n        longArray.set(pos * 2 + 1, keyHashcode);\n        isDefined = true;\n\n        if (numKeys > growthThreshold && longArray.size() < MAX_CAPACITY) {\n          try {\n            growAndRehash();\n          } catch (OutOfMemoryError oom) {\n            canGrowArray = false;\n          }\n        }\n      }\n      return true;\n    }\n  }\n\n  /**\n   * Acquire a new page from the memory manager.\n   * @return whether there is enough space to allocate the new page.\n   */\n  private boolean acquireNewPage(long required) {\n    try {\n      currentPage = allocatePage(required);\n    } catch (OutOfMemoryError e) {\n      return false;\n    }\n    dataPages.add(currentPage);\n    Platform.putInt(currentPage.getBaseObject(), currentPage.getBaseOffset(), 0);\n    pageCursor = 4;\n    return true;\n  }\n\n  @Override\n  public long spill(long size, MemoryConsumer trigger) throws IOException {\n    if (trigger != this && destructiveIterator != null) {\n      return destructiveIterator.spill(size);\n    }\n    return 0L;\n  }\n\n  /**\n   * Allocate new data structures for this map. When calling this outside of the constructor,\n   * make sure to keep references to the old data structures so that you can free them.\n   *\n   * @param capacity the new map capacity\n   */\n  private void allocate(int capacity) {\n    assert (capacity >= 0);\n    capacity = Math.max((int) Math.min(MAX_CAPACITY, ByteArrayMethods.nextPowerOf2(capacity)), 64);\n    assert (capacity <= MAX_CAPACITY);\n    longArray = allocateLongArray(capacity * 2);\n    longArray.zeroOut();\n\n    this.growthThreshold = (int) (capacity * loadFactor);\n    this.mask = capacity - 1;\n  }\n\n  /**\n   * Free all allocated memory associated with this map, including the storage for keys and values\n   * as well as the hash map array itself.\n   *\n   * This method is idempotent and can be called multiple times.\n   */\n  public void free() {\n    updatePeakMemoryUsed();\n    if (longArray != null) {\n      freeLongArray(longArray);\n      longArray = null;\n    }\n    Iterator<MemoryBlock> dataPagesIterator = dataPages.iterator();\n    while (dataPagesIterator.hasNext()) {\n      MemoryBlock dataPage = dataPagesIterator.next();\n      dataPagesIterator.remove();\n      freePage(dataPage);\n    }\n    assert(dataPages.isEmpty());\n\n    while (!spillWriters.isEmpty()) {\n      File file = spillWriters.removeFirst().getFile();\n      if (file != null && file.exists()) {\n        if (!file.delete()) {\n          logger.error(\"Was unable to delete spill file {}\", file.getAbsolutePath());\n        }\n      }\n    }\n  }\n\n  public DataNodeMemoryManager getDataNodeMemoryManager() {\n    return dataNodeMemoryManager;\n  }\n\n  public long getPageSizeBytes() {\n    return pageSizeBytes;\n  }\n\n  /**\n   * Returns the total amount of memory, in bytes, consumed by this map's managed structures.\n   */\n  public long getTotalMemoryConsumption() {\n    long totalDataPagesSize = 0L;\n    for (MemoryBlock dataPage : dataPages) {\n      totalDataPagesSize += dataPage.size();\n    }\n    return totalDataPagesSize + ((longArray != null) ? longArray.memoryBlock().size() : 0L);\n  }\n\n  private void updatePeakMemoryUsed() {\n    long mem = getTotalMemoryConsumption();\n    if (mem > peakMemoryUsedBytes) {\n      peakMemoryUsedBytes = mem;\n    }\n  }\n\n  /**\n   * Return the peak memory used so far, in bytes.\n   */\n  public long getPeakMemoryUsedBytes() {\n    updatePeakMemoryUsed();\n    return peakMemoryUsedBytes;\n  }\n\n  /**\n   * Returns the total amount of time spent resizing this map (in nanoseconds).\n   */\n  public long getTimeSpentResizingNs() {\n    if (!enablePerfMetrics) {\n      throw new IllegalStateException();\n    }\n    return timeSpentResizingNs;\n  }\n\n  /**\n   * Returns the average number of probes per key lookup.\n   */\n  public double getAverageProbesPerLookup() {\n    if (!enablePerfMetrics) {\n      throw new IllegalStateException();\n    }\n    return (1.0 * numProbes) / numKeyLookups;\n  }\n\n  public long getNumHashCollisions() {\n    if (!enablePerfMetrics) {\n      throw new IllegalStateException();\n    }\n    return numHashCollisions;\n  }\n\n  @VisibleForTesting\n  public int getNumDataPages() {\n    return dataPages.size();\n  }\n\n  /**\n   * Returns the underline long[] of longArray.\n   */\n  public LongArray getArray() {\n    assert(longArray != null);\n    return longArray;\n  }\n\n  /**\n   * Reset this map to initialized state.\n   */\n  public void reset() {\n    numKeys = 0;\n    numValues = 0;\n    longArray.zeroOut();\n\n    while (dataPages.size() > 0) {\n      MemoryBlock dataPage = dataPages.removeLast();\n      freePage(dataPage);\n    }\n    currentPage = null;\n    pageCursor = 0;\n  }\n\n  /**\n   * Grows the size of the hash table and re-hash everything.\n   */\n  @VisibleForTesting\n  void growAndRehash() {\n    assert(longArray != null);\n\n    long resizeStartTime = -1;\n    if (enablePerfMetrics) {\n      resizeStartTime = System.nanoTime();\n    }\n    // Store references to the old data structures to be used when we re-hash\n    final LongArray oldLongArray = longArray;\n    final int oldCapacity = (int) oldLongArray.size() / 2;\n\n    // Allocate the new data structures\n    allocate(Math.min(growthStrategy.nextCapacity(oldCapacity), MAX_CAPACITY));\n\n    // Re-mask (we don't recompute the hashcode because we stored all 32 bits of it)\n    for (int i = 0; i < oldLongArray.size(); i += 2) {\n      final long keyPointer = oldLongArray.get(i);\n      if (keyPointer == 0) {\n        continue;\n      }\n      final int hashcode = (int) oldLongArray.get(i + 1);\n      int newPos = hashcode & mask;\n      int step = 1;\n      while (longArray.get(newPos * 2) != 0) {\n        newPos = (newPos + step) & mask;\n        step++;\n      }\n      longArray.set(newPos * 2, keyPointer);\n      longArray.set(newPos * 2 + 1, hashcode);\n    }\n\n    freeLongArray(oldLongArray);\n\n    if (enablePerfMetrics) {\n      timeSpentResizingNs += System.nanoTime() - resizeStartTime;\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/map/HashMapGrowthStrategy.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.map;\n\n/**\n * Interface that defines how we can grow the size of a hash map when it is over a threshold.\n */\npublic interface HashMapGrowthStrategy {\n\n  int nextCapacity(int currentCapacity);\n\n  /**\n   * Double the size of the hash map every time.\n   */\n  HashMapGrowthStrategy DOUBLING = new Doubling();\n\n  class Doubling implements HashMapGrowthStrategy {\n    @Override\n    public int nextCapacity(int currentCapacity) {\n      assert (currentCapacity > 0);\n      // Guard against overflow\n      return (currentCapacity * 2 > 0) ? (currentCapacity * 2) : Integer.MAX_VALUE;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/map/UnsafeFixedWidthAggregationMap.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.map;\n\nimport io.mycat.MycatServer;\nimport io.mycat.memory.unsafe.KVIterator;\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.hash.Murmur3_x86_32;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.row.StructType;\nimport io.mycat.memory.unsafe.row.UnsafeRow;\nimport io.mycat.memory.unsafe.utils.sort.UnsafeKVExternalSorter;\nimport org.apache.log4j.Logger;\n\n\nimport java.io.IOException;\n\n/**\n * Modify by zagnix ,add put find func\n * Unsafe-based HashMap for performing aggregations where the aggregated values are fixed-width.\n * This map supports a maximum of 2 billion keys.\n */\npublic final class UnsafeFixedWidthAggregationMap {\n    private static Logger LOGGER = Logger.getLogger(UnsafeFixedWidthAggregationMap.class);\n\n  /**\n   * An empty aggregation buffer, encoded in UnsafeRow format. When inserting a new key into the\n   * map, we copy this buffer and use it as the value.\n   */\n  private final byte[] emptyAggregationBuffer;\n  private final StructType aggregationBufferSchema;\n  private final StructType groupingKeySchema;\n\n  /**\n   * A hashmap which maps from opaque bytearray keys to bytearray values.\n   */\n  private final BytesToBytesMap map;\n\n  /**\n   * Re-used pointer to the current aggregation buffer\n   */\n  private final UnsafeRow currentAggregationBuffer;\n\n  private final boolean enablePerfMetrics;\n\n  private final static int SEED = 42;\n\n  /**\n   * @return true if UnsafeFixedWidthAggregationMap supports aggregation buffers with the given\n   *         schema, false otherwise.\n   */\n  public static boolean supportsAggregationBufferSchema(StructType schema) {\n    return true;\n  }\n\n  /**\n   * Create a new UnsafeFixedWidthAggregationMap.\n   *\n   * @param emptyAggregationBuffer the default value for new keys (a \"zero\" of the agg. function)\n   * @param aggregationBufferSchema the schema of the aggregation buffer, used for row conversion.\n   * @param groupingKeySchema the schema of the grouping key, used for row conversion.\n   * @param dataNodeMemoryManager the memory manager used to allocate our Unsafe memory structures.\n   * @param initialCapacity the initial capacity of the map (a sizing hint to avoid re-hashing).\n   * @param pageSizeBytes the data page size, in bytes; limits the maximum record size.\n   * @param enablePerfMetrics if true, performance metrics will be recorded (has minor perf impact)\n   */\n  public UnsafeFixedWidthAggregationMap(\n      UnsafeRow emptyAggregationBuffer,\n      StructType aggregationBufferSchema,\n      StructType groupingKeySchema,\n      DataNodeMemoryManager dataNodeMemoryManager,\n      int initialCapacity,\n      long pageSizeBytes,\n      boolean enablePerfMetrics) {\n    this.aggregationBufferSchema = aggregationBufferSchema;\n\n    this.currentAggregationBuffer = new UnsafeRow(aggregationBufferSchema.length());\n    this.groupingKeySchema = groupingKeySchema;\n    this.map = new BytesToBytesMap(dataNodeMemoryManager,initialCapacity, pageSizeBytes, enablePerfMetrics);\n    this.enablePerfMetrics = enablePerfMetrics;\n    this.emptyAggregationBuffer = emptyAggregationBuffer.getBytes() ;\n  }\n\n  /**\n   * Return the aggregation buffer for the current group. For efficiency, all calls to this method\n   * return the same object. If additional memory could not be allocated, then this method will\n   * signal an error by returning null.\n   */\n  public UnsafeRow getAggregationBuffer(UnsafeRow groupingKey) {\n    return getAggregationBufferFromUnsafeRow(groupingKey);\n  }\n\n  public UnsafeRow getAggregationBufferFromUnsafeRow(UnsafeRow key) {\n\n    return getAggregationBufferFromUnsafeRow(key,\n            Murmur3_x86_32.hashUnsafeWords(key.getBaseObject(),key.getBaseOffset(),\n            key.getSizeInBytes(),SEED));\n  }\n\n  public boolean put(UnsafeRow key, UnsafeRow value){\n\n    int hash =  Murmur3_x86_32.hashUnsafeWords(key.getBaseObject(),\n            key.getBaseOffset(), key.getSizeInBytes(),SEED);\n\n    // Probe our map using the serialized key\n    final BytesToBytesMap.Location loc = map.lookup(\n            key.getBaseObject(),\n            key.getBaseOffset(),\n            key.getSizeInBytes(),\n            hash);\n\n    if (!loc.isDefined()) {\n      // This is the first time that we've seen this grouping key, so we'll insert a copy of the\n      // empty aggregation buffer into the map:\n      boolean putSucceeded = loc.append(\n              key.getBaseObject(),\n              key.getBaseOffset(),\n              key.getSizeInBytes(),\n              value.getBaseObject(),\n              value.getBaseOffset(),\n              value.getSizeInBytes());\n\n      if (!putSucceeded) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n\n  public boolean find(UnsafeRow key){\n\n    int hash =  Murmur3_x86_32.hashUnsafeWords(key.getBaseObject(),key.getBaseOffset(), key.getSizeInBytes(),42);\n    // Probe our map using the serialized key\n    final BytesToBytesMap.Location loc = map.lookup(key.getBaseObject(),\n            key.getBaseOffset(), key.getSizeInBytes(), hash);\n\n    if (!loc.isDefined()) {\n     return false;\n    }\n    return true;\n  }\n\n\n  public UnsafeRow getAggregationBufferFromUnsafeRow(UnsafeRow key, int hash) {\n    // Probe our map using the serialized key\n    final BytesToBytesMap.Location loc = map.lookup(\n      key.getBaseObject(),\n      key.getBaseOffset(),\n      key.getSizeInBytes(),\n      hash);\n\n    if (!loc.isDefined()) {\n      // This is the first time that we've seen this grouping key, so we'll insert a copy of the\n      // empty aggregation buffer into the map:\n      boolean putSucceeded = loc.append(\n        key.getBaseObject(),\n        key.getBaseOffset(),\n        key.getSizeInBytes(),\n        emptyAggregationBuffer,\n        Platform.BYTE_ARRAY_OFFSET,\n        emptyAggregationBuffer.length\n      );\n\n      if (!putSucceeded) {\n        return null;\n      }\n    }\n\n    // Reset the pointer to point to the value that we just stored or looked up:\n    currentAggregationBuffer.pointTo(\n      loc.getValueBase(),\n      loc.getValueOffset(),\n      loc.getValueLength()\n    );\n    return currentAggregationBuffer;\n  }\n\n  /**\n   * Returns an iterator over the keys and values in this map. This uses destructive iterator of\n   * BytesToBytesMap. So it is illegal to call any other method on this map after `iterator()` has\n   * been called.\n   *\n   * For efficiency, each call returns the same object.\n   */\n  public KVIterator<UnsafeRow,UnsafeRow> iterator() {\n    return new KVIterator<UnsafeRow, UnsafeRow>() {\n\n      private final BytesToBytesMap.MapIterator mapLocationIterator = map.iterator();\n\n      private final UnsafeRow key = new UnsafeRow(groupingKeySchema.length());\n      private final UnsafeRow value = new UnsafeRow(aggregationBufferSchema.length());\n\n      @Override\n      public boolean next() {\n        if (mapLocationIterator.hasNext()) {\n          final BytesToBytesMap.Location loc = mapLocationIterator.next();\n            if (loc == null)\n                return false;\n          key.pointTo(\n            loc.getKeyBase(),\n            loc.getKeyOffset(),\n            loc.getKeyLength()\n          );\n          value.pointTo(\n            loc.getValueBase(),\n            loc.getValueOffset(),\n            loc.getValueLength()\n          );\n          return true;\n        } else {\n          return false;\n        }\n      }\n\n      @Override\n      public UnsafeRow getKey() {\n        return key;\n      }\n\n      @Override\n      public UnsafeRow getValue() {\n        return value;\n      }\n\n      @Override\n      public void close() {\n      }\n    };\n  }\n\n  /**\n   * Return the peak memory used so far, in bytes.\n   */\n  public long getPeakMemoryUsedBytes() {\n    return map.getPeakMemoryUsedBytes();\n  }\n\n  /**\n   * Free the memory associated with this map. This is idempotent and can be called multiple times.\n   */\n  public void free() {\n    map.free();\n  }\n\n  @SuppressWarnings(\"UseOfSystemOutOrSystemErr\")\n  public void printPerfMetrics() {\n    if (!enablePerfMetrics) {\n      throw new IllegalStateException(\"Perf metrics not enabled\");\n    }\n    System.out.println(\"Average probes per lookup: \" + map.getAverageProbesPerLookup());\n    System.out.println(\"Number of hash collisions: \" + map.getNumHashCollisions());\n    System.out.println(\"Time spent resizing (ns): \" + map.getTimeSpentResizingNs());\n    System.out.println(\"Total memory consumption (bytes): \" + map.getTotalMemoryConsumption());\n  }\n\n  /**\n   * Sorts the map's records in place, spill them to disk, and returns an [[UnsafeKVExternalSorter]]\n   *\n   * Note that the map will be reset for inserting new records, and the returned sorter can NOT be\n   * used to insert records.\n   */\n  public UnsafeKVExternalSorter destructAndCreateExternalSorter() throws IOException {\n    return new UnsafeKVExternalSorter(\n      groupingKeySchema,\n      aggregationBufferSchema,\n      MycatServer.getInstance().getMyCatMemory().getBlockManager(),\n      MycatServer.getInstance().getMyCatMemory().getSerializerManager(),\n      map.getPageSizeBytes(),\n      map);\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/memory/HeapMemoryAllocator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.memory;\n\n\nimport io.mycat.memory.unsafe.Platform;\n\nimport javax.annotation.concurrent.GuardedBy;\nimport java.lang.ref.WeakReference;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.Map;\n\n\n/**\n * A simple {@link MemoryAllocator} that can allocate up to 16GB using a JVM long primitive array.\n */\npublic class HeapMemoryAllocator implements MemoryAllocator {\n\n  @GuardedBy(\"this\")\n  private final Map<Long, LinkedList<WeakReference<MemoryBlock>>> bufferPoolsBySize =\n    new HashMap<Long, LinkedList<WeakReference<MemoryBlock>>>();\n\n  private static final int POOLING_THRESHOLD_BYTES = 1024 * 1024;\n\n  /**\n   * Returns true if allocations of the given size should go through the pooling mechanism and\n   * false otherwise.\n   */\n  private boolean shouldPool(long size) {\n    // Very small allocations are less likely to benefit from pooling.\n    return size >= POOLING_THRESHOLD_BYTES;\n  }\n\n  @Override\n  public MemoryBlock allocate(long size) throws OutOfMemoryError {\n    if (shouldPool(size)) {\n      synchronized (this) {\n        final LinkedList<WeakReference<MemoryBlock>> pool = bufferPoolsBySize.get(size);\n        if (pool != null) {\n          while (!pool.isEmpty()) {\n            final WeakReference<MemoryBlock> blockReference = pool.pop();\n            final MemoryBlock memory = blockReference.get();\n            if (memory != null) {\n              assert (memory.size() == size);\n              return memory;\n            }\n          }\n          bufferPoolsBySize.remove(size);\n        }\n      }\n    }\n    long[] array = new long[(int) ((size + 7) / 8)];\n    return new MemoryBlock(array, Platform.LONG_ARRAY_OFFSET, size);\n  }\n\n  @Override\n  public void free(MemoryBlock memory) {\n    final long size = memory.size();\n    if (shouldPool(size)) {\n      synchronized (this) {\n        LinkedList<WeakReference<MemoryBlock>> pool = bufferPoolsBySize.get(size);\n        if (pool == null) {\n          pool = new LinkedList<WeakReference<MemoryBlock>>();\n          bufferPoolsBySize.put(size, pool);\n        }\n        pool.add(new WeakReference<MemoryBlock>(memory));\n      }\n    } else {\n      // Do nothing\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/memory/MemoryAllocator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.memory;\n\npublic interface MemoryAllocator {\n\n  /**\n   * Allocates a contiguous block of memory. Note that the allocated memory is not guaranteed\n   * to be zeroed out (call `zero()` on the result if this is necessary).\n   */\n  MemoryBlock allocate(long size) throws OutOfMemoryError;\n\n  void free(MemoryBlock memory);\n\n  MemoryAllocator UNSAFE = new UnsafeMemoryAllocator();\n\n  MemoryAllocator HEAP = new HeapMemoryAllocator();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/memory/MemoryBlock.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.memory;\n\n\n\nimport io.mycat.memory.unsafe.Platform;\n\nimport javax.annotation.Nullable;\n\n/**\n * A consecutive block of memory, starting at a {@link MemoryLocation} with a fixed size.\n */\npublic class MemoryBlock extends MemoryLocation {\n\n  private final long length;\n\n  /**\n   * Optional page number; used when this MemoryBlock represents a page allocated by a\n   * DataNodeMemoryManager. This field is public so that it can be modified by the DataNodeMemoryManager,\n   * which lives in a different package.\n   */\n  public int pageNumber = -1;\n\n  public MemoryBlock(@Nullable Object obj, long offset, long length) {\n    super(obj, offset);\n    this.length = length;\n  }\n\n  /**\n   * Returns the size of the memory block.\n   */\n  public long size() {\n    return length;\n  }\n\n  /**\n   * Creates a memory block pointing to the memory used by the long array.\n   */\n  public static MemoryBlock fromLongArray(final long[] array) {\n    return new MemoryBlock(array, Platform.LONG_ARRAY_OFFSET, array.length * 8);\n  }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/memory/MemoryLocation.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.memory;\n\nimport javax.annotation.Nullable;\n\n/**\n * A memory location. Tracked either by a memory address (with off-heap allocation),\n * or by an offset from a JVM object (in-heap allocation).\n */\npublic class MemoryLocation {\n\n  @Nullable\n  Object obj;\n\n  long offset;\n\n  public MemoryLocation(@Nullable Object obj, long offset) {\n    this.obj = obj;\n    this.offset = offset;\n  }\n\n  public MemoryLocation() {\n    this(null, 0);\n  }\n\n  public void setObjAndOffset(Object newObj, long newOffset) {\n    this.obj = newObj;\n    this.offset = newOffset;\n  }\n\n  public final Object getBaseObject() {\n    return obj;\n  }\n\n  public final long getBaseOffset() {\n    return offset;\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/memory/UnsafeMemoryAllocator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.memory;\n\n\nimport io.mycat.memory.unsafe.Platform;\n\n/**\n * A simple {@link MemoryAllocator} that uses {@code Unsafe} to allocate off-heap memory.\n */\npublic class UnsafeMemoryAllocator implements MemoryAllocator {\n\n  @Override\n  public MemoryBlock allocate(long size) throws OutOfMemoryError {\n    long address = Platform.allocateMemory(size);\n    return new MemoryBlock(null, address, size);\n  }\n\n  @Override\n  public void free(MemoryBlock memory) {\n    assert (memory.obj == null) :\n      \"baseObject not null; are you trying to use the off-heap allocator to free on-heap memory?\";\n    Platform.freeMemory(memory.offset);\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/memory/mm/DataNodeMemoryManager.java",
    "content": "package io.mycat.memory.unsafe.memory.mm;\n\nimport com.google.common.annotations.VisibleForTesting;\n\nimport io.mycat.memory.unsafe.memory.MemoryBlock;\nimport io.mycat.memory.unsafe.utils.JavaUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.annotation.concurrent.GuardedBy;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.BitSet;\nimport java.util.HashSet;\n\n/** \n * Modify by zagnix \n * Manages the memory allocated by an individual thread.\n * <p>\n * Most of the complexity in this class deals with encoding of off-heap addresses into 64-bit longs.\n * In off-heap mode, memory can be directly addressed with 64-bit longs. In on-heap mode, memory is\n * addressed by the combination of a base Object reference and a 64-bit offset within that object.\n * This is a problem when we want to store pointers to data structures inside of other structures,\n * such as record pointers inside hashmaps or sorting buffers. Even if we decided to use 128 bits\n * to address memory, we can't just store the address of the base object since it's not guaranteed\n * to remain stable as the heap gets reorganized due to GC.\n * <p>\n * Instead, we use the following approach to encode record pointers in 64-bit longs: for off-heap\n * mode, just store the raw address, and for on-heap mode use the upper 13 bits of the address to\n * store a \"page number\" and the lower 51 bits to store an offset within this page. These page\n * numbers are used to index into a \"page table\" array inside of the MemoryManager in order to\n * retrieve the base object.\n * <p>\n * This allows us to address 8192 pages. In on-heap mode, the maximum page size is limited by the\n * maximum size of a long[] array, allowing us to address 8192 * 2^32 * 8 bytes, which is\n * approximately 35 terabytes of memory.\n */\npublic class DataNodeMemoryManager {\n\n  private final Logger logger = LoggerFactory.getLogger(DataNodeMemoryManager.class);\n\n  /** The number of bits used to address the page table. */\n  private static final int PAGE_NUMBER_BITS = 13;\n\n  /** The number of bits used to encode offsets in data pages. */\n  public static final int OFFSET_BITS = 64 - PAGE_NUMBER_BITS;  // 51\n\n  /** The number of entries in the page table. */\n  private static final int PAGE_TABLE_SIZE = 1 << PAGE_NUMBER_BITS;\n\n  /**\n   * Maximum supported data page size (in bytes). In principle, the maximum addressable page size is\n   * (1L &lt;&lt; OFFSET_BITS) bytes, which is 2+ petabytes. However, the on-heap allocator's\n   * maximum page size is limited by the maximum amount of data that can be stored in a long[]\n   * array, which is (2^32 - 1) * 8 bytes (or 16 gigabytes). Therefore, we cap this at 16 gigabytes.\n   */\n  public static final long MAXIMUM_PAGE_SIZE_BYTES = ((1L << 31) - 1) * 8L;\n\n  /** Bit mask for the lower 51 bits of a long. */\n  private static final long MASK_LONG_LOWER_51_BITS = 0x7FFFFFFFFFFFFL;\n\n  /** Bit mask for the upper 13 bits of a long */\n  private static final long MASK_LONG_UPPER_13_BITS = ~MASK_LONG_LOWER_51_BITS;\n\n  /**\n   * Similar to an operating system's page table, this array maps page numbers into base object\n   * pointers, allowing us to translate between the hashtable's internal 64-bit address\n   * representation and the baseObject+offset representation which we use to support both in- and\n   * off-heap addresses. When using an off-heap allocator, every entry in this map will be `null`.\n   * When using an in-heap allocator, the entries in this map will point to pages' base objects.\n   * Entries are added to this map as new data pages are allocated.\n   */\n  private final MemoryBlock[] pageTable = new MemoryBlock[PAGE_TABLE_SIZE];\n\n  /**\n   * Bitmap for tracking free pages.\n   */\n  private final BitSet allocatedPages = new BitSet(PAGE_TABLE_SIZE);\n\n  private final MemoryManager memoryManager;\n\n  private final long connectionAttemptId;\n\n  /**\n   * Tracks whether we're in-heap or off-heap. For off-heap, we short-circuit most of these methods\n   * without doing any masking or lookups. Since this branching should be well-predicted by the JIT,\n   * this extra layer of indirection / abstraction hopefully shouldn't be too expensive.\n   */\n  public final MemoryMode tungstenMemoryMode;\n\n  /**\n   * Tracks spillable memory consumers.\n   */\n  @GuardedBy(\"this\")\n  private final HashSet<MemoryConsumer> consumers;\n\n  /**\n   * The amount of memory that is acquired but not used.\n   */\n  private volatile long acquiredButNotUsed = 0L;\n\n  /**\n   * Construct a new DataNodeMemoryManager.\n   */\n  public DataNodeMemoryManager(MemoryManager memoryManager, long connectionAttemptId) {\n    this.tungstenMemoryMode = memoryManager.tungstenMemoryMode();\n    this.memoryManager = memoryManager;\n    this.connectionAttemptId = connectionAttemptId;\n    this.consumers = new HashSet<MemoryConsumer>();\n  }\n\n  /**\n   * Acquire N bytes of memory for a consumer. If there is no enough memory, it will call\n   * spill() of consumers to release more memory.\n   *\n   * @return number of bytes successfully granted (<= N).\n   */\n  public long acquireExecutionMemory(long required,MemoryMode mode,MemoryConsumer consumer) throws InterruptedException {\n\n    assert(required >= 0);\n    // If we are allocating Tungsten pages off-heap and receive a request to allocate on-heap\n    // memory here, then it may not make sense to spill since that would only end up freeing\n    // off-heap memory. This is subject to change, though, so it may be risky to make this\n    // optimization now in case we forget to undo it late when making changes.\n    synchronized (this) {\n      long got = memoryManager.acquireExecutionMemory(required,connectionAttemptId, mode);\n      // Try to release memory from other consumers first, then we can reduce the frequency of\n      // spilling, avoid to have too many spilled files.\n      if (got < required) {\n        // Call spill() on other consumers to release memory\n        for (MemoryConsumer c: consumers) {\n          if (c != consumer && c.getUsed() > 0) {\n            try {\n               /**\n               * 调用spill函数，写数据到磁盘中\n               */\n              long released = c.spill(required - got, consumer);\n                if (released > 0 && mode == tungstenMemoryMode) {\n                logger.info(\"Thread \"+connectionAttemptId+\" released \"+ JavaUtils.bytesToString(released) +\n                        \" from \"+ c +\" for\" + consumer);\n                got += memoryManager.acquireExecutionMemory(required - got, connectionAttemptId, mode);\n                if (got >= required) {\n                  break;\n                }\n              }\n            } catch (IOException e) {\n              logger.error(\"error while calling spill() on \" + c, e);\n              throw new OutOfMemoryError(\"error while calling spill() on \" + c + \" : \"\n                + e.getMessage());\n            }\n          }\n        }\n      }\n\n      // call spill() on itself\n      if (got < required && consumer != null) {\n        try {\n          long released = consumer.spill(required - got, consumer);\n          if (released > 0 && mode == tungstenMemoryMode) {\n            logger.info(\"Thread \" + connectionAttemptId +\n                    \" released \"+   JavaUtils.bytesToString(released) +\"from itself (\"+consumer+ \")\");\n            got += memoryManager.acquireExecutionMemory(required - got, connectionAttemptId, mode);\n          }\n        } catch (IOException e) {\n          logger.error(\"error while calling spill() on \" + consumer, e);\n          throw new OutOfMemoryError(\"error while calling spill() on \" + consumer + \" : \"\n            + e.getMessage());\n\n        }\n      }\n\n      if (consumer != null) {\n        consumers.add(consumer);\n      }\n     // logger.info(\"Thread\" + connectionAttemptId + \" acquire \"+  JavaUtils.bytesToString(got) +\" for \"+ consumer+\"\");\n      return got;\n    }\n  }\n\n  /**\n   * Release N bytes of execution memory for a MemoryConsumer.\n   */\n  public void releaseExecutionMemory(long size, MemoryMode mode, MemoryConsumer consumer) {\n    logger.debug   (\"Thread\" + connectionAttemptId + \" release \"+  JavaUtils.bytesToString(size) +\" from \"+ consumer+\"\");\n\n    memoryManager.releaseExecutionMemory(size, connectionAttemptId, mode);\n  }\n\n  /**\n   * Dump the memory usage of all consumers.\n   */\n  public void showMemoryUsage() {\n    logger.info(\"Memory used in Thread \" + connectionAttemptId);\n    synchronized (this) {\n      long memoryAccountedForByConsumers = 0;\n      for (MemoryConsumer c: consumers) {\n        long totalMemUsage = c.getUsed();\n        memoryAccountedForByConsumers += totalMemUsage;\n        if (totalMemUsage > 0) {\n          logger.info(\"Acquired by \" + c + \": \" + JavaUtils.bytesToString(totalMemUsage));\n        }\n      }\n      long memoryNotAccountedFor =\n        memoryManager.getExecutionMemoryUsageForConnection(connectionAttemptId) - memoryAccountedForByConsumers;\n      logger.info(\n        \"{} bytes of memory were used by task {} but are not associated with specific consumers\",\n        memoryNotAccountedFor, connectionAttemptId);\n      logger.info(\n        \"{} bytes of memory are used for execution and {} bytes of memory are used for storage\",\n        memoryManager.executionMemoryUsed());\n    }\n  }\n\n  /**\n   * Return the page size in bytes.\n   */\n  public long pageSizeBytes() {\n    return memoryManager.pageSizeBytes();\n  }\n\n  /**\n   * Allocate a block of memory that will be tracked in the MemoryManager's page table; this is\n   * intended for allocating large blocks of Tungsten memory that will be shared between operators.\n   *\n   * Returns `null` if there was not enough memory to allocate the page. May return a page that\n   * contains fewer bytes than requested, so callers should verify the size of returned pages.\n   */\n  public MemoryBlock allocatePage(long size, MemoryConsumer consumer) {\n    if (size > MAXIMUM_PAGE_SIZE_BYTES) {\n      throw new IllegalArgumentException(\n        \"Cannot allocate a page with more than \" + MAXIMUM_PAGE_SIZE_BYTES + \" bytes\");\n    }\n\n    /**\n     * 这里spill到磁盘中，释放内存空间\n     */\n    long acquired = 0;\n    try {\n      acquired = acquireExecutionMemory(size,tungstenMemoryMode, consumer);\n    } catch (InterruptedException e) {\n      logger.error(e.getMessage());\n    }\n\n    if (acquired <= 0) {\n      return null;\n    }\n\n    final int pageNumber;\n\n    synchronized (this) {\n      pageNumber = allocatedPages.nextClearBit(0);\n      if (pageNumber >= PAGE_TABLE_SIZE) {\n        releaseExecutionMemory(acquired, tungstenMemoryMode, consumer);\n        throw new IllegalStateException(\n          \"Have already allocated a maximum of \" + PAGE_TABLE_SIZE + \" pages\");\n      }\n      allocatedPages.set(pageNumber);\n    }\n\n\n\n    MemoryBlock page = null;\n\n    try {\n      page = memoryManager.tungstenMemoryAllocator().allocate(acquired);\n    } catch (OutOfMemoryError e) {\n      logger.warn(\"Failed to allocate a page ({} bytes), try again.\", acquired);\n      // there is no enough memory actually, it means the actual free memory is smaller than\n      // MemoryManager thought, we should keep the acquired memory.\n      synchronized (this) {\n        acquiredButNotUsed += acquired;\n        allocatedPages.clear(pageNumber);\n      }\n      // this could trigger spilling to free some pages.\n      return allocatePage(size, consumer);\n    }\n\n    page.pageNumber = pageNumber;\n    pageTable[pageNumber] = page;\n\n//    logger.info(\"Allocate page number \" + pageNumber + \" (\"+ acquired +\" bytes)\");\n\n    return page;\n  }\n\n  /**\n   * Free a block of memory allocated via {@link DataNodeMemoryManager#allocatePage}.\n   */\n  public void freePage(MemoryBlock page, MemoryConsumer consumer) {\n\n    assert (page.pageNumber != -1) :\n      \"Called freePage() on memory that wasn't allocated with allocatePage()\";\n    assert(allocatedPages.get(page.pageNumber));\n    pageTable[page.pageNumber] = null;\n\n    synchronized (this) {\n      allocatedPages.clear(page.pageNumber);\n    }\n\n    logger.trace(\"Freed page number \"+ page.pageNumber +\" (\"+page.size()  +\" bytes)\");\n\n    long pageSize = page.size();\n    memoryManager.tungstenMemoryAllocator().free(page);\n    releaseExecutionMemory(pageSize,tungstenMemoryMode,consumer);\n  }\n\n  /**\n   * Given a memory page and offset within that page, encode this address into a 64-bit long.\n   * This address will remain valid as long as the corresponding page has not been freed.\n   *\n   * @param page a data page allocated by {@link DataNodeMemoryManager#allocatePage}/\n   * @param offsetInPage an offset in this page which incorporates the base offset. In other words,\n   *                     this should be the value that you would pass as the base offset into an\n   *                     UNSAFE call (e.g. page.baseOffset() + something).\n   * @return an encoded page address.\n   */\n  public long encodePageNumberAndOffset(MemoryBlock page, long offsetInPage) {\n\n    if (tungstenMemoryMode == MemoryMode.OFF_HEAP) {\n      // In off-heap mode, an offset is an absolute address that may require a full 64 bits to\n      // encode. Due to our page size limitation, though, we can convert this into an offset that's\n      // relative to the page's base offset; this relative offset will fit in 51 bits.\n      offsetInPage -= page.getBaseOffset();\n    }\n\n    return encodePageNumberAndOffset(page.pageNumber, offsetInPage);\n  }\n\n  @VisibleForTesting\n  public static long encodePageNumberAndOffset(int pageNumber, long offsetInPage) {\n    assert (pageNumber != -1) : \"encodePageNumberAndOffset called with invalid page\";\n    return (((long) pageNumber) << OFFSET_BITS) | (offsetInPage & MASK_LONG_LOWER_51_BITS);\n  }\n\n  @VisibleForTesting\n  public static int decodePageNumber(long pagePlusOffsetAddress) {\n    return (int) (pagePlusOffsetAddress >>> OFFSET_BITS);\n  }\n\n  private static long decodeOffset(long pagePlusOffsetAddress) {\n    return (pagePlusOffsetAddress & MASK_LONG_LOWER_51_BITS);\n  }\n\n  /**\n   * Get the page associated with an address encoded by\n   * {@link DataNodeMemoryManager#encodePageNumberAndOffset(MemoryBlock, long)}\n   */\n  public Object getPage(long pagePlusOffsetAddress) {\n    if (tungstenMemoryMode == MemoryMode.ON_HEAP) {\n      final int pageNumber = decodePageNumber(pagePlusOffsetAddress);\n      assert (pageNumber >= 0 && pageNumber < PAGE_TABLE_SIZE);\n      final MemoryBlock page = pageTable[pageNumber];\n      assert (page != null);\n      assert (page.getBaseObject() != null);\n      return page.getBaseObject();\n    } else {\n      return null;\n    }\n  }\n\n  /**\n   * Get the offset associated with an address encoded by\n   * {@link DataNodeMemoryManager#encodePageNumberAndOffset(MemoryBlock, long)}\n   */\n  public long getOffsetInPage(long pagePlusOffsetAddress) {\n    final long offsetInPage = decodeOffset(pagePlusOffsetAddress);\n    if (tungstenMemoryMode == MemoryMode.ON_HEAP) {\n      return offsetInPage;\n    } else {\n      // In off-heap mode, an offset is an absolute address. In encodePageNumberAndOffset, we\n      // converted the absolute address into a relative address. Here, we invert that operation:\n      final int pageNumber = decodePageNumber(pagePlusOffsetAddress);\n      assert (pageNumber >= 0 && pageNumber < PAGE_TABLE_SIZE);\n      final MemoryBlock page = pageTable[pageNumber];\n      assert (page != null);\n      return page.getBaseOffset() + offsetInPage;\n    }\n  }\n\n  /**\n   * Clean up all allocated memory and pages. Returns the number of bytes freed. A non-zero return\n   * value can be used to detect memory leaks.\n   */\n  public long cleanUpAllAllocatedMemory() {\n    synchronized (this) {\n      for (MemoryConsumer c: consumers) {\n        if (c != null && c.getUsed() > 0) {\n          // In case of failed task, it's normal to see leaked memory\n          logger.warn(\"leak \" + JavaUtils.bytesToString(c.getUsed()) + \" memory from \" + c);\n        }\n      }\n      consumers.clear();\n\n      for (MemoryBlock page : pageTable) {\n        if (page != null) {\n          logger.warn(\"leak a page: \" + page + \" in task \" + connectionAttemptId);\n          memoryManager.tungstenMemoryAllocator().free(page);\n        }\n      }\n      Arrays.fill(pageTable, null);\n    }\n\n    // release the memory that is not used by any consumer.\n    memoryManager.releaseExecutionMemory(acquiredButNotUsed, connectionAttemptId, tungstenMemoryMode);\n\n    return memoryManager.releaseAllExecutionMemoryForConnection(connectionAttemptId);\n  }\n\n  /**\n   * Returns the memory consumption, in bytes, for the current task.\n   */\n  public long getMemoryConsumptionForThisConnection() {\n    return memoryManager.getExecutionMemoryUsageForConnection(connectionAttemptId);\n  }\n\n  /**\n   * Returns Tungsten memory mode\n   */\n  public MemoryMode getTungstenMemoryMode() {\n    return tungstenMemoryMode;\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/memory/mm/MemoryConsumer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.mycat.memory.unsafe.memory.mm;\n\nimport io.mycat.memory.unsafe.array.CharArray;\nimport io.mycat.memory.unsafe.array.LongArray;\nimport io.mycat.memory.unsafe.memory.MemoryBlock;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOException;\n/**\n * An memory consumer of DataNodeMemoryManager, which support spilling.\n * Note: this only supports allocation / spilling of Tungsten memory.\n */\npublic abstract class MemoryConsumer {\n  private final Logger logger = LoggerFactory.getLogger(MemoryConsumer.class);\n\n  protected final DataNodeMemoryManager dataNodeMemoryManager;\n  private final long pageSize;\n  protected long used;\n\n  protected MemoryConsumer(DataNodeMemoryManager dataNodeMemoryManager, long pageSize) {\n    this.dataNodeMemoryManager = dataNodeMemoryManager;\n    this.pageSize = pageSize;\n  }\n\n  protected MemoryConsumer(DataNodeMemoryManager dataNodeMemoryManager) {\n    this(dataNodeMemoryManager, dataNodeMemoryManager.pageSizeBytes());\n  }\n\n  /**\n   * Returns the size of used memory in bytes.\n   */\n  public long getUsed() {\n    return used;\n  }\n\n  /**\n   * Force spill during building.\n   *\n   * For testing.\n   */\n  public void spill() throws IOException {\n    spill(Long.MAX_VALUE, this);\n  }\n\n  /**\n   * Spill some data to disk to release memory, which will be called by DataNodeMemoryManager\n   * when there is not enough memory for the task.\n   *\n   * This should be implemented by subclass.\n   *\n   * Note: In order to avoid possible deadlock, should not call acquireMemory() from spill().\n   *\n   * Note: today, this only frees Tungsten-managed pages.\n   *\n   * @param size the amount of memory should be released\n   * @param trigger the MemoryConsumer that trigger this spilling\n   * @return the amount of released memory in bytes\n   * @throws IOException\n   */\n  public abstract long spill(long size, MemoryConsumer trigger) throws IOException;\n\n  /**\n   * Allocates a LongArray of `size`.\n   */\n  public LongArray allocateLongArray(long size) {\n    long required = size * 8L;\n    MemoryBlock page = dataNodeMemoryManager.allocatePage(required,this);\n    if (page == null || page.size() < required) {\n      long got = 0;\n      if (page != null) {\n        got = page.size();\n        dataNodeMemoryManager.freePage(page, this);\n      }\n      dataNodeMemoryManager.showMemoryUsage();\n      throw new OutOfMemoryError(\"Unable to acquire \" + required + \" bytes of memory, got \" + got);\n    }\n    used += required;\n    return new LongArray(page);\n  }\n\n  /**\n   * Frees a LongArray.\n   */\n  public void freeLongArray(LongArray array) {\n    freePage(array.memoryBlock());\n  }\n\n  public CharArray allocateCharArray(long size) {\n    long required = size * 2L;\n    MemoryBlock page = dataNodeMemoryManager.allocatePage(required,this);\n    if (page == null || page.size() < required) {\n      long got = 0;\n      if (page != null) {\n        got = page.size();\n        dataNodeMemoryManager.freePage(page, this);\n      }\n      dataNodeMemoryManager.showMemoryUsage();\n      throw new OutOfMemoryError(\"Unable to acquire \" + required + \" bytes of memory, got \" + got);\n    }\n    used += required;\n    return new CharArray(page,this);\n  }\n\n  /**\n   * Frees a CharArray.\n   */\n  public void freeCharArray(CharArray array) {\n    freePage(array.memoryBlock());\n  }\n\n  /**\n   * Allocate a memory block with at least `required` bytes.\n   *\n   * Throws IOException if there is not enough memory.\n   *\n   * @throws OutOfMemoryError\n   */\n  protected MemoryBlock allocatePage(long required) {\n    MemoryBlock page = dataNodeMemoryManager.allocatePage(Math.max(pageSize, required), this);\n    if (page == null || page.size() < required) {\n      long got = 0;\n      if (page != null) {\n        got = page.size();\n        dataNodeMemoryManager.freePage(page,this);\n      }\n      dataNodeMemoryManager.showMemoryUsage();\n      throw new OutOfMemoryError(\"Unable to acquire \" + required + \" bytes of memory, got \" + got);\n    }\n    used += page.size();\n    return page;\n  }\n\n  /**\n   * Free a memory block.\n   */\n  protected void freePage(MemoryBlock page) {\n    used -= page.size();\n    dataNodeMemoryManager.freePage(page, this);\n  }\n\n  /**\n   * Allocates a heap memory of `size`.\n   */\n  public long acquireOnHeapMemory(long size) {\n    long granted = 0;\n    try {\n      granted = dataNodeMemoryManager.acquireExecutionMemory(size, MemoryMode.ON_HEAP, this);\n    } catch (InterruptedException e) {\n      logger.error(e.getMessage());\n    }\n    used += granted;\n    return granted;\n  }\n\n  /**\n   * Release N bytes of heap memory.\n   */\n  public void freeOnHeapMemory(long size) {\n    dataNodeMemoryManager.releaseExecutionMemory(size, MemoryMode.ON_HEAP, this);\n    used -= size;\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/memory/mm/MemoryManager.java",
    "content": "package io.mycat.memory.unsafe.memory.mm;\n\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.array.ByteArrayMethods;\nimport io.mycat.memory.unsafe.memory.MemoryAllocator;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\nimport javax.annotation.concurrent.GuardedBy;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic abstract class MemoryManager {\n\n  private MycatPropertyConf conf;\n\n  @GuardedBy(\"this\")\n  protected ResultSetMemoryPool onHeapExecutionMemoryPool =\n          new ResultSetMemoryPool(this, MemoryMode.ON_HEAP);\n\n  @GuardedBy(\"this\")\n  protected ResultSetMemoryPool offHeapExecutionMemoryPool =\n          new ResultSetMemoryPool(this, MemoryMode.OFF_HEAP);\n\n  protected long maxOffHeapMemory = 0L;\n  protected long offHeapExecutionMemory = 0L;\n  private  int numCores = 0;\n\n  public MemoryManager(MycatPropertyConf conf, int numCores, long onHeapExecutionMemory){\n    this.conf = conf;\n    this.numCores =numCores;\n    maxOffHeapMemory = conf.getSizeAsBytes(\"mycat.memory.offHeap.size\",\"128m\");\n    offHeapExecutionMemory = maxOffHeapMemory;\n    onHeapExecutionMemoryPool.incrementPoolSize(onHeapExecutionMemory);\n\n    offHeapExecutionMemoryPool.incrementPoolSize(offHeapExecutionMemory);\n  }\n\n  protected abstract long acquireExecutionMemory(long numBytes,long taskAttemptId,MemoryMode memoryMode) throws InterruptedException;\n\n  /**\n   * Release numBytes of execution memory belonging to the given task.\n   */\npublic void releaseExecutionMemory(long numBytes, long taskAttemptId, MemoryMode memoryMode) {\n  synchronized (this) {\n      switch (memoryMode) {\n          case ON_HEAP:\n              onHeapExecutionMemoryPool.releaseMemory(numBytes, taskAttemptId);\n              break;\n          case OFF_HEAP:\n              offHeapExecutionMemoryPool.releaseMemory(numBytes, taskAttemptId);\n              break;\n      }\n   }\n\n  }\n\n  /**\n   * Release all memory for the given task and mark it as inactive (e.g. when a task ends).\n   * @return the number of bytes freed.\n   */\n  public  long releaseAllExecutionMemoryForConnection(long connAttemptId){\n      synchronized(this) {\n          return (onHeapExecutionMemoryPool.releaseAllMemoryForeConnection(connAttemptId) +\n                  offHeapExecutionMemoryPool.releaseAllMemoryForeConnection(connAttemptId));\n      }\n  }\n\n  /**\n   * Execution memory currently in use, in bytes.\n   */\n  public  final long executionMemoryUsed() {\n      synchronized(this) {\n          return (onHeapExecutionMemoryPool.memoryUsed() + offHeapExecutionMemoryPool.memoryUsed());\n      }\n  }\n\n  /**\n   * Returns the execution memory consumption, in bytes, for the given task.\n   */\n  public  long getExecutionMemoryUsageForConnection(long connAttemptId)  {\n      synchronized (this) {\n          assert (connAttemptId >= 0);\n          return (onHeapExecutionMemoryPool.getMemoryUsageConnection(connAttemptId) +\n                  offHeapExecutionMemoryPool.getMemoryUsageConnection(connAttemptId));\n      }\n  }\n\n  /**\n   * Tracks whether Tungsten memory will be allocated on the JVM heap or off-heap using\n   * sun.misc.Unsafe.\n   */\n  public final MemoryMode tungstenMemoryMode(){\n    if (conf.getBoolean(\"mycat.memory.offHeap.enabled\", false)) {\n      assert (conf.getSizeAsBytes(\"mycat.memory.offHeap.size\",0) > 0);\n      assert (Platform.unaligned());\n      return MemoryMode.OFF_HEAP;\n    } else {\n      return  MemoryMode.ON_HEAP;\n    }\n  }\n\n  /**\n   * The default page size, in bytes.\n   *\n   * If user didn't explicitly set \"mycat.buffer.pageSize\", we figure out the default value\n   * by looking at the number of cores available to the process, and the total amount of memory,\n   * and then divide it by a factor of safety.\n   */\n  public long pageSizeBytes() {\n\n    long minPageSize = 1L * 1024 * 1024 ;  // 1MB\n    long maxPageSize = 64L * minPageSize ; // 64MB\n\n    int cores = 0;\n\n    if (numCores > 0){\n       cores = numCores ;\n    } else {\n      cores =  Runtime.getRuntime().availableProcessors();\n    }\n\n    // Because of rounding to next power of 2, we may have safetyFactor as 8 in worst case\n    int safetyFactor = 16;\n    long maxTungstenMemory = 0L;\n\n    switch (tungstenMemoryMode()){\n      case ON_HEAP:\n        maxTungstenMemory = onHeapExecutionMemoryPool.poolSize();\n        break;\n      case OFF_HEAP:\n        maxTungstenMemory = offHeapExecutionMemoryPool.poolSize();\n        break;\n    }\n\n    long size = ByteArrayMethods.nextPowerOf2(maxTungstenMemory / cores / safetyFactor);\n    long defaultSize =  Math.min(maxPageSize, Math.max(minPageSize, size));\n    defaultSize = conf.getSizeAsBytes(\"mycat.buffer.pageSize\", defaultSize);\n\n    return defaultSize;\n  }\n\n  /**\n   * Allocates memory for use by Unsafe/Tungsten code.\n   */\n  public final MemoryAllocator tungstenMemoryAllocator() {\n    switch (tungstenMemoryMode()){\n      case ON_HEAP:\n        return MemoryAllocator.HEAP;\n      case OFF_HEAP:\n        return MemoryAllocator.UNSAFE;\n    }\n    return null;\n  }\n\n    /**\n     * Get Direct Memory Usage.\n     */\n    public final ConcurrentHashMap<Long, Long> getDirectMemorUsage() {\n\n        return offHeapExecutionMemoryPool.getMemoryForConnection();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/memory/mm/MemoryMode.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.memory.mm;\n\npublic enum MemoryMode {\n  ON_HEAP,\n  OFF_HEAP\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/memory/mm/MemoryPool.java",
    "content": "package io.mycat.memory.unsafe.memory.mm;\n\nimport javax.annotation.concurrent.GuardedBy;\n\n/**\n * Manages bookkeeping for an adjustable-sized region of memory. This class is internal to\n * the [[MemoryManager]]. See subclasses for more details.\n *\n */\npublic  abstract class MemoryPool {\n  /**\n   *  lock [[MemoryManager]] instance, used for synchronization. We purposely erase the type\n   *  to `Object` to avoid programming errors, since this object should only be used for\n   *  synchronization purposes.\n   */\n  protected final Object lock;\n  public  MemoryPool(Object lock){\n    this.lock = lock;\n  }\n\n  @GuardedBy(\"lock\")\n  private  long _poolSize = 0;\n\n  /**\n   * Returns the current size of the pool, in bytes.\n   */\n  public final long poolSize()  {\n    synchronized(lock) {\n      return _poolSize;\n    }\n  }\n\n  /**\n   * Returns the amount of free memory in the pool, in bytes.\n   */\n  public long memoryFree() {\n    synchronized(lock) {\n     return (_poolSize - memoryUsed());\n    }\n  }\n\n  /**\n   * Expands the pool by `delta` bytes.\n   */\n  public final void incrementPoolSize(long delta) {\n      assert (delta >= 0);\n      synchronized(lock) {\n        _poolSize += delta;\n      }\n  }\n\n  /**\n   * Shrinks the pool by `delta` bytes.\n   */\n  public final void decrementPoolSize(long delta){\n      synchronized(lock) {\n        assert (delta >= 0);\n        assert (delta <= _poolSize);\n        assert (_poolSize - delta >= memoryUsed());\n        _poolSize -= delta;\n      }\n}\n\n  /**\n   * Returns the amount of used memory in this pool (in bytes).\n   */\n  protected abstract  long memoryUsed();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/memory/mm/ResultMergeMemoryManager.java",
    "content": "package io.mycat.memory.unsafe.memory.mm;\n\n\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\n\n/**\n * Created by zagnix on 2016/6/7.\n */\npublic class ResultMergeMemoryManager extends MemoryManager {\n\n    private long  maxOnHeapExecutionMemory;\n    private int numCores;\n    private MycatPropertyConf conf;\n    public ResultMergeMemoryManager(MycatPropertyConf conf, int numCores, long onHeapExecutionMemory){\n        super(conf,numCores,onHeapExecutionMemory);\n        this.conf = conf;\n        this.numCores = numCores;\n        this.maxOnHeapExecutionMemory = onHeapExecutionMemory;\n    }\n\n    @Override\n    protected  synchronized long acquireExecutionMemory(long numBytes,long taskAttemptId,MemoryMode memoryMode) throws InterruptedException {\n        switch (memoryMode) {\n            case ON_HEAP:\n                return  onHeapExecutionMemoryPool.acquireMemory(numBytes,taskAttemptId);\n            case OFF_HEAP:\n                return  offHeapExecutionMemoryPool.acquireMemory(numBytes,taskAttemptId);\n        }\n        return 0L;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/memory/mm/ResultSetMemoryPool.java",
    "content": "package io.mycat.memory.unsafe.memory.mm;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.annotation.concurrent.GuardedBy;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * Created by zagnix on 2016/6/6.\n */\npublic class ResultSetMemoryPool extends MemoryPool {\n    private static final Logger LOG = LoggerFactory.getLogger(ResultSetMemoryPool.class);\n\n    private  MemoryMode memoryMode ;\n\n    /**\n     * @param lock a [[MemoryManager]] instance to synchronize on\n     * @param memoryMode the type of memory tracked by this pool (on- or off-heap)\n     */\n    public ResultSetMemoryPool(Object lock, MemoryMode memoryMode) {\n        super(lock);\n        this.memoryMode = memoryMode;\n    }\n\n\n    private String poolName(){\n\n        switch (memoryMode){\n            case ON_HEAP:\n                return  \"on-heap memory\";\n            case OFF_HEAP:\n                return \"off-heap memory\";\n        }\n\n        return \"off-heap memory\";\n    }\n\n    public ConcurrentHashMap<Long, Long> getMemoryForConnection() {\n        return memoryForConnection;\n    }\n    /**\n     * Map from taskAttemptId -> memory consumption in bytes\n     */\n    private ConcurrentHashMap<Long,Long> memoryForConnection = new ConcurrentHashMap<Long,Long>();\n\n    @Override\n    protected long memoryUsed() {\n        synchronized (lock) {\n            long used =0;\n            for (Map.Entry<Long, Long> entry : memoryForConnection.entrySet()) {\n                used += entry.getValue();\n            }\n            return used;\n        }\n    }\n\n\n    /**\n     * Returns the memory consumption, in bytes, for the given task.\n     */\n    public  long getMemoryUsageConnection(long taskAttemptId) {\n        synchronized (lock) {\n            if (!memoryForConnection.containsKey(taskAttemptId)) {\n                memoryForConnection.put(taskAttemptId, 0L);\n            }\n            return memoryForConnection.get(taskAttemptId);\n        }\n    }\n\n\n    /**\n     * Try to acquire up to `numBytes` of memory for the given task and return the number of bytes\n     * obtained, or 0 if none can be allocated.\n     *\n     * This call may block until there is enough free memory in some situations, to make sure each\n     * task has a chance to ramp up to at least 1 / 8N of the total memory pool (where N is the # of\n     * active tasks) before it is forced to spill. This can happen if the number of tasks increase\n     * but an older task had a lot of memory already.\n     *\n     * @param numBytes number of bytes to acquire\n     * @param connAttemptId the task attempt acquiring memory\n     * @return the number of bytes granted to the task.\n     */\n    public  long acquireMemory(long numBytes, long connAttemptId) throws InterruptedException {\n\n        synchronized (lock) {\n            assert (numBytes > 0);\n            // Add this connection to the taskMemory map just so we can keep an accurate count of the number\n            // of active tasks, to let other tasks ramp down their memory in calls to `acquireMemory`\n            if (!memoryForConnection.containsKey(connAttemptId)) {\n                memoryForConnection.put(connAttemptId, 0L);\n                // This will later cause waiting tasks to wake up and check numTasks again\n                lock.notifyAll();\n            }\n\n\n            while (true) {\n                long numActiveConns = memoryForConnection.size();\n                long curMem = memoryForConnection.get(connAttemptId);\n\n                long maxPoolSize = poolSize();\n                long maxMemoryPerTask = maxPoolSize / numActiveConns;\n                long minMemoryPerTask = poolSize() / (8 * numActiveConns);\n\n                // How much we can grant this connection; keep its share within 0 <= X <= 1 / numActiveConns\n                long maxToGrant = Math.min(numBytes, Math.max(0, maxMemoryPerTask - curMem));\n                // Only give it as much memory as is free, which might be none if it reached 1 / numActiveConns\n                long toGrant = Math.min(maxToGrant, memoryFree());\n\n                // We want to let each connection get at least 1 / (8 * numActiveConns) before blocking;\n                // if we can't give it this much now, wait for other tasks to free up memory\n                // (this happens if older tasks allocated lots of memory before N grew)\n                if (toGrant < numBytes && curMem + toGrant < minMemoryPerTask) {\n                    LOG.info(\"Thread \" + connAttemptId + \" waiting for at least 1/8N of \" + poolName() + \" pool to be free\");\n                    lock.wait();\n                } else {\n                    long temp = memoryForConnection.get(connAttemptId);\n                    memoryForConnection.put(connAttemptId, (temp + toGrant));\n                    return toGrant;\n                }\n            }\n        }\n    }\n\n    /**\n     * Release `numBytes` of memory acquired by the given task.\n     */\n    public  void releaseMemory(long numBytes, long connAttemptId) {\n\n        synchronized (lock) {\n            long curMem = memoryForConnection.get(connAttemptId);\n\n            long memoryToFree = 0L;\n\n            if (curMem < numBytes) {\n                 LOG.error(\n                        \"Internal error: release called on $numBytes bytes but task only has $curMem bytes \" +\n                                \"of memory from the \" + poolName() + \"  pool\");\n                memoryToFree = curMem;\n            } else {\n                memoryToFree = numBytes;\n            }\n\n            if (memoryForConnection.containsKey(connAttemptId)) {\n                long temp = memoryForConnection.get(connAttemptId);\n                memoryForConnection.put(connAttemptId, (temp - memoryToFree));\n                if (memoryForConnection.get(connAttemptId) <= 0) {\n                    memoryForConnection.remove(connAttemptId);\n                }\n            }\n            // Notify waiters in acquireMemory() that memory has been freed\n            lock.notifyAll();\n        }\n    }\n\n    /**\n     * Release all memory for the given task and mark it as inactive (e.g. when a task ends).\n     * @return the number of bytes freed.\n     */\n    public  long releaseAllMemoryForeConnection(long connAttemptId) {\n        synchronized (lock){\n            long numBytesToFree = getMemoryUsageConnection(connAttemptId);\n            releaseMemory(numBytesToFree,connAttemptId);\n            return numBytesToFree;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/RingBuffer.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer;\n\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.memory.mm.MemoryConsumer;\nimport io.mycat.memory.unsafe.ringbuffer.common.Cursored;\nimport io.mycat.memory.unsafe.ringbuffer.common.event.*;\nimport io.mycat.memory.unsafe.ringbuffer.exception.InsufficientCapacityException;\nimport io.mycat.memory.unsafe.ringbuffer.producer.Sequencer;\n\n/**\n * 环形buffer 待实现，\n */\npublic class RingBuffer<E extends MemoryConsumer> implements Cursored, EventSequencer<E>, EventSink<E> {\n    //Buffer数组填充\n    private static final int BUFFER_PAD;\n    //Buffer数组起始基址\n    private static final long REF_ARRAY_BASE;\n    //2^n=每个数组对象引用所占空间，这个n就是REF_ELEMENT_SHIFT\n    private static final int REF_ELEMENT_SHIFT;\n\n    static {\n        final int scale = Platform.arrayIndexScale(Object[].class);\n        //Object数组引用长度，32位为4字节，64位为8字节\n        if (4 == scale) {\n            REF_ELEMENT_SHIFT = 2;\n        } else if (8 == scale) {\n            REF_ELEMENT_SHIFT = 3;\n        } else {\n            throw new IllegalStateException(\"Unknown pointer size\");\n        }\n        //需要填充128字节，缓存行长度一般是128字节\n        BUFFER_PAD = 128 / scale;\n        REF_ARRAY_BASE = Platform.arrayBaseOffset(Object[].class) + (BUFFER_PAD << REF_ELEMENT_SHIFT);\n    }\n\n    private final long indexMask;\n    private final Object[] entries;\n    protected final int bufferSize;\n    protected final Sequencer sequencer;\n\n    public RingBuffer(EventFactory<E> eventFactory, Sequencer sequencer) {\n        this.sequencer = sequencer;\n        this.bufferSize = sequencer.getBufferSize();\n        //保证buffer大小不小于1\n        if (bufferSize < 1) {\n            throw new IllegalArgumentException(\"bufferSize must not be less than 1\");\n        }\n        //保证buffer大小为2的n次方\n        if (Integer.bitCount(bufferSize) != 1) {\n            throw new IllegalArgumentException(\"bufferSize must be a power of 2\");\n        }\n        //m % 2^n  <=>  m & (2^n - 1)\n        this.indexMask = bufferSize - 1;\n        /**\n         * 结构：缓存行填充，避免频繁访问的任一entry与另一被修改的无关变量写入同一缓存行\n         * --------------\n         * *   数组头   * BASE\n         * *   Padding  * 128字节\n         * * reference1 * SCALE\n         * * reference2 * SCALE\n         * * reference3 * SCALE\n         * ..........\n         * *   Padding  * 128字节\n         * --------------\n         */\n        this.entries = new Object[sequencer.getBufferSize() + 2 * BUFFER_PAD];\n        //利用eventFactory初始化RingBuffer的每个槽\n        fill(eventFactory);\n    }\n\n    private void fill(EventFactory<E> eventFactory) {\n        for (int i = 0; i < bufferSize; i++) {\n            entries[BUFFER_PAD + i] = eventFactory.newInstance();\n        }\n    }\n\n    /**\n     * 根据地址取出一个元素的引用\n     *\n     * @param sequence\n     * @return\n     */\n    private E elementAt(long sequence) {\n        return (E) Platform.getObject(entries, REF_ARRAY_BASE + ((sequence & indexMask) << REF_ELEMENT_SHIFT));\n    }\n\n\n    @Override\n    public long getCursor() {\n        return sequencer.getCursor();\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.DataProvider#get(long)\n     */\n    @Override\n    public E get(long sequence) {\n        return elementAt(sequence);\n    }\n\n    private void translateAndPublish(EventTranslator<E> translator, long sequence) {\n        try {\n            translator.translateTo(get(sequence), sequence);\n        } finally {\n            sequencer.publish(sequence);\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public void publishEvent(EventTranslator<E> translator) {\n        final long sequence = sequencer.next();\n        translateAndPublish(translator, sequence);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#tryPublishEvent(EventTranslator)\n     */\n    @Override\n    public boolean tryPublishEvent(EventTranslator<E> translator) {\n        try {\n            final long sequence = sequencer.tryNext();\n            translateAndPublish(translator, sequence);\n            return true;\n        } catch (InsufficientCapacityException e) {\n            return false;\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    private <A> void translateAndPublish(EventTranslatorOneArg<E, A> translator, long sequence, A arg0) {\n        try {\n            translator.translateTo(get(sequence), sequence, arg0);\n        } finally {\n            sequencer.publish(sequence);\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A> void publishEvent(EventTranslatorOneArg<E, A> translator, A arg0) {\n        final long sequence = sequencer.next();\n        translateAndPublish(translator, sequence, arg0);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A> boolean tryPublishEvent(EventTranslatorOneArg<E, A> translator, A arg0) {\n        try {\n            final long sequence = sequencer.tryNext();\n            translateAndPublish(translator, sequence, arg0);\n            return true;\n        } catch (InsufficientCapacityException e) {\n            return false;\n        }\n    }\n\n    private <A, B> void translateAndPublish(EventTranslatorTwoArg<E, A, B> translator, long sequence, A arg0, B arg1) {\n        try {\n            translator.translateTo(get(sequence), sequence, arg0, arg1);\n        } finally {\n            sequencer.publish(sequence);\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A, B> void publishEvent(EventTranslatorTwoArg<E, A, B> translator, A arg0, B arg1) {\n        final long sequence = sequencer.next();\n        translateAndPublish(translator, sequence, arg0, arg1);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A, B> boolean tryPublishEvent(EventTranslatorTwoArg<E, A, B> translator, A arg0, B arg1) {\n        try {\n            final long sequence = sequencer.tryNext();\n            translateAndPublish(translator, sequence, arg0, arg1);\n            return true;\n        } catch (InsufficientCapacityException e) {\n            return false;\n        }\n    }\n\n    private <A, B, C> void translateAndPublish(\n            EventTranslatorThreeArg<E, A, B, C> translator, long sequence,\n            A arg0, B arg1, C arg2) {\n        try {\n            translator.translateTo(get(sequence), sequence, arg0, arg1, arg2);\n        } finally {\n            sequencer.publish(sequence);\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A, B, C> void publishEvent(EventTranslatorThreeArg<E, A, B, C> translator, A arg0, B arg1, C arg2) {\n        final long sequence = sequencer.next();\n        translateAndPublish(translator, sequence, arg0, arg1, arg2);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A, B, C> boolean tryPublishEvent(EventTranslatorThreeArg<E, A, B, C> translator, A arg0, B arg1, C arg2) {\n        try {\n            final long sequence = sequencer.tryNext();\n            translateAndPublish(translator, sequence, arg0, arg1, arg2);\n            return true;\n        } catch (InsufficientCapacityException e) {\n            return false;\n        }\n    }\n\n    private void translateAndPublish(EventTranslatorVararg<E> translator, long sequence, Object... args) {\n        try {\n            translator.translateTo(get(sequence), sequence, args);\n        } finally {\n            sequencer.publish(sequence);\n        }\n    }\n\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public void publishEvent(EventTranslatorVararg<E> translator, Object... args) {\n        final long sequence = sequencer.next();\n        translateAndPublish(translator, sequence, args);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public boolean tryPublishEvent(EventTranslatorVararg<E> translator, Object... args) {\n        try {\n            final long sequence = sequencer.tryNext();\n            translateAndPublish(translator, sequence, args);\n            return true;\n        } catch (InsufficientCapacityException e) {\n            return false;\n        }\n    }\n\n    private void checkBounds(final EventTranslator<E>[] translators, final int batchStartsAt, final int batchSize) {\n        checkBatchSizing(batchStartsAt, batchSize);\n        batchOverRuns(translators, batchStartsAt, batchSize);\n    }\n\n    private <A> void batchOverRuns(final A[] arg0, final int batchStartsAt, final int batchSize) {\n        if (batchStartsAt + batchSize > arg0.length) {\n            throw new IllegalArgumentException(\n                    \"A batchSize of: \" + batchSize +\n                            \" with batchStatsAt of: \" + batchStartsAt +\n                            \" will overrun the available number of arguments: \" + (arg0.length - batchStartsAt));\n        }\n    }\n\n    private void checkBatchSizing(int batchStartsAt, int batchSize) {\n        if (batchStartsAt < 0 || batchSize < 0) {\n            throw new IllegalArgumentException(\"Both batchStartsAt and batchSize must be positive but got: batchStartsAt \" + batchStartsAt + \" and batchSize \" + batchSize);\n        } else if (batchSize > bufferSize) {\n            throw new IllegalArgumentException(\"The ring buffer cannot accommodate \" + batchSize + \" it only has space for \" + bufferSize + \" entities.\");\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public void publishEvents(EventTranslator<E>[] translators) {\n        publishEvents(translators, 0, translators.length);\n    }\n\n    private void translateAndPublishBatch(\n            final EventTranslator<E>[] translators, int batchStartsAt,\n            final int batchSize, final long finalSequence) {\n        final long initialSequence = finalSequence - (batchSize - 1);\n        try {\n            long sequence = initialSequence;\n            final int batchEndsAt = batchStartsAt + batchSize;\n            for (int i = batchStartsAt; i < batchEndsAt; i++) {\n                final EventTranslator<E> translator = translators[i];\n                translator.translateTo(get(sequence), sequence++);\n            }\n        } finally {\n            sequencer.publish(initialSequence, finalSequence);\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public void publishEvents(EventTranslator<E>[] translators, int batchStartsAt, int batchSize) {\n        checkBounds(translators, batchStartsAt, batchSize);\n        final long finalSequence = sequencer.next(batchSize);\n        translateAndPublishBatch(translators, batchStartsAt, batchSize, finalSequence);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public boolean tryPublishEvents(EventTranslator<E>[] translators) {\n        return false;\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public boolean tryPublishEvents(EventTranslator<E>[] translators, int batchStartsAt, int batchSize) {\n        checkBounds(translators, batchStartsAt, batchSize);\n        try {\n            final long finalSequence = sequencer.tryNext(batchSize);\n            translateAndPublishBatch(translators, batchStartsAt, batchSize, finalSequence);\n            return true;\n        } catch (InsufficientCapacityException e) {\n            return false;\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A> void publishEvents(EventTranslatorOneArg<E, A> translator, A[] arg0) {\n        publishEvents(translator, 0, arg0.length, arg0);\n    }\n\n    private <A> void checkBounds(final A[] arg0, final int batchStartsAt, final int batchSize) {\n        checkBatchSizing(batchStartsAt, batchSize);\n        batchOverRuns(arg0, batchStartsAt, batchSize);\n    }\n\n    private <A> void translateAndPublishBatch(\n            final EventTranslatorOneArg<E, A> translator, final A[] arg0,\n            int batchStartsAt, final int batchSize, final long finalSequence) {\n        final long initialSequence = finalSequence - (batchSize - 1);\n        try {\n            long sequence = initialSequence;\n            final int batchEndsAt = batchStartsAt + batchSize;\n            for (int i = batchStartsAt; i < batchEndsAt; i++) {\n                translator.translateTo(get(sequence), sequence++, arg0[i]);\n            }\n        } finally {\n            sequencer.publish(initialSequence, finalSequence);\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A> void publishEvents(EventTranslatorOneArg<E, A> translator, int batchStartsAt, int batchSize, A[] arg0) {\n        checkBounds(arg0, batchStartsAt, batchSize);\n        final long finalSequence = sequencer.next(batchSize);\n        translateAndPublishBatch(translator, arg0, batchStartsAt, batchSize, finalSequence);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A> boolean tryPublishEvents(EventTranslatorOneArg<E, A> translator, A[] arg0) {\n        return tryPublishEvents(translator, 0, arg0.length, arg0);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A> boolean tryPublishEvents(EventTranslatorOneArg<E, A> translator, int batchStartsAt, int batchSize, A[] arg0) {\n        checkBounds(arg0, batchStartsAt, batchSize);\n        try {\n            final long finalSequence = sequencer.tryNext(batchSize);\n            translateAndPublishBatch(translator, arg0, batchStartsAt, batchSize, finalSequence);\n            return true;\n        } catch (InsufficientCapacityException e) {\n            return false;\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A, B> void publishEvents(EventTranslatorTwoArg<E, A, B> translator, A[] arg0, B[] arg1) {\n        publishEvents(translator, 0, arg0.length, arg0, arg1);\n    }\n\n    private <A, B> void checkBounds(final A[] arg0, final B[] arg1, final int batchStartsAt, final int batchSize) {\n        checkBatchSizing(batchStartsAt, batchSize);\n        batchOverRuns(arg0, batchStartsAt, batchSize);\n        batchOverRuns(arg1, batchStartsAt, batchSize);\n    }\n\n    private <A, B> void translateAndPublishBatch(\n            final EventTranslatorTwoArg<E, A, B> translator, final A[] arg0,\n            final B[] arg1, int batchStartsAt, int batchSize,\n            final long finalSequence) {\n        final long initialSequence = finalSequence - (batchSize - 1);\n        try {\n            long sequence = initialSequence;\n            final int batchEndsAt = batchStartsAt + batchSize;\n            for (int i = batchStartsAt; i < batchEndsAt; i++) {\n                translator.translateTo(get(sequence), sequence++, arg0[i], arg1[i]);\n            }\n        } finally {\n            sequencer.publish(initialSequence, finalSequence);\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A, B> void publishEvents(EventTranslatorTwoArg<E, A, B> translator, int batchStartsAt, int batchSize, A[] arg0, B[] arg1) {\n        checkBounds(arg0, arg1, batchStartsAt, batchSize);\n        final long finalSequence = sequencer.next(batchSize);\n        translateAndPublishBatch(translator, arg0, arg1, batchStartsAt, batchSize, finalSequence);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A, B> boolean tryPublishEvents(EventTranslatorTwoArg<E, A, B> translator, A[] arg0, B[] arg1) {\n        return tryPublishEvents(translator, 0, arg0.length, arg0, arg1);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A, B> boolean tryPublishEvents(EventTranslatorTwoArg<E, A, B> translator, int batchStartsAt, int batchSize, A[] arg0, B[] arg1) {\n        checkBounds(arg0, arg1, batchStartsAt, batchSize);\n        try {\n            final long finalSequence = sequencer.tryNext(batchSize);\n            translateAndPublishBatch(translator, arg0, arg1, batchStartsAt, batchSize, finalSequence);\n            return true;\n        } catch (InsufficientCapacityException e) {\n            return false;\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A, B, C> void publishEvents(EventTranslatorThreeArg<E, A, B, C> translator, A[] arg0, B[] arg1, C[] arg2) {\n        publishEvents(translator, 0, arg0.length, arg0, arg1, arg2);\n    }\n\n    private <A, B, C> void checkBounds(\n            final A[] arg0, final B[] arg1, final C[] arg2, final int batchStartsAt, final int batchSize) {\n        checkBatchSizing(batchStartsAt, batchSize);\n        batchOverRuns(arg0, batchStartsAt, batchSize);\n        batchOverRuns(arg1, batchStartsAt, batchSize);\n        batchOverRuns(arg2, batchStartsAt, batchSize);\n    }\n\n    private <A, B, C> void translateAndPublishBatch(\n            final EventTranslatorThreeArg<E, A, B, C> translator,\n            final A[] arg0, final B[] arg1, final C[] arg2, int batchStartsAt,\n            final int batchSize, final long finalSequence) {\n        final long initialSequence = finalSequence - (batchSize - 1);\n        try {\n            long sequence = initialSequence;\n            final int batchEndsAt = batchStartsAt + batchSize;\n            for (int i = batchStartsAt; i < batchEndsAt; i++) {\n                translator.translateTo(get(sequence), sequence++, arg0[i], arg1[i], arg2[i]);\n            }\n        } finally {\n            sequencer.publish(initialSequence, finalSequence);\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A, B, C> void publishEvents(EventTranslatorThreeArg<E, A, B, C> translator, int batchStartsAt, int batchSize, A[] arg0, B[] arg1, C[] arg2) {\n        checkBounds(arg0, arg1, arg2, batchStartsAt, batchSize);\n        final long finalSequence = sequencer.next(batchSize);\n        translateAndPublishBatch(translator, arg0, arg1, arg2, batchStartsAt, batchSize, finalSequence);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A, B, C> boolean tryPublishEvents(EventTranslatorThreeArg<E, A, B, C> translator, A[] arg0, B[] arg1, C[] arg2) {\n        return tryPublishEvents(translator, 0, arg0.length, arg0, arg1, arg2);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public <A, B, C> boolean tryPublishEvents(EventTranslatorThreeArg<E, A, B, C> translator, int batchStartsAt, int batchSize, A[] arg0, B[] arg1, C[] arg2) {\n        checkBounds(arg0, arg1, arg2, batchStartsAt, batchSize);\n        try {\n            final long finalSequence = sequencer.tryNext(batchSize);\n            translateAndPublishBatch(translator, arg0, arg1, arg2, batchStartsAt, batchSize, finalSequence);\n            return true;\n        } catch (InsufficientCapacityException e) {\n            return false;\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public void publishEvents(EventTranslatorVararg<E> translator, Object[]... args) {\n        publishEvents(translator, 0, args.length, args);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public void publishEvents(EventTranslatorVararg<E> translator, int batchStartsAt, int batchSize, Object[]... args) {\n        checkBounds(batchStartsAt, batchSize, args);\n        final long finalSequence = sequencer.next(batchSize);\n        translateAndPublishBatch(translator, batchStartsAt, batchSize, finalSequence, args);\n    }\n\n    private void checkBounds(final int batchStartsAt, final int batchSize, final Object[][] args) {\n        checkBatchSizing(batchStartsAt, batchSize);\n        batchOverRuns(args, batchStartsAt, batchSize);\n    }\n\n    private void translateAndPublishBatch(\n            final EventTranslatorVararg<E> translator, int batchStartsAt,\n            final int batchSize, final long finalSequence, final Object[][] args) {\n        final long initialSequence = finalSequence - (batchSize - 1);\n        try {\n            long sequence = initialSequence;\n            final int batchEndsAt = batchStartsAt + batchSize;\n            for (int i = batchStartsAt; i < batchEndsAt; i++) {\n                translator.translateTo(get(sequence), sequence++, args[i]);\n            }\n        } finally {\n            sequencer.publish(initialSequence, finalSequence);\n        }\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public boolean tryPublishEvents(EventTranslatorVararg<E> translator, Object[]... args) {\n        return tryPublishEvents(translator, 0, args.length, args);\n    }\n\n    /**\n     * @see io.mycat.memory.unsafe.ringbuffer.common.event.EventSink#publishEvent(EventTranslator)\n     */\n    @Override\n    public boolean tryPublishEvents(EventTranslatorVararg<E> translator, int batchStartsAt, int batchSize, Object[]... args) {\n        return false;\n    }\n\n    @Override\n    public int getBufferSize() {\n        return bufferSize;\n    }\n\n    @Override\n    public boolean hasAvailableCapacity(int requiredCapacity) {\n        return sequencer.hasAvailableCapacity(requiredCapacity);\n    }\n\n    @Override\n    public long remainingCapacity() {\n        return sequencer.remainingCapacity();\n    }\n\n    @Override\n    public long next() {\n        return sequencer.next();\n    }\n\n    @Override\n    public long next(int n) {\n        return sequencer.next(n);\n    }\n\n    @Override\n    public long tryNext() throws InsufficientCapacityException {\n        return sequencer.tryNext();\n    }\n\n    @Override\n    public long tryNext(int n) throws InsufficientCapacityException {\n        return sequencer.tryNext(n);\n    }\n\n    @Override\n    public void publish(long sequence) {\n        sequencer.publish(sequence);\n    }\n\n    @Override\n    public void publish(long lo, long hi) {\n        sequencer.publish(lo, hi);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/Cursored.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/23\n */\npublic interface Cursored {\n    long getCursor();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/DataProvider.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/29\n */\npublic interface DataProvider<T> {\n    /**\n     * 获取sequence对应的对象\n     * @param sequence\n     * @return\n     */\n    public T get(long sequence);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/Sequenced.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common;\n\nimport io.mycat.memory.unsafe.ringbuffer.exception.InsufficientCapacityException;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/23\n */\npublic interface Sequenced {\n    /**\n     * @return ringBuffer的大小\n     */\n    int getBufferSize();\n\n    /**\n     * @param requiredCapacity 需要的大小\n     * @return true ringBuffer的剩余空间足够 | false ringBuffer的剩余空间不足\n     */\n    boolean hasAvailableCapacity(final int requiredCapacity);\n\n    /**\n     * @return ringBuffer的剩余空间\n     */\n    long remainingCapacity();\n\n    /**\n     * 申请下一个sequence(value)作为生产event的位置\n     * @return sequence的value\n     */\n    long next();\n\n    /**\n     * 申请下n个sequence(value)作为生产多个event的位置\n     * @param n\n     * @return 最高的sequence的value\n     */\n    long next(int n);\n    /**\n     * 尝试申请下一个sequence(value)作为生产event的位置\n     * @return sequence的value\n     * @throws InsufficientCapacityException\n     */\n    long tryNext() throws InsufficientCapacityException;\n\n    /**\n     * 尝试申请下n个sequence(value)作为生产多个event的位置\n     * @param n\n     * @return 最高的sequence的value\n     * @throws InsufficientCapacityException\n     */\n    long tryNext(int n) throws InsufficientCapacityException;\n\n    /**\n     * 发布一个Sequence，一般在这个Sequence对应位置的Event被填充后\n     * @param sequence\n     */\n    void publish(long sequence);\n\n    /**\n     * 发布多个Sequence，一般在这些Sequence对应位置的Event被填充后\n     * @param lo 第一个sequence的value\n     * @param hi 最后一个sequence的value\n     */\n    void publish(long lo, long hi);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/barrier/SequenceBarrier.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.barrier;\n\nimport io.mycat.memory.unsafe.ringbuffer.exception.AlertException;\nimport io.mycat.memory.unsafe.ringbuffer.exception.TimeoutException;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/24\n */\npublic interface SequenceBarrier {\n    /**\n     * 等待给定的sequence值可以被消费\n     *\n     * @param sequence 等待的sequence值\n     * @return 可以消费的最大sequence值\n     * @throws AlertException 当Disruptor的状态改变时会抛出\n     * @throws InterruptedException 唤醒线程\n     * @throws TimeoutException 超过最大等待时间\n     */\n    long waitFor(long sequence) throws AlertException, InterruptedException, TimeoutException;\n\n    /**\n     * 获取当前可以消费的cursor值\n     *\n     * @return 当前可以消费的cursor值（已经被publish的）\n     */\n    long getCursor();\n\n    /**\n     * alert状态\n     *\n     * @return true 如果被alerted\n     */\n    boolean isAlerted();\n\n    /**\n     * 进入alert状态\n     */\n    void alert();\n\n    /**\n     * 清除当前alert状态\n     */\n    void clearAlert();\n\n    /**\n     * 检查是否被alerted，如果是，则抛出{@link AlertException}\n     *\n     * @throws AlertException if alert has been raised.\n     */\n    void checkAlert() throws AlertException;\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/event/EventFactory.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.event;\n\n/**\n * 用户实现，生成Event的接口\n *\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/29\n */\npublic interface EventFactory<T> {\n    T newInstance();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/event/EventSequencer.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.event;\n\nimport io.mycat.memory.unsafe.ringbuffer.common.DataProvider;\nimport io.mycat.memory.unsafe.ringbuffer.common.Sequenced;\n\n/**\n * EventSequencer接口没有自己的方法，只是为了将Sequencer和DataProvider合起来。\n *\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/29\n */\npublic interface EventSequencer<T> extends DataProvider<T>, Sequenced {\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/event/EventSink.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.event;\n\n/**\n * Event槽接口\n *\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/29\n */\npublic interface EventSink<E> {\n    /**\n     * 申请下一个Sequence->申请成功则获取对应槽的Event->利用translator初始化并填充对应槽的Event->发布Event\n     * @param translator translator用户实现，用于初始化Event，这里是不带参数Translator\n     */\n     void publishEvent(EventTranslator<E> translator);\n\n    /**\n     * 尝试申请下一个Sequence->申请成功则获取对应槽的Event->利用translator初始化并填充对应槽的Event->发布Event\n     * 若空间不足，则立即失败返回\n     * @param translator translator用户实现，用于初始化Event，这里是不带参数Translator\n     * @return 成功true，失败false\n     */\n     boolean tryPublishEvent(EventTranslator<E> translator);\n\n     <A> void publishEvent(EventTranslatorOneArg<E, A> translator, A arg0);\n\n     <A> boolean tryPublishEvent(EventTranslatorOneArg<E, A> translator, A arg0);\n\n     <A, B> void publishEvent(EventTranslatorTwoArg<E, A, B> translator, A arg0, B arg1);\n\n     <A, B> boolean tryPublishEvent(EventTranslatorTwoArg<E, A, B> translator, A arg0, B arg1);\n\n     <A, B, C> void publishEvent(EventTranslatorThreeArg<E, A, B, C> translator, A arg0, B arg1, C arg2);\n\n     <A, B, C> boolean tryPublishEvent(EventTranslatorThreeArg<E, A, B, C> translator, A arg0, B arg1, C arg2);\n\n     void publishEvent(EventTranslatorVararg<E> translator, Object... args);\n\n     boolean tryPublishEvent(EventTranslatorVararg<E> translator, Object... args);\n\n    /**\n     * 包括申请多个Sequence->申请成功则获取对应槽的Event->利用每个translator初始化并填充每个对应槽的Event->发布Event\n     * @param translators\n     */\n     void publishEvents(EventTranslator<E>[] translators);\n\n     void publishEvents(EventTranslator<E>[] translators, int batchStartsAt, int batchSize);\n\n     boolean tryPublishEvents(EventTranslator<E>[] translators);\n\n     boolean tryPublishEvents(EventTranslator<E>[] translators, int batchStartsAt, int batchSize);\n\n     <A> void publishEvents(EventTranslatorOneArg<E, A> translator, A[] arg0);\n\n     <A> void publishEvents(EventTranslatorOneArg<E, A> translator, int batchStartsAt, int batchSize, A[] arg0);\n\n     <A> boolean tryPublishEvents(EventTranslatorOneArg<E, A> translator, A[] arg0);\n\n     <A> boolean tryPublishEvents(EventTranslatorOneArg<E, A> translator, int batchStartsAt, int batchSize, A[] arg0);\n\n     <A, B> void publishEvents(EventTranslatorTwoArg<E, A, B> translator, A[] arg0, B[] arg1);\n\n     <A, B> void publishEvents(\n            EventTranslatorTwoArg<E, A, B> translator, int batchStartsAt, int batchSize, A[] arg0,\n            B[] arg1);\n\n     <A, B> boolean tryPublishEvents(EventTranslatorTwoArg<E, A, B> translator, A[] arg0, B[] arg1);\n\n     <A, B> boolean tryPublishEvents(\n            EventTranslatorTwoArg<E, A, B> translator, int batchStartsAt, int batchSize,\n            A[] arg0, B[] arg1);\n\n     <A, B, C> void publishEvents(EventTranslatorThreeArg<E, A, B, C> translator, A[] arg0, B[] arg1, C[] arg2);\n\n     <A, B, C> void publishEvents(\n            EventTranslatorThreeArg<E, A, B, C> translator, int batchStartsAt, int batchSize,\n            A[] arg0, B[] arg1, C[] arg2);\n\n     <A, B, C> boolean tryPublishEvents(EventTranslatorThreeArg<E, A, B, C> translator, A[] arg0, B[] arg1, C[] arg2);\n\n     <A, B, C> boolean tryPublishEvents(\n            EventTranslatorThreeArg<E, A, B, C> translator, int batchStartsAt,\n            int batchSize, A[] arg0, B[] arg1, C[] arg2);\n\n     void publishEvents(EventTranslatorVararg<E> translator, Object[]... args);\n\n     void publishEvents(EventTranslatorVararg<E> translator, int batchStartsAt, int batchSize, Object[]... args);\n\n     boolean tryPublishEvents(EventTranslatorVararg<E> translator, Object[]... args);\n\n     boolean tryPublishEvents(EventTranslatorVararg<E> translator, int batchStartsAt, int batchSize, Object[]... args);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/event/EventTranslator.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.event;\n\n/**\n * Event初始化接口，生产者通过实现这个接口，在发布Event时，对应实现的translateTo方法会被调用\n *\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/29\n */\npublic interface EventTranslator<T> {\n     void translateTo(final T event, long sequence);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/event/EventTranslatorOneArg.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.event;\n\n/**\n * Event初始化接口，生产者通过实现这个接口，在发布Event时，对应实现的translateTo方法会被调用\n * 这里用户可以传一个参数\n *\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/29\n */\npublic interface EventTranslatorOneArg<T,A> {\n     void translateTo(final T event, long sequence, final A arg0);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/event/EventTranslatorThreeArg.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.event;\n\n/**\n * Event初始化接口，生产者通过实现这个接口，在发布Event时，对应实现的translateTo方法会被调用\n * 这里用户可以传三个参数\n *\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/29\n */\npublic interface EventTranslatorThreeArg<T, A, B, C> {\n     void translateTo(final T event, long sequence, final A arg0, final B arg1, final C arg2);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/event/EventTranslatorTwoArg.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.event;\n\n/**\n * Event初始化接口，生产者通过实现这个接口，在发布Event时，对应实现的translateTo方法会被调用\n * 这里用户可以传两个参数\n *\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/29\n */\npublic interface EventTranslatorTwoArg<T, A, B> {\n     void translateTo(final T event, long sequence, final A arg0, final B arg1);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/event/EventTranslatorVararg.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.event;\n\n/**\n * Event初始化接口，生产者通过实现这个接口，在发布Event时，对应实现的translateTo方法会被调用\n * 这里用户可以传多个参数\n *\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/29\n */\npublic interface EventTranslatorVararg<T> {\n     void translateTo(final T event, long sequence, final Object... args);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/sequence/Sequence.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.sequence;\n\n\nimport io.mycat.memory.unsafe.Platform;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/23\n */\nclass LhsPadding\n{\n    protected long p1, p2, p3, p4, p5, p6, p7;\n}\n\nclass Value extends LhsPadding\n{\n    protected volatile long value;\n}\n\nclass RhsPadding extends Value\n{\n    protected long p9, p10, p11, p12, p13, p14, p15;\n}\n\n/**\n * <p>Concurrent sequence class used for tracking the progress of\n * the ring buffer and event processors.  Support a number\n * of concurrent operations including CAS and order writes.\n *\n * <p>Also attempts to be more efficient with regards to false\n * sharing by adding padding around the volatile field.\n */\npublic class Sequence extends RhsPadding\n{\n    public static final long INITIAL_VALUE = -1L;\n    private static final long VALUE_OFFSET;\n\n    static\n    {\n        try\n        {\n            VALUE_OFFSET = Platform.objectFieldOffset(Value.class.getDeclaredField(\"value\"));\n        }\n        catch (final Exception e)\n        {\n            throw new RuntimeException(e);\n        }\n    }\n\n    /**\n     * Create a sequence initialised to -1.\n     */\n    public Sequence()\n    {\n        this(INITIAL_VALUE);\n    }\n\n    /**\n     * Create a sequence with a specified initial value.\n     *\n     * @param initialValue The initial value for this sequence.\n     */\n    public Sequence(final long initialValue)\n    {\n        Platform.putOrderedLong(this, VALUE_OFFSET, initialValue);\n    }\n\n    /**\n     * Perform a volatile read of this sequence's value.\n     *\n     * @return The current value of the sequence.\n     */\n    public long get()\n    {\n        return value;\n    }\n\n    /**\n     * Perform an ordered write of this sequence.  The intent is\n     * a Store/Store barrier between this write and any previous\n     * store.\n     *\n     * @param value The new value for the sequence.\n     */\n    public void set(final long value)\n    {\n        Platform.putOrderedLong(this, VALUE_OFFSET, value);\n    }\n\n    /**\n     * Performs a volatile write of this sequence.  The intent is\n     * a Store/Store barrier between this write and any previous\n     * write and a Store/Load barrier between this write and any\n     * subsequent volatile read.\n     *\n     * @param value The new value for the sequence.\n     */\n    public void setVolatile(final long value)\n    {\n        Platform.putLongVolatile(this, VALUE_OFFSET, value);\n    }\n\n    /**\n     * Perform a compare and set operation on the sequence.\n     *\n     * @param expectedValue The expected current value.\n     * @param newValue The value to update to.\n     * @return true if the operation succeeds, false otherwise.\n     */\n    public boolean compareAndSet(final long expectedValue, final long newValue)\n    {\n        return Platform.compareAndSwapLong(this, VALUE_OFFSET, expectedValue, newValue);\n    }\n\n    /**\n     * Atomically increment the sequence by one.\n     *\n     * @return The value after the increment\n     */\n    public long incrementAndGet()\n    {\n        return addAndGet(1L);\n    }\n\n    /**\n     * Atomically add the supplied value.\n     *\n     * @param increment The value to add to the sequence.\n     * @return The value after the increment.\n     */\n    public long addAndGet(final long increment)\n    {\n        long currentValue;\n        long newValue;\n\n        do\n        {\n            currentValue = get();\n            newValue = currentValue + increment;\n        }\n        while (!compareAndSet(currentValue, newValue));\n\n        return newValue;\n    }\n\n    @Override\n    public String toString()\n    {\n        return Long.toString(get());\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/sequence/SequenceGroups.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.sequence;\n\nimport io.mycat.memory.unsafe.ringbuffer.common.Cursored;\n\nimport java.util.concurrent.atomic.AtomicReferenceFieldUpdater;\n\nimport static java.util.Arrays.copyOf;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/25\n */\npublic class SequenceGroups {\n    /**\n     * 原子添加sequences\n     *\n     * @param holder 原子更新的域所属的类对象\n     * @param updater 原子更新的域对象\n     * @param cursor 定位\n     * @param sequencesToAdd 要添加的sequences\n     * @param <T>\n     */\n    public static <T> void addSequences(\n            final T holder,\n            final AtomicReferenceFieldUpdater<T, Sequence[]> updater,\n            final Cursored cursor,\n            final Sequence... sequencesToAdd)\n    {\n        long cursorSequence;\n        Sequence[] updatedSequences;\n        Sequence[] currentSequences;\n        //在更新成功之前，一直重新读取currentSequences，扩充为添加所有sequence之后的updatedSequences\n        do\n        {\n            currentSequences = updater.get(holder);\n            updatedSequences = copyOf(currentSequences, currentSequences.length + sequencesToAdd.length);\n            cursorSequence = cursor.getCursor();\n\n            int index = currentSequences.length;\n            //将新的sequences的值设置为cursorSequence\n            for (Sequence sequence : sequencesToAdd)\n            {\n                sequence.set(cursorSequence);\n                updatedSequences[index++] = sequence;\n            }\n        }\n        while (!updater.compareAndSet(holder, currentSequences, updatedSequences));\n\n        cursorSequence = cursor.getCursor();\n        for (Sequence sequence : sequencesToAdd)\n        {\n            sequence.set(cursorSequence);\n        }\n    }\n\n    /**\n     * 原子移除某个指定的sequence\n     *\n     * @param holder 原子更新的域所属的类对象\n     * @param sequenceUpdater 原子更新的域对象\n     * @param sequence 要移除的sequence\n     * @param <T>\n     * @return\n     */\n    public static <T> boolean removeSequence(\n            final T holder,\n            final AtomicReferenceFieldUpdater<T, Sequence[]> sequenceUpdater,\n            final Sequence sequence)\n    {\n        int numToRemove;\n        Sequence[] oldSequences;\n        Sequence[] newSequences;\n\n        do\n        {\n            oldSequences = sequenceUpdater.get(holder);\n\n            numToRemove = countMatching(oldSequences, sequence);\n\n            if (0 == numToRemove)\n            {\n                break;\n            }\n\n            final int oldSize = oldSequences.length;\n            newSequences = new Sequence[oldSize - numToRemove];\n\n            for (int i = 0, pos = 0; i < oldSize; i++)\n            {\n                final Sequence testSequence = oldSequences[i];\n                if (sequence != testSequence)\n                {\n                    newSequences[pos++] = testSequence;\n                }\n            }\n        }\n        while (!sequenceUpdater.compareAndSet(holder, oldSequences, newSequences));\n\n        return numToRemove != 0;\n    }\n\n    private static <T> int countMatching(T[] values, final T toMatch)\n    {\n        int numToRemove = 0;\n        for (T value : values)\n        {\n            if (value == toMatch) // Specifically uses identity\n            {\n                numToRemove++;\n            }\n        }\n        return numToRemove;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/waitStrategy/WaitStrategy.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.waitStrategy;\n\nimport io.mycat.memory.unsafe.ringbuffer.common.barrier.SequenceBarrier;\nimport io.mycat.memory.unsafe.ringbuffer.common.sequence.Sequence;\nimport io.mycat.memory.unsafe.ringbuffer.exception.AlertException;\nimport io.mycat.memory.unsafe.ringbuffer.exception.TimeoutException;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/24\n */\npublic interface WaitStrategy {\n    /**\n     * @param sequence 需要等待available的sequence\n     * @param cursor 对应RingBuffer的Cursor\n     * @param dependentSequence 需要等待（依赖）的Sequence\n     * @param barrier 多消费者注册的SequenceBarrier\n     * @return 已经available的sequence\n     * @throws AlertException\n     * @throws InterruptedException\n     * @throws TimeoutException\n     */\n    long waitFor(long sequence, Sequence cursor, Sequence dependentSequence, SequenceBarrier barrier)\n            throws AlertException, InterruptedException, TimeoutException;\n\n    /**\n     * 唤醒所有等待的消费者\n     */\n    void signalAllWhenBlocking();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/waitStrategy/impl/BlockingWaitStrategy.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.waitStrategy.impl;\n\nimport io.mycat.memory.unsafe.ringbuffer.common.barrier.SequenceBarrier;\nimport io.mycat.memory.unsafe.ringbuffer.common.sequence.Sequence;\nimport io.mycat.memory.unsafe.ringbuffer.common.waitStrategy.WaitStrategy;\nimport io.mycat.memory.unsafe.ringbuffer.exception.AlertException;\nimport io.mycat.memory.unsafe.ringbuffer.exception.TimeoutException;\n\nimport java.util.concurrent.locks.Condition;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/8/1\n */\npublic class BlockingWaitStrategy implements WaitStrategy {\n    private final Lock lock = new ReentrantLock();\n    private final Condition processorNotifyCondition = lock.newCondition();\n\n    @Override\n    public long waitFor(long sequence, Sequence cursorSequence, Sequence dependentSequence, SequenceBarrier barrier)\n            throws AlertException, InterruptedException {\n        long availableSequence;\n        if (cursorSequence.get() < sequence) {\n            lock.lock();\n            try {\n                while (cursorSequence.get() < sequence) {\n                    //检查是否Alert，如果Alert，则抛出AlertException\n                    //Alert在这里代表对应的消费者被halt停止了\n                    barrier.checkAlert();\n                    //在processorNotifyCondition上等待唤醒\n                    processorNotifyCondition.await();\n                }\n            } finally {\n                lock.unlock();\n            }\n        }\n\n        while ((availableSequence = dependentSequence.get()) < sequence) {\n            barrier.checkAlert();\n        }\n\n        return availableSequence;\n    }\n\n    @Override\n    public void signalAllWhenBlocking() {\n        lock.lock();\n        try {\n            //生产者生产消息后，会唤醒所有等待的消费者线程\n            processorNotifyCondition.signalAll();\n        } finally {\n            lock.unlock();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/waitStrategy/impl/BusySpinWaitStrategy.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.waitStrategy.impl;\n\nimport io.mycat.memory.unsafe.ringbuffer.common.barrier.SequenceBarrier;\nimport io.mycat.memory.unsafe.ringbuffer.common.sequence.Sequence;\nimport io.mycat.memory.unsafe.ringbuffer.common.waitStrategy.WaitStrategy;\nimport io.mycat.memory.unsafe.ringbuffer.exception.AlertException;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/8/1\n */\npublic class BusySpinWaitStrategy implements WaitStrategy {\n    @Override\n    public long waitFor(\n            final long sequence, Sequence cursor, final Sequence dependentSequence, final SequenceBarrier barrier)\n            throws AlertException, InterruptedException {\n\n        long availableSequence;\n        //一直while自旋检查\n        while ((availableSequence = dependentSequence.get()) < sequence) {\n            barrier.checkAlert();\n        }\n        return availableSequence;\n    }\n\n    @Override\n    public void signalAllWhenBlocking() {\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/common/waitStrategy/impl/SleepingWaitStrategy.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.common.waitStrategy.impl;\n\nimport io.mycat.memory.unsafe.ringbuffer.common.barrier.SequenceBarrier;\nimport io.mycat.memory.unsafe.ringbuffer.common.sequence.Sequence;\nimport io.mycat.memory.unsafe.ringbuffer.common.waitStrategy.WaitStrategy;\nimport io.mycat.memory.unsafe.ringbuffer.exception.AlertException;\n\nimport java.util.concurrent.locks.LockSupport;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/8/1\n */\npublic class SleepingWaitStrategy implements WaitStrategy {\n    //重试200次\n    private static final int DEFAULT_RETRIES = 200;\n    private final int retries;\n\n    public SleepingWaitStrategy() {\n        this(DEFAULT_RETRIES);\n    }\n\n    public SleepingWaitStrategy(int retries) {\n        this.retries = retries;\n    }\n\n    @Override\n    public long waitFor(\n            final long sequence, Sequence cursor, final Sequence dependentSequence, final SequenceBarrier barrier)\n            throws AlertException, InterruptedException {\n        long availableSequence;\n        int counter = retries;\n        //直接检查dependentSequence.get() < sequence\n        while ((availableSequence = dependentSequence.get()) < sequence) {\n            counter = applyWaitMethod(barrier, counter);\n        }\n\n        return availableSequence;\n    }\n\n    @Override\n    public void signalAllWhenBlocking() {\n    }\n\n    private int applyWaitMethod(final SequenceBarrier barrier, int counter)\n            throws AlertException {\n        //检查是否需要终止\n        barrier.checkAlert();\n        //如果在200~100,重试\n        if (counter > 100) {\n            --counter;\n        }\n        //如果在100~0,调用Thread.yield()让出CPU\n        else if (counter > 0) {\n            --counter;\n            Thread.yield();\n        }\n        //<0的话，利用LockSupport.parkNanos(1L)来sleep最小时间\n        else {\n            LockSupport.parkNanos(1L);\n        }\n        return counter;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/exception/AlertException.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.exception;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/24\n */\npublic class AlertException extends Exception {\n\n    public static final AlertException INSTANCE = new AlertException();\n\n    private AlertException()\n    {\n    }\n\n    @Override\n    public Throwable fillInStackTrace()\n    {\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/exception/InsufficientCapacityException.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.exception;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/23\n */\n@SuppressWarnings(\"serial\")\npublic final class InsufficientCapacityException extends Exception {\n    public static final InsufficientCapacityException INSTANCE = new InsufficientCapacityException();\n\n    private InsufficientCapacityException() {\n    }\n\n    @Override\n    public synchronized Throwable fillInStackTrace() {\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/exception/TimeoutException.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.exception;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/24\n */\npublic class TimeoutException extends Exception {\n    public static final TimeoutException INSTANCE = new TimeoutException();\n\n    private TimeoutException()\n    {\n    }\n\n    @Override\n    public synchronized Throwable fillInStackTrace()\n    {\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/producer/AbstractSequencer.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.producer;\n\nimport io.mycat.memory.unsafe.ringbuffer.common.sequence.Sequence;\nimport io.mycat.memory.unsafe.ringbuffer.common.barrier.SequenceBarrier;\nimport io.mycat.memory.unsafe.ringbuffer.common.sequence.SequenceGroups;\nimport io.mycat.memory.unsafe.ringbuffer.common.waitStrategy.WaitStrategy;\nimport io.mycat.memory.unsafe.ringbuffer.exception.InsufficientCapacityException;\nimport io.mycat.memory.unsafe.ringbuffer.utils.Util;\n\nimport java.util.concurrent.atomic.AtomicReferenceFieldUpdater;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/24\n */\npublic abstract class AbstractSequencer implements Sequencer {\n\n    private static final AtomicReferenceFieldUpdater<AbstractSequencer, Sequence[]> SEQUENCE_UPDATER =\n            AtomicReferenceFieldUpdater.newUpdater(AbstractSequencer.class, Sequence[].class, \"gatingSequences\");\n\n    protected final int bufferSize;\n    protected final WaitStrategy waitStrategy;\n    protected final Sequence cursor = new Sequence(Sequencer.INITIAL_CURSOR_VALUE);\n    protected volatile Sequence[] gatingSequences = new Sequence[0];\n\n    public AbstractSequencer(int bufferSize, WaitStrategy waitStrategy)\n    {\n        if (bufferSize < 1)\n        {\n            throw new IllegalArgumentException(\"bufferSize must not be less than 1\");\n        }\n        if (Integer.bitCount(bufferSize) != 1)\n        {\n            throw new IllegalArgumentException(\"bufferSize must be a power of 2\");\n        }\n\n        this.bufferSize = bufferSize;\n        this.waitStrategy = waitStrategy;\n    }\n\n    @Override\n    public final long getCursor()\n    {\n        return cursor.get();\n    }\n\n\n    @Override\n    public final int getBufferSize()\n    {\n        return bufferSize;\n    }\n\n    @Override\n    public void addGatingSequences(Sequence... gatingSequences) {\n        SequenceGroups.addSequences(this, SEQUENCE_UPDATER, this, gatingSequences);\n    }\n\n    @Override\n    public boolean removeGatingSequence(Sequence sequence) {\n        return SequenceGroups.removeSequence(this, SEQUENCE_UPDATER, sequence);\n    }\n\n    @Override\n    public long getMinimumSequence() {\n        return Util.getMinimumSequence(gatingSequences, cursor.get());\n    }\n\n    public SequenceBarrier newBarrier(Sequence... sequencesToTrack)\n    {\n        return null;\n        //TODO 完成SequenceBarrier\n//        return new ProcessingSequenceBarrier(this, waitStrategy, cursor, sequencesToTrack);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/producer/MultiProducerSequencer.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.producer;\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.ringbuffer.common.sequence.Sequence;\nimport io.mycat.memory.unsafe.ringbuffer.common.waitStrategy.WaitStrategy;\nimport io.mycat.memory.unsafe.ringbuffer.exception.InsufficientCapacityException;\nimport io.mycat.memory.unsafe.ringbuffer.utils.Util;\n\nimport java.util.concurrent.locks.LockSupport;\n\n/**\n * 多生产者类，线程安全，与单一生产者不同的是，这里的cursor不再是可以消费的标记，而是多线程生产者抢占的标记\n * 可以消费的sequence由availableBuffer来判断标识\n *\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/24\n */\npublic class MultiProducerSequencer extends AbstractSequencer{\n    private static final long BASE = Platform.arrayBaseOffset(int[].class);\n    private static final long SCALE = Platform.arrayIndexScale(int[].class);\n\n    private final Sequence gatingSequenceCache = new Sequence(Sequencer.INITIAL_CURSOR_VALUE);\n\n    private final int[] availableBuffer;\n    //利用对2^n取模 = 对2^n -1 取与运算原理，indexMask=bufferSize - 1\n    private final int indexMask;\n    //就是上面的n，用来定位某个sequence到底转了多少圈，用来标识已被发布的sequence。\n    //为什么不直接将sequence存入availableBuffer，因为这样sequence值会过大，很容易溢出\n    private final int indexShift;\n\n    public MultiProducerSequencer(int bufferSize, final WaitStrategy waitStrategy)\n    {\n        super(bufferSize, waitStrategy);\n        availableBuffer = new int[bufferSize];\n        indexMask = bufferSize - 1;\n        indexShift = Util.log2(bufferSize);\n        initialiseAvailableBuffer();\n    }\n\n    /**\n     * 将availableBuffer都初始化为-1\n     */\n    private void initialiseAvailableBuffer() {\n        for (int i = availableBuffer.length - 1; i != 0; i--) {\n            setAvailableBufferValue(i, -1);\n        }\n        setAvailableBufferValue(0, -1);\n    }\n\n    /**\n     * 发布某个sequence之前的都可以被消费了需要将availableBuffer上对应sequence下标的值设置为第几次用到这个槽\n     * @param sequence\n     */\n    private void setAvailable(final long sequence) {\n        setAvailableBufferValue(calculateIndex(sequence), calculateAvailabilityFlag(sequence));\n    }\n\n    /**\n     * 某个sequence右移indexShift，代表这个Sequence是第几次用到这个ringBuffer的某个槽，也就是这个sequence转了多少圈\n     * @param sequence\n     * @return\n     */\n    private int calculateAvailabilityFlag(final long sequence) {\n        return (int) (sequence >>> indexShift);\n    }\n\n    /**\n     * 定位ringBuffer上某个槽用于生产event，对2^n取模 = 对2^n -1\n     * @param sequence\n     * @return\n     */\n    private int calculateIndex(final long sequence) {\n        return ((int) sequence) & indexMask;\n    }\n\n    /**\n     * 通过Unsafe更新数组非volatile类型的值\n     * 数组结构\n     * --------------\n     * *   数组头   * BASE\n     * * reference1 * SCALE\n     * * reference2 * SCALE\n     * * reference3 * SCALE\n     * --------------\n     * @param index\n     * @param flag\n     */\n    private void setAvailableBufferValue(int index, int flag) {\n        long bufferAddress = (index * SCALE) + BASE;\n        Platform.putOrderedInt(availableBuffer, bufferAddress, flag);\n    }\n\n    @Override\n    public void claim(long sequence) {\n        cursor.set(sequence);\n    }\n\n    /**\n     * 用同样的方法计算给定的sequence，判断与availableBuffer对应下标的值是否相等，如果相等证明已被发布可以消费\n     * @param sequence of the buffer to check\n     * @return\n     */\n    @Override\n    public boolean isAvailable(long sequence) {\n        int index = calculateIndex(sequence);\n        int flag = calculateAvailabilityFlag(sequence);\n        long bufferAddress = (index * SCALE) + BASE;\n        return Platform.getIntVolatile(availableBuffer, bufferAddress) == flag;\n    }\n\n    /**\n     * 获取最高的可消费sequence，减少获取次数\n     * @param nextSequence      The sequence to start scanning from.\n     * @param availableSequence The sequence to scan to.\n     * @return\n     */\n    @Override\n    public long getHighestPublishedSequence(long nextSequence, long availableSequence) {\n        for (long sequence = nextSequence; sequence <= availableSequence; sequence++) {\n            if (!isAvailable(sequence)) {\n                return sequence - 1;\n            }\n        }\n        return availableSequence;\n    }\n\n    @Override\n    public boolean hasAvailableCapacity(final int requiredCapacity) {\n        return hasAvailableCapacity(gatingSequences, requiredCapacity, cursor.get());\n    }\n\n    /**\n     * 与单一生产者验证原理类似\n     * @param gatingSequences\n     * @param requiredCapacity\n     * @param cursorValue\n     * @return\n     */\n    private boolean hasAvailableCapacity(Sequence[] gatingSequences, final int requiredCapacity, long cursorValue)\n    {\n        //下一位置加上所需容量减去整个bufferSize，如果为正数，那证明至少转了一圈，则需要检查gatingSequences（由消费者更新里面的Sequence值）以保证不覆盖还未被消费的\n        //由于最多只能生产不大于整个bufferSize的Events。所以减去一个bufferSize与最小sequence相比较即可\n        long wrapPoint = (cursorValue + requiredCapacity) - bufferSize;\n        //缓存\n        long cachedGatingSequence = gatingSequenceCache.get();\n        //缓存失效条件\n        if (wrapPoint > cachedGatingSequence || cachedGatingSequence > cursorValue)\n        {\n            long minSequence = Util.getMinimumSequence(gatingSequences, cursorValue);\n            gatingSequenceCache.set(minSequence);\n            //空间不足\n            if (wrapPoint > minSequence)\n            {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public long remainingCapacity() {\n        //与单一生产者的计算方法同理，不考虑并发\n        long consumed = Util.getMinimumSequence(gatingSequences, cursor.get());\n        long produced = cursor.get();\n        return getBufferSize() - (produced - consumed);\n    }\n\n    @Override\n    public long next() {\n        return next(1);\n    }\n\n    /**\n     * 用于多个生产者抢占n个RingBuffer槽用于生产Event\n     *\n     * @param n\n     * @return\n     */\n    @Override\n    public long next(int n) {\n        if (n < 1) {\n            throw new IllegalArgumentException(\"n must be > 0\");\n        }\n\n        long current;\n        long next;\n\n        do {\n            //首先通过缓存判断空间是否足够\n            current = cursor.get();\n            next = current + n;\n\n            long wrapPoint = next - bufferSize;\n            long cachedGatingSequence = gatingSequenceCache.get();\n            //如果缓存不满足\n            if (wrapPoint > cachedGatingSequence || cachedGatingSequence > current) {\n                //重新获取最小的\n                long gatingSequence = Util.getMinimumSequence(gatingSequences, current);\n                //如果空间不足，则唤醒消费者消费，并让出CPU\n                if (wrapPoint > gatingSequence) {\n                    waitStrategy.signalAllWhenBlocking();\n                    LockSupport.parkNanos(1); // TODO, should we spin based on the wait strategy?\n                    continue;\n                }\n                //重新设置缓存\n                gatingSequenceCache.set(gatingSequence);\n            } //如果空间足够，尝试CAS更新cursor，更新cursor成功代表成功获取n个槽，退出死循环\n            else if (cursor.compareAndSet(current, next)) {\n                break;\n            }\n        }\n        while (true);\n        //返回最新的cursor值\n        return next;\n    }\n\n    @Override\n    public long tryNext() throws InsufficientCapacityException {\n        return tryNext(1);\n    }\n\n    @Override\n    public long tryNext(int n) throws InsufficientCapacityException {\n        if (n < 1) {\n            throw new IllegalArgumentException(\"n must be > 0\");\n        }\n\n        long current;\n        long next;\n        //尝试获取一次，若不成功，则抛InsufficientCapacityException\n        do {\n            current = cursor.get();\n            next = current + n;\n\n            if (!hasAvailableCapacity(gatingSequences, n, current)) {\n                throw InsufficientCapacityException.INSTANCE;\n            }\n        }\n        while (!cursor.compareAndSet(current, next));\n\n        return next;\n    }\n\n    @Override\n    public void publish(long sequence) {\n        setAvailable(sequence);\n        waitStrategy.signalAllWhenBlocking();\n    }\n\n    @Override\n    public void publish(long lo, long hi) {\n        for (long l = lo; l <= hi; l++) {\n            setAvailable(l);\n        }\n        waitStrategy.signalAllWhenBlocking();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/producer/Sequencer.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.producer;\n\nimport io.mycat.memory.unsafe.ringbuffer.common.Cursored;\nimport io.mycat.memory.unsafe.ringbuffer.common.sequence.Sequence;\nimport io.mycat.memory.unsafe.ringbuffer.common.Sequenced;\nimport io.mycat.memory.unsafe.ringbuffer.common.barrier.SequenceBarrier;\n\n/**\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/23\n */\npublic interface Sequencer extends Cursored,Sequenced{\n    /**\n     * -1 为 sequence的起始值\n     */\n    long INITIAL_CURSOR_VALUE = -1L;\n\n    /**\n     * 申请一个特殊的Sequence，只有设定特殊起始值的ringBuffer时才会使用\n     *\n     * @param sequence The sequence to initialise too.\n     */\n    void claim(long sequence);\n\n    /**\n     * 非阻塞，验证一个sequence是否已经被published并且可以消费\n     *\n     * @param sequence of the buffer to check\n     * @return true if the sequence is available for use, false if not\n     */\n    boolean isAvailable(long sequence);\n\n    /**\n     * 将这些sequence加入到需要跟踪处理的gatingSequences中\n     *\n     * @param gatingSequences The sequences to add.\n     */\n    void addGatingSequences(Sequence... gatingSequences);\n\n    /**\n     * 移除某个sequence\n     *\n     * @param sequence to be removed.\n     * @return <tt>true</tt> if this sequence was found, <tt>false</tt> otherwise.\n     */\n    boolean removeGatingSequence(Sequence sequence);\n\n    /**\n     * 给定一串需要跟踪的sequence，创建SequenceBarrier\n     * SequenceBarrier是用来给多消费者确定消费位置是否可以消费用的\n     *\n     * @param sequencesToTrack\n     * @return A sequence barrier that will track the specified sequences.\n     * @see SequenceBarrier\n     */\n    SequenceBarrier newBarrier(Sequence... sequencesToTrack);\n\n    /**\n     * 获取这个ringBuffer的gatingSequences中最小的一个sequence\n     *\n     * @return The minimum gating sequence or the cursor sequence if\n     */\n    long getMinimumSequence();\n\n    /**\n     * 获取最高可以读取的Sequence\n     *\n     * @param nextSequence      The sequence to start scanning from.\n     * @param availableSequence The sequence to scan to.\n     * @return The highest value that can be safely read, will be at least <code>nextSequence - 1</code>.\n     */\n    long getHighestPublishedSequence(long nextSequence, long availableSequence);\n    /**\n     * 并没有什么用，不实现，注释掉\n     */\n//    <T> EventPoller<T> newPoller(DataProvider<T> provider, Sequence... gatingSequences);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/producer/SingleProducerSequencer.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.producer;\n\nimport io.mycat.memory.unsafe.ringbuffer.common.sequence.Sequence;\nimport io.mycat.memory.unsafe.ringbuffer.common.waitStrategy.WaitStrategy;\nimport io.mycat.memory.unsafe.ringbuffer.exception.InsufficientCapacityException;\nimport io.mycat.memory.unsafe.ringbuffer.utils.Util;\n\nimport java.util.concurrent.locks.LockSupport;\n\n/**\n * 单一生产者相关类，非线程安全\n *\n * @author lmax.Disruptor\n * @version 3.3.5\n * @date 2016/7/24\n */\n\nabstract class SingleProducerSequencerPad extends AbstractSequencer\n{\n    protected long p1, p2, p3, p4, p5, p6, p7;\n\n    public SingleProducerSequencerPad(int bufferSize, WaitStrategy waitStrategy)\n    {\n        super(bufferSize, waitStrategy);\n    }\n}\n\nabstract class SingleProducerSequencerFields extends SingleProducerSequencerPad\n{\n    public SingleProducerSequencerFields(int bufferSize, WaitStrategy waitStrategy)\n    {\n        super(bufferSize, waitStrategy);\n    }\n\n    protected long nextValue = Sequence.INITIAL_VALUE;\n    protected long cachedValue = Sequence.INITIAL_VALUE;\n}\n\npublic class SingleProducerSequencer extends SingleProducerSequencerFields{\n\n    public SingleProducerSequencer(int bufferSize, final WaitStrategy waitStrategy) {\n        super(bufferSize, waitStrategy);\n    }\n\n    @Override\n    public void claim(long sequence) {\n        nextValue = sequence;\n    }\n\n    @Override\n    public boolean isAvailable(long sequence) {\n        return sequence <= cursor.get();\n    }\n\n    @Override\n    public long getHighestPublishedSequence(long nextSequence, long availableSequence) {\n        return availableSequence;\n    }\n\n    @Override\n    public boolean hasAvailableCapacity(int requiredCapacity) {\n        //下一个生产Sequence位置\n        long nextValue = this.nextValue;\n        //下一位置加上所需容量减去整个bufferSize，如果为正数，那证明至少转了一圈，则需要检查gatingSequences（由消费者更新里面的Sequence值）以保证不覆盖还未被消费的\n        long wrapPoint = (nextValue + requiredCapacity) - bufferSize;\n        //Disruptor经常用缓存，这里缓存之间所有gatingSequences最小的那个，这样不用每次都遍历一遍gatingSequences，影响效率\n        long cachedGatingSequence = this.cachedValue;\n        //只要wrapPoint大于缓存的所有gatingSequences最小的那个，就重新检查更新缓存\n        if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue)\n        {\n            long minSequence = Util.getMinimumSequence(gatingSequences, nextValue);\n            this.cachedValue = minSequence;\n            //空间不足返回false\n            if (wrapPoint > minSequence)\n            {\n                return false;\n            }\n        }\n        //若wrapPoint小于缓存的所有gatingSequences最小的那个，证明可以放心生产\n        return true;\n    }\n\n    @Override\n    public long remainingCapacity() {\n        //使用的 = 生产的 - 已经消费的\n        //剩余容量 = 容量 - 使用的\n        long nextValue = this.nextValue;\n        long consumed = Util.getMinimumSequence(gatingSequences, nextValue);\n        long produced = nextValue;\n        return getBufferSize() - (produced - consumed);\n    }\n\n    @Override\n    public long next() {\n        return next(1);\n    }\n\n    @Override\n    public long next(int n) {\n        if (n < 1) {\n            throw new IllegalArgumentException(\"n must be > 0\");\n        }\n\n        long nextValue = this.nextValue;\n        //next方法和之前的hasAvailableCapacity同理，只不过这里是相当于阻塞的\n        long nextSequence = nextValue + n;\n        long wrapPoint = nextSequence - bufferSize;\n        long cachedGatingSequence = this.cachedValue;\n\n        if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue) {\n            long minSequence;\n            //只要wrapPoint大于最小的gatingSequences，那么不断唤醒消费者去消费，并利用LockSupport让出CPU，直到wrapPoint不大于最小的gatingSequences\n            while (wrapPoint > (minSequence = Util.getMinimumSequence(gatingSequences, nextValue))) {\n                waitStrategy.signalAllWhenBlocking();\n                LockSupport.parkNanos(1L); // TODO: Use waitStrategy to spin?\n            }\n            //同理，缓存最小的gatingSequences\n            this.cachedValue = minSequence;\n        }\n\n        this.nextValue = nextSequence;\n\n        return nextSequence;\n    }\n\n    @Override\n    public long tryNext() throws InsufficientCapacityException {\n        return tryNext(1);\n    }\n\n    @Override\n    public long tryNext(int n) throws InsufficientCapacityException {\n        if (n < 1) {\n            throw new IllegalArgumentException(\"n must be > 0\");\n        }\n\n        if (!hasAvailableCapacity(n)) {\n            throw InsufficientCapacityException.INSTANCE;\n        }\n\n        long nextSequence = this.nextValue += n;\n\n        return nextSequence;\n    }\n\n    @Override\n    public void publish(long sequence) {\n        //cursor代表可以消费的sequence\n        cursor.set(sequence);\n        waitStrategy.signalAllWhenBlocking();\n    }\n\n    @Override\n    public void publish(long lo, long hi) {\n        publish(hi);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/ringbuffer/utils/Util.java",
    "content": "package io.mycat.memory.unsafe.ringbuffer.utils;\n\nimport io.mycat.memory.unsafe.ringbuffer.common.sequence.Sequence;\n\n/**\n * Set of common functions used by the Disruptor\n */\npublic class Util {\n    /**\n     * 计算下一个不小于x的2的n次方\n     * 原理：int最长为32位，计算x-1的前面有多少个0，之后用32减去这个值得到n，那么2的n次方就是下一个不小于x的2的n次方\n     *\n     * @param x Value to round up\n     * @return The next power of 2 from x inclusive\n     */\n    public static int ceilingNextPowerOfTwo(final int x) {\n        return 1 << (32 - Integer.numberOfLeadingZeros(x - 1));\n    }\n\n    /**\n     * 获取Sequence数组中value最小的值\n     *\n     * @param sequences to compare.\n     * @return the minimum sequence found or Long.MAX_VALUE if the array is empty.\n     */\n    public static long getMinimumSequence(final Sequence[] sequences)\n    {\n        return getMinimumSequence(sequences, Long.MAX_VALUE);\n    }\n\n    /**\n     * 获取Sequence数组中value最小的值\n     *\n     * @param sequences to compare.\n     * @param minimum   如果数组为空，将返回这个值\n     * @return the smaller of minimum sequence value found in {@code sequences} and {@code minimum};\n     * {@code minimum} if {@code sequences} is empty\n     */\n    public static long getMinimumSequence(final Sequence[] sequences, long minimum)\n    {\n        for (int i = 0, n = sequences.length; i < n; i++)\n        {\n            long value = sequences[i].get();\n            minimum = Math.min(minimum, value);\n        }\n\n        return minimum;\n    }\n\n    public static int log2(int i)\n    {\n        int r = 0;\n        while ((i >>= 1) != 0)\n        {\n            ++r;\n        }\n        return r;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/row/BufferHolder.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.row;\n\n\nimport io.mycat.memory.unsafe.Platform;\n\n/**\n * A helper class to manage the data buffer for an unsafe row.  The data buffer can grow and\n * automatically re-point the unsafe row to it.\n *\n * This class can be used to build a one-pass unsafe row writing program, i.e. data will be written\n * to the data buffer directly and no extra copy is needed.  There should be only one instance of\n * this class per writing program, so that the memory segment/data buffer can be reused.  Note that\n * for each incoming record, we should call `reset` of BufferHolder instance before write the record\n * and reuse the data buffer.\n *\n * Generally we should call `UnsafeRow.setTotalSize` and pass in `BufferHolder.totalSize` to update\n * the size of the result row, after writing a record to the buffer. However, we can skip this step\n * if the fields of row are all fixed-length, as the size of result row is also fixed.\n */\npublic class BufferHolder {\n  public byte[] buffer;\n  public int cursor = Platform.BYTE_ARRAY_OFFSET;\n\n\n  private final UnsafeRow row;\n  private final int fixedSize;\n\n  public BufferHolder(UnsafeRow row) {\n    this(row, 64);\n  }\n\n  public BufferHolder(UnsafeRow row, int initialSize) {\n    this.fixedSize = UnsafeRow.calculateBitSetWidthInBytes(row.numFields()) + 8 * row.numFields();\n    this.buffer = new byte[fixedSize + initialSize];\n    this.row = row;\n    this.row.pointTo(buffer, buffer.length);\n  }\n\n  /**\n   * Grows the buffer by at least neededSize and points the row to the buffer.\n   */\n  public void grow(int neededSize) {\n    final int length = totalSize() + neededSize;\n    if (buffer.length < length) {\n      // This will not happen frequently, because the buffer is re-used.\n      final byte[] tmp = new byte[length * 2];\n      Platform.copyMemory(\n        buffer,\n        Platform.BYTE_ARRAY_OFFSET,\n        tmp,\n        Platform.BYTE_ARRAY_OFFSET,\n        totalSize());\n      buffer = tmp;\n      row.pointTo(buffer, buffer.length);\n    }\n  }\n\n  public UnsafeRow getRow() {\n    return row;\n  }\n\n\n  public void reset() {\n    cursor = Platform.BYTE_ARRAY_OFFSET + fixedSize;\n  }\n\n  public int totalSize() {\n    return cursor - Platform.BYTE_ARRAY_OFFSET;\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/row/StructType.java",
    "content": "package io.mycat.memory.unsafe.row;\n\nimport io.mycat.sqlengine.mpp.ColMeta;\nimport io.mycat.sqlengine.mpp.OrderCol;\n\nimport javax.annotation.Nonnull;\nimport java.util.Map;\n\n/**\n * Created by zagnix on 2016/6/6.\n */\npublic class StructType {\n\n    private final Map<String, ColMeta> columToIndx;\n    private final int fieldCount;\n\n    private  OrderCol[] orderCols = null;\n\n    public StructType(@Nonnull Map<String,ColMeta> columToIndx,int fieldCount){\n        assert fieldCount >=0;\n        this.columToIndx = columToIndx;\n        this.fieldCount = fieldCount;\n    }\n\n    public int length() {\n        return fieldCount;\n    }\n\n    public Map<String, ColMeta> getColumToIndx() {\n        return columToIndx;\n    }\n\n    public OrderCol[] getOrderCols() {\n        return orderCols;\n    }\n\n    public void setOrderCols(OrderCol[] orderCols) {\n        this.orderCols = orderCols;\n    }\n\n    public long apply(int i) {\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/row/UnsafeRow.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.row;\n\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.array.ByteArrayMethods;\nimport io.mycat.memory.unsafe.bitset.BitSetMethods;\nimport io.mycat.memory.unsafe.hash.Murmur3_x86_32;\nimport io.mycat.memory.unsafe.types.UTF8String;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.net.mysql.MySQLPacket;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.nio.ByteBuffer;\n\n\n/**\n * Modify by zagnix \n * An Unsafe implementation of Row which is backed by raw memory instead of Java objects.\n *\n * Each tuple has three parts: [null bit set] [values] [variable length portion]\n *\n * The bit set is used for null tracking and is aligned to 8-byte word boundaries.  It stores\n * one bit per field.\n *\n * In the `values` region, we store one 8-byte word per field. For fields that hold fixed-length\n * primitive types, such as long, double, or int, we store the value directly in the word. For\n * fields with non-primitive or variable-length values, we store a relative offset (w.r.t. the\n * base address of the row) that points to the beginning of the variable-length field, and length\n * (they are combined into a long).\n *\n * Instances of `UnsafeRow` act as pointers to row data stored in this format.\n */\npublic final class UnsafeRow extends MySQLPacket {\n\n  //////////////////////////////////////////////////////////////////////////////\n  // Static methods\n  //////////////////////////////////////////////////////////////////////////////\n\n  public static int calculateBitSetWidthInBytes(int numFields) {\n    return ((numFields + 63)/ 64) * 8;\n  }\n\n  public static int calculateFixedPortionByteSize(int numFields) {\n    return 8 * numFields + calculateBitSetWidthInBytes(numFields);\n  }\n\n  //////////////////////////////////////////////////////////////////////////////\n  // Private fields and methods\n  //////////////////////////////////////////////////////////////////////////////\n\n  private Object baseObject;\n  private long baseOffset;\n\n  /** The number of fields in this row, used for calculating the bitset width (and in assertions) */\n  private int numFields;\n\n  /** The size of this row's backing data, in bytes) */\n  private int sizeInBytes;\n\n  /** The width of the null tracking bit set, in bytes */\n  private int bitSetWidthInBytes;\n\n  private long getFieldOffset(int ordinal) {\n    return baseOffset + bitSetWidthInBytes + ordinal * 8L;\n  }\n\n  private void assertIndexIsValid(int index) {\n    assert index >= 0 : \"index (\" + index + \") should >= 0\";\n    assert index < numFields : \"index (\" + index + \") should < \" + numFields;\n  }\n\n  //////////////////////////////////////////////////////////////////////////////\n  // Public methods\n  //////////////////////////////////////////////////////////////////////////////\n\n  /**\n   * Construct a new UnsafeRow. The resulting row won't be usable until `pointTo()` has been called,\n   * since the value returned by this constructor is equivalent to a null pointer.\n   *\n   * @param numFields the number of fields in this row\n   */\n  public UnsafeRow(int numFields) {\n    this.numFields = numFields;\n    this.bitSetWidthInBytes = calculateBitSetWidthInBytes(numFields);\n  }\n\n  // for serializer\n  public UnsafeRow() {}\n\n  public Object getBaseObject() { return baseObject; }\n  public long getBaseOffset() { return baseOffset; }\n  public int getSizeInBytes() { return sizeInBytes; }\n\n  public int numFields() { return numFields; }\n\n  /**\n   * Update this UnsafeRow to point to different backing data.\n   *\n   * @param baseObject the base object\n   * @param baseOffset the offset within the base object\n   * @param sizeInBytes the size of this row's backing data, in bytes\n   */\n  public void pointTo(Object baseObject, long baseOffset, int sizeInBytes) {\n    assert numFields >= 0 : \"numFields (\" + numFields + \") should >= 0\";\n    this.baseObject = baseObject;\n    this.baseOffset = baseOffset;\n    this.sizeInBytes = sizeInBytes;\n  }\n\n  /**\n   * Update this UnsafeRow to point to the underlying byte array.\n   *\n   * @param buf byte array to point to\n   * @param sizeInBytes the number of bytes valid in the byte array\n   */\n  public void pointTo(byte[] buf, int sizeInBytes) {\n    pointTo(buf, Platform.BYTE_ARRAY_OFFSET, sizeInBytes);\n  }\n\n  public void setTotalSize(int sizeInBytes) {\n    this.sizeInBytes = sizeInBytes;\n  }\n\n  public void setNotNullAt(int i) {\n    assertIndexIsValid(i);\n    BitSetMethods.unset(baseObject, baseOffset, i);\n  }\n\n\n  public void setNullAt(int i) {\n    assertIndexIsValid(i);\n    BitSetMethods.set(baseObject, baseOffset, i);\n    // To preserve row equality, zero out the value when setting the column to null.\n    // Since this row does does not currently support updates to variable-length values, we don't\n    // have to worry about zeroing out that data.\n    Platform.putLong(baseObject, getFieldOffset(i), 0);\n  }\n\n  public void update(int ordinal, Object value) {\n    throw new UnsupportedOperationException();\n  }\n\n  public void setInt(int ordinal, int value) {\n    assertIndexIsValid(ordinal);\n    setNotNullAt(ordinal);\n    Platform.putInt(baseObject, getFieldOffset(ordinal), value);\n  }\n\n  public void setLong(int ordinal, long value) {\n    assertIndexIsValid(ordinal);\n    setNotNullAt(ordinal);\n    Platform.putLong(baseObject, getFieldOffset(ordinal), value);\n  }\n\n  public void setDouble(int ordinal, double value) {\n    assertIndexIsValid(ordinal);\n    setNotNullAt(ordinal);\n    if (Double.isNaN(value)) {\n      value = Double.NaN;\n    }\n    Platform.putDouble(baseObject, getFieldOffset(ordinal), value);\n  }\n\n  public void setBoolean(int ordinal, boolean value) {\n    assertIndexIsValid(ordinal);\n    setNotNullAt(ordinal);\n    Platform.putBoolean(baseObject, getFieldOffset(ordinal), value);\n  }\n\n  public void setShort(int ordinal, short value) {\n    assertIndexIsValid(ordinal);\n    setNotNullAt(ordinal);\n    Platform.putShort(baseObject, getFieldOffset(ordinal), value);\n  }\n\n  public void setByte(int ordinal, byte value) {\n    assertIndexIsValid(ordinal);\n    setNotNullAt(ordinal);\n    Platform.putByte(baseObject, getFieldOffset(ordinal), value);\n  }\n\n  public void setFloat(int ordinal, float value) {\n    assertIndexIsValid(ordinal);\n    setNotNullAt(ordinal);\n    if (Float.isNaN(value)) {\n      value = Float.NaN;\n    }\n    Platform.putFloat(baseObject, getFieldOffset(ordinal), value);\n  }\n\n\n  public boolean isNullAt(int ordinal) {\n    assertIndexIsValid(ordinal);\n    return BitSetMethods.isSet(baseObject, baseOffset, ordinal);\n  }\n\n\n  public boolean getBoolean(int ordinal) {\n    assertIndexIsValid(ordinal);\n    return Platform.getBoolean(baseObject, getFieldOffset(ordinal));\n  }\n\n\n  public byte getByte(int ordinal) {\n    assertIndexIsValid(ordinal);\n    return Platform.getByte(baseObject, getFieldOffset(ordinal));\n  }\n\n\n  public short getShort(int ordinal) {\n    assertIndexIsValid(ordinal);\n    return Platform.getShort(baseObject, getFieldOffset(ordinal));\n  }\n\n\n  public int getInt(int ordinal) {\n    assertIndexIsValid(ordinal);\n    return Platform.getInt(baseObject, getFieldOffset(ordinal));\n  }\n\n\n  public long getLong(int ordinal) {\n    assertIndexIsValid(ordinal);\n    return Platform.getLong(baseObject, getFieldOffset(ordinal));\n  }\n\n\n  public float getFloat(int ordinal) {\n    assertIndexIsValid(ordinal);\n    return Platform.getFloat(baseObject, getFieldOffset(ordinal));\n  }\n\n\n  public double getDouble(int ordinal) {\n    assertIndexIsValid(ordinal);\n    return Platform.getDouble(baseObject, getFieldOffset(ordinal));\n  }\n\n\n  public UTF8String getUTF8String(int ordinal) {\n    if (isNullAt(ordinal)) return null;\n    final long offsetAndSize = getLong(ordinal);\n    final int offset = (int) (offsetAndSize >> 32);\n    final int size = (int) offsetAndSize;\n    return UTF8String.fromAddress(baseObject, baseOffset + offset, size);\n  }\n  public byte[] getBinary(int ordinal) {\n    if (isNullAt(ordinal)) {\n      return null;\n    } else {\n      final long offsetAndSize = getLong(ordinal);\n      final int offset = (int) (offsetAndSize >> 32);\n      final int size = (int) offsetAndSize;\n      final byte[] bytes = new byte[size];\n      Platform.copyMemory(\n        baseObject,\n        baseOffset + offset,\n        bytes,\n        Platform.BYTE_ARRAY_OFFSET,\n        size\n      );\n      return bytes;\n    }\n  }\n\n\n\n  /**\n   * Copies this row, returning a self-contained UnsafeRow that stores its data in an internal\n   * byte array rather than referencing data stored in a data page.\n   */\n  public UnsafeRow copy() {\n    UnsafeRow rowCopy = new UnsafeRow(numFields);\n    final byte[] rowDataCopy = new byte[sizeInBytes];\n    Platform.copyMemory(\n      baseObject,\n      baseOffset,\n      rowDataCopy,\n      Platform.BYTE_ARRAY_OFFSET,\n      sizeInBytes\n    );\n    rowCopy.pointTo(rowDataCopy, Platform.BYTE_ARRAY_OFFSET, sizeInBytes);\n    return rowCopy;\n  }\n\n  /**\n   * Creates an empty UnsafeRow from a byte array with specified numBytes and numFields.\n   * The returned row is invalid until we call copyFrom on it.\n   */\n  public static UnsafeRow createFromByteArray(int numBytes, int numFields) {\n    final UnsafeRow row = new UnsafeRow(numFields);\n    row.pointTo(new byte[numBytes], numBytes);\n    return row;\n  }\n\n  /**\n   * Copies the input UnsafeRow to this UnsafeRow, and resize the underlying byte[] when the\n   * input row is larger than this row.\n   */\n  public void copyFrom(UnsafeRow row) {\n    // copyFrom is only available for UnsafeRow created from byte array.\n    assert (baseObject instanceof byte[]) && baseOffset == Platform.BYTE_ARRAY_OFFSET;\n    if (row.sizeInBytes > this.sizeInBytes) {\n      // resize the underlying byte[] if it's not large enough.\n      this.baseObject = new byte[row.sizeInBytes];\n    }\n    Platform.copyMemory(\n      row.baseObject, row.baseOffset, this.baseObject, this.baseOffset, row.sizeInBytes);\n    // update the sizeInBytes.\n    this.sizeInBytes = row.sizeInBytes;\n  }\n\n  /**\n   * Write this UnsafeRow's underlying bytes to the given OutputStream.\n   *\n   * @param out the stream to write to.\n   * @param writeBuffer a byte array for buffering chunks of off-heap data while writing to the\n   *                    output stream. If this row is backed by an on-heap byte array, then this\n   *                    buffer will not be used and may be null.\n   */\n  public void writeToStream(OutputStream out, byte[] writeBuffer) throws IOException {\n    if (baseObject instanceof byte[]) {\n      int offsetInByteArray = (int) (Platform.BYTE_ARRAY_OFFSET - baseOffset);\n      out.write((byte[]) baseObject, offsetInByteArray, sizeInBytes);\n    } else {\n      int dataRemaining = sizeInBytes;\n      long rowReadPosition = baseOffset;\n      while (dataRemaining > 0) {\n        int toTransfer = Math.min(writeBuffer.length, dataRemaining);\n        Platform.copyMemory(\n          baseObject, rowReadPosition, writeBuffer, Platform.BYTE_ARRAY_OFFSET, toTransfer);\n        out.write(writeBuffer, 0, toTransfer);\n        rowReadPosition += toTransfer;\n        dataRemaining -= toTransfer;\n      }\n    }\n  }\n\n  @Override\n  public int hashCode() {\n    return Murmur3_x86_32.hashUnsafeWords(baseObject, baseOffset, sizeInBytes, 42);\n  }\n\n  @Override\n  public boolean equals(Object other) {\n    if (other instanceof UnsafeRow) {\n      UnsafeRow o = (UnsafeRow) other;\n      return (sizeInBytes == o.sizeInBytes) &&\n        ByteArrayMethods.arrayEquals(baseObject, baseOffset, o.baseObject, o.baseOffset,\n          sizeInBytes);\n    }\n    return false;\n  }\n\n  /**\n   * Returns the underlying bytes for this UnsafeRow.\n   */\n  public byte[] getBytes() {\n    if (baseObject instanceof byte[] && baseOffset == Platform.BYTE_ARRAY_OFFSET\n      && (((byte[]) baseObject).length == sizeInBytes)) {\n      return (byte[]) baseObject;\n    } else {\n      byte[] bytes = new byte[sizeInBytes];\n      Platform.copyMemory(baseObject, baseOffset, bytes, Platform.BYTE_ARRAY_OFFSET, sizeInBytes);\n      return bytes;\n    }\n  }\n\n  public static final byte NULL_MARK = (byte) 251;\n  public static final byte EMPTY_MARK = (byte) 0;\n\n  @Override\n  public ByteBuffer write(ByteBuffer bb, FrontendConnection c,\n                          boolean writeSocketIfFull) {\n    bb = c.checkWriteBuffer(bb,c.getPacketHeaderSize(),writeSocketIfFull);\n    BufferUtil.writeUB3(bb, calcPacketSize());\n    bb.put(packetId);\n    for (int i = 0; i < numFields; i++) {\n      if (!isNullAt(i)) {\n        byte[] fv = this.getBinary(i);\n        if (fv.length == 0) {\n          bb = c.checkWriteBuffer(bb, 1, writeSocketIfFull);\n          bb.put(UnsafeRow.EMPTY_MARK);\n        } else {\n          bb = c.checkWriteBuffer(bb, BufferUtil.getLength(fv),\n                  writeSocketIfFull);\n          BufferUtil.writeLength(bb, fv.length);\n          /**\n           * 把数据写到Writer Buffer中\n           */\n          bb = c.writeToBuffer(fv, bb);\n        }\n      } else {\n        //Col null value\n        bb = c.checkWriteBuffer(bb,1,writeSocketIfFull);\n        bb.put(UnsafeRow.NULL_MARK);\n      }\n    }\n    return bb;\n  }\n\n  @Override\n  public int calcPacketSize() {\n    int size = 0;\n    for (int i = 0; i < numFields; i++) {\n      byte[] v = this.getBinary(i);\n      size += (v == null || v.length == 0) ? 1 : BufferUtil.getLength(v);\n    }\n    return size;\n  }\n  \n  \tpublic BigDecimal getDecimal(int ordinal, int scale) {\n\t\tif (isNullAt(ordinal)) {\n\t\t\treturn null;\n\t\t}\n\t\tbyte[] bytes = getBinary(ordinal);\n\t\tBigInteger bigInteger = new BigInteger(bytes);\n\t\tBigDecimal javaDecimal = new BigDecimal(bigInteger, scale);\n\t\treturn javaDecimal;\n\t}\n  \t\n  \t/**\n \t * update <strong>exist</strong> decimal column value to new decimal value\n \t * \n \t * NOTE: decimal max precision is limit to 38\n \t * @param ordinal\n \t * @param value\n \t * @param precision\n \t */\n \tpublic void updateDecimal(int ordinal, BigDecimal value) {\n \t\tassertIndexIsValid(ordinal);\n \t\t// fixed length\n \t\tlong cursor = getLong(ordinal) >>> 32;\n \t\tassert cursor > 0 : \"invalid cursor \" + cursor;\n \t\t// zero-out the bytes\n \t\tPlatform.putLong(baseObject, baseOffset + cursor, 0L);\n \t\tPlatform.putLong(baseObject, baseOffset + cursor + 8, 0L);\n \n \t\tif (value == null) {\n \t\t\tsetNullAt(ordinal);\n \t\t\t// keep the offset for future update\n \t\t\tPlatform.putLong(baseObject, getFieldOffset(ordinal), cursor << 32);\n \t\t} else {\n \n \t\t\tfinal BigInteger integer = value.unscaledValue();\n \t\t\tbyte[] bytes = integer.toByteArray();\n \t\t\tassert (bytes.length <= 16);\n \n \t\t\t// Write the bytes to the variable length portion.\n \t\t\tPlatform.copyMemory(bytes, Platform.BYTE_ARRAY_OFFSET, baseObject, baseOffset + cursor, bytes.length);\n \t\t\tsetLong(ordinal, (cursor << 32) | ((long) bytes.length));\n \t\t}\n \n \t}\n\n  /**\n  public Decimal getDecimal(int ordinal, int precision, int scale) {\n    if (isNullAt(ordinal)) {\n      return null;\n    }\n    if (precision <= Decimal.MAX_LONG_DIGITS()) {\n      return Decimal.createUnsafe(getLong(ordinal), precision, scale);\n    } else {\n      byte[] bytes = getBinary(ordinal);\n      BigInteger bigInteger = new BigInteger(bytes);\n      BigDecimal javaDecimal = new BigDecimal(bigInteger, scale);\n      return Decimal.apply(javaDecimal, precision, scale);\n    }\n  }\n\n  public void setDecimal(int ordinal, Decimal value, int precision) {\n    assertIndexIsValid(ordinal);\n    if (precision <= Decimal.MAX_LONG_DIGITS()) {\n      // compact format\n      if (value == null) {\n        setNullAt(ordinal);\n      } else {\n        setLong(ordinal, value.toUnscaledLong());\n      }\n    } else {\n      // fixed length\n      long cursor = getLong(ordinal) >>> 32;\n      assert cursor > 0 : \"invalid cursor \" + cursor;\n      // zero-out the bytes\n      Platform.putLong(baseObject, baseOffset + cursor, 0L);\n      Platform.putLong(baseObject, baseOffset + cursor + 8, 0L);\n\n      if (value == null) {\n        setNullAt(ordinal);\n        // keep the offset for future update\n        Platform.putLong(baseObject, getFieldOffset(ordinal), cursor << 32);\n      } else {\n\n        final BigInteger integer = value.toJavaBigDecimal().unscaledValue();\n        byte[] bytes = integer.toByteArray();\n        assert(bytes.length <= 16);\n\n        // Write the bytes to the variable length portion.\n        Platform.copyMemory(\n                bytes, Platform.BYTE_ARRAY_OFFSET, baseObject, baseOffset + cursor, bytes.length);\n        setLong(ordinal, (cursor << 32) | ((long) bytes.length));\n      }\n    }\n  }\n\n*/\n  @Override\n  protected String getPacketInfo() {\n    return \"MySQL RowData Packet\";\n  }\n\n  // This is for debugging\n  @Override\n  public String toString() {\n    StringBuilder build = new StringBuilder(\"[\");\n    for (int i = 0; i < sizeInBytes; i += 8) {\n      if (i != 0) build.append(',');\n      build.append(Long.toHexString(Platform.getLong(baseObject, baseOffset + i)));\n    }\n    build.append(']');\n    return build.toString();\n  }\n\n  public boolean anyNull() {\n    return BitSetMethods.anySet(baseObject, baseOffset, bitSetWidthInBytes / 8);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/row/UnsafeRowWriter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.row;\n\n\nimport java.math.BigDecimal;\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.array.ByteArrayMethods;\nimport io.mycat.memory.unsafe.bitset.BitSetMethods;\n\n/**\n * A helper class to write data into global row buffer using `UnsafeRow` format.\n *\n * It will remember the offset of row buffer which it starts to write, and move the cursor of row\n * buffer while writing.  If new data(can be the input record if this is the outermost writer, or\n * nested struct if this is an inner writer) comes, the starting cursor of row buffer may be\n * changed, so we need to call `UnsafeRowWriter.reset` before writing, to update the\n * `startingOffset` and clear out null bits.\n *\n * Note that if this is the outermost writer, which means we will always write from the very\n * beginning of the global row buffer, we don't need to update `startingOffset` and can just call\n * `zeroOutNullBytes` before writing new data.\n */\npublic class UnsafeRowWriter {\n\n  private final BufferHolder holder;\n  // The offset of the global buffer where we start to write this row.\n  private int startingOffset;\n  private final int nullBitsSize;\n  private final int fixedSize;\n\n  public UnsafeRowWriter(BufferHolder holder,int numFields) {\n    this.holder = holder;\n    this.nullBitsSize = UnsafeRow.calculateBitSetWidthInBytes(numFields);\n    this.fixedSize = nullBitsSize + 8 * numFields;\n    this.startingOffset = holder.cursor;\n  }\n\n  /**\n   * Resets the `startingOffset` according to the current cursor of row buffer, and clear out null\n   * bits.  This should be called before we write a new nested struct to the row buffer.\n   */\n  public void reset() {\n    this.startingOffset = holder.cursor;\n\n    // grow the global buffer to make sure it has enough space to write fixed-length data.\n    holder.grow(fixedSize);\n    holder.cursor += fixedSize;\n\n    zeroOutNullBytes();\n  }\n\n  /**\n   * Clears out null bits.  This should be called before we write a new row to row buffer.\n   */\n  public void zeroOutNullBytes() {\n    for (int i = 0; i < nullBitsSize; i += 8) {\n      Platform.putLong(holder.buffer, startingOffset + i, 0L);\n    }\n  }\n\n  private void zeroOutPaddingBytes(int numBytes) {\n    if ((numBytes & 0x07) > 0) {\n      Platform.putLong(holder.buffer, holder.cursor + ((numBytes >> 3) << 3), 0L);\n    }\n  }\n\n  public BufferHolder holder() { return holder; }\n\n  public boolean isNullAt(int ordinal) {\n    return BitSetMethods.isSet(holder.buffer, startingOffset, ordinal);\n  }\n\n  public void setNullAt(int ordinal) {\n    BitSetMethods.set(holder.buffer, startingOffset, ordinal);\n    Platform.putLong(holder.buffer, getFieldOffset(ordinal), 0L);\n  }\n\n  public long getFieldOffset(int ordinal) {\n    return startingOffset + nullBitsSize + 8 * ordinal;\n  }\n\n  public void setOffsetAndSize(int ordinal, long size) {\n    setOffsetAndSize(ordinal, holder.cursor, size);\n  }\n\n  public void setOffsetAndSize(int ordinal, long currentCursor, long size) {\n    final long relativeOffset = currentCursor - startingOffset;\n    final long fieldOffset = getFieldOffset(ordinal);\n    final long offsetAndSize = (relativeOffset << 32) | size;\n\n    Platform.putLong(holder.buffer, fieldOffset, offsetAndSize);\n  }\n\n  // Do word alignment for this row and grow the row buffer if needed.\n  // todo: remove this after we make unsafe array data word align.\n  public void alignToWords(int numBytes) {\n    final int remainder = numBytes & 0x07;\n\n    if (remainder > 0) {\n      final int paddingBytes = 8 - remainder;\n      holder.grow(paddingBytes);\n\n      for (int i = 0; i < paddingBytes; i++) {\n        Platform.putByte(holder.buffer, holder.cursor, (byte) 0);\n        holder.cursor++;\n      }\n    }\n  }\n\n  public void write(int ordinal, boolean value) {\n    final long offset = getFieldOffset(ordinal);\n    Platform.putLong(holder.buffer, offset, 0L);\n    Platform.putBoolean(holder.buffer, offset, value);\n  }\n\n  public void write(int ordinal, byte value) {\n    final long offset = getFieldOffset(ordinal);\n    Platform.putLong(holder.buffer, offset, 0L);\n    Platform.putByte(holder.buffer, offset, value);\n  }\n\n  public void write(int ordinal, short value) {\n    final long offset = getFieldOffset(ordinal);\n    Platform.putLong(holder.buffer, offset, 0L);\n    Platform.putShort(holder.buffer, offset, value);\n  }\n\n  public void write(int ordinal, int value) {\n    final long offset = getFieldOffset(ordinal);\n    Platform.putLong(holder.buffer, offset, 0L);\n    Platform.putInt(holder.buffer, offset, value);\n  }\n\n  public void write(int ordinal, long value) {\n    Platform.putLong(holder.buffer, getFieldOffset(ordinal), value);\n  }\n\n  public void write(int ordinal, float value) {\n    if (Float.isNaN(value)) {\n      value = Float.NaN;\n    }\n    final long offset = getFieldOffset(ordinal);\n    Platform.putLong(holder.buffer, offset, 0L);\n    Platform.putFloat(holder.buffer, offset, value);\n  }\n\n  public void write(int ordinal, double value) {\n    if (Double.isNaN(value)) {\n      value = Double.NaN;\n    }\n    Platform.putDouble(holder.buffer, getFieldOffset(ordinal), value);\n  }\n\n  public void write(int ordinal, byte[] input) {\n    if(input == null){\n      return;\n    }\n    write(ordinal, input, 0, input.length);\n  }\n  \n  public void grow( int numBytes) {\n\t  final int roundedSize = ByteArrayMethods.roundNumberOfBytesToNearestWord(numBytes);\n\n\t  // grow the global buffer before writing data.\n\t  holder.grow(roundedSize);\n  }\n  \n  public void write(int ordinal, byte[] input, int offset, int numBytes) {\n    final int roundedSize = ByteArrayMethods.roundNumberOfBytesToNearestWord(numBytes);\n\n    // grow the global buffer before writing data.\n    holder.grow(roundedSize);\n\n    zeroOutPaddingBytes(numBytes);\n\n    // Write the bytes to the variable length portion.\n    Platform.copyMemory(input, Platform.BYTE_ARRAY_OFFSET + offset,\n      holder.buffer, holder.cursor, numBytes);\n\n    setOffsetAndSize(ordinal, numBytes);\n\n    // move the cursor forward.\n    holder.cursor += roundedSize;\n  }\n\n  \t/**\n\t * different from Spark, we use java BigDecimal here, \n\t * and we limit the max precision to be 38 because the bytes length limit to be 16\n\t * \n\t * @param ordinal\n\t * @param input\n\t */\n\tpublic void write(int ordinal, BigDecimal input) {\n\n\t\t// grow the global buffer before writing data.\n\t\tholder.grow(16);\n\n\t\t// zero-out the bytes\n\t\tPlatform.putLong(holder.buffer, holder.cursor, 0L);\n\t\tPlatform.putLong(holder.buffer, holder.cursor + 8, 0L);\n\n\t\t// Make sure Decimal object has the same scale as DecimalType.\n\t\t// Note that we may pass in null Decimal object to set null for it.\n\t\tif (input == null) {\n\t\t\tBitSetMethods.set(holder.buffer, startingOffset, ordinal);\n\t\t\t// keep the offset for future update\n\t\t\tsetOffsetAndSize(ordinal, 0L);\n\t\t} else {\n\t\t\tfinal byte[] bytes = input.unscaledValue().toByteArray();\n\t\t\tassert bytes.length <= 16;\n\n\t\t\t// Write the bytes to the variable length portion.\n\t\t\tPlatform.copyMemory(bytes, Platform.BYTE_ARRAY_OFFSET, holder.buffer, holder.cursor, bytes.length);\n\t\t\tsetOffsetAndSize(ordinal, bytes.length);\n\t\t}\n\n\t\t// move the cursor forward.\n\t\tholder.cursor += 16;\n\t}\n  \n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/storage/ConnectionId.java",
    "content": "package io.mycat.memory.unsafe.storage;\n\n/**\n *\n * Created by zagnix on 2016/6/6.\n *\n */\npublic abstract class ConnectionId {\n\tprotected String name;\n\tpublic abstract String getBlockName();\n\n\t@Override\n\tpublic boolean equals(Object arg0) {\n\t\treturn super.equals(arg0);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn super.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn super.toString();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/storage/DataNodeDiskManager.java",
    "content": "package io.mycat.memory.unsafe.storage;\n\n\n\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\n\n/**\n * Created by zagnix on 2016/6/3.\n */\npublic class DataNodeDiskManager {\n\n    private MycatPropertyConf conf;\n    private  boolean deleteFilesOnStop;\n    private  SerializerManager serializerManager;\n\n    public DataNodeDiskManager(MycatPropertyConf conf, boolean deleteFilesOnStop, SerializerManager  serializerManager){\n        this.conf = conf;\n        this.deleteFilesOnStop = deleteFilesOnStop;\n        this.serializerManager = serializerManager;\n    }\n\n    public DataNodeFileManager diskBlockManager() throws IOException {\n        return new DataNodeFileManager(conf, deleteFilesOnStop);\n    }\n\n\n    /**\n     * A short circuited method to get a block writer that can write data directly to disk.\n     * The Block will be appended to the File specified by filename. Callers should handle error\n     * cases.\n     */\n    public DiskRowWriter getDiskWriter(\n            ConnectionId blockId,\n            File file,\n            SerializerInstance serializerInstance,\n            int bufferSize) throws IOException {\n        boolean syncWrites = conf.getBoolean(\"mycat.merge.sync\", false);\n        return new DiskRowWriter(file, serializerInstance, bufferSize,new FileOutputStream(file),\n                syncWrites,blockId);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/storage/DataNodeFileManager.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.storage;\n\n\nimport io.mycat.memory.unsafe.utils.JavaUtils;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\n\n\n/**\n * Creates and maintains the logical mapping between logical blocks and physical on-disk\n * locations. One block is mapped to one file with a name given by its BlockId.\n *\n * Block files are hashed among the directories listed in mycat.local.dir\n */\npublic class DataNodeFileManager {\n  private static final Logger LOG = LoggerFactory.getLogger(DataNodeFileManager.class);\n  private MycatPropertyConf conf;\n  private  boolean deleteFilesOnStop;\n  /**\n   * TODO 操作完成之后，需要删除临时文件\n   */\n  // The content of subDirs is immutable but the content of subDirs(i) is mutable. And the content\n  // of subDirs(i) is protected by the lock of subDirs(i)\n  // private val shutdownHook ;\n  /* Create one local directory for each path mentioned in spark.local.dir; then, inside this\n   * directory, create multiple subdirectories that we will hash files into, in order to avoid\n   * having really large inodes at the top level. */\n\n  private List<File> localDirs ;\n  private int subDirsPerLocalDir;\n\n  private ConcurrentHashMap<Integer,ArrayList<File>> subDirs;\n\n\n  public DataNodeFileManager(MycatPropertyConf conf , boolean deleteFilesOnStop) throws IOException {\n\n    this.conf = conf;\n    this.deleteFilesOnStop = deleteFilesOnStop;\n\n\n    subDirsPerLocalDir = conf.getInt(\"mycat.diskStore.subDirectories\", 64);\n    localDirs  = createLocalDirs(conf);\n    if (localDirs.isEmpty()) {\n      System.exit(-1);\n    }\n    subDirs =  new ConcurrentHashMap<Integer,ArrayList<File>>(localDirs.size());\n\n\n\n    for (int i = 0; i < localDirs.size() ; i++) {\n      ArrayList<File> list = new ArrayList<File>(subDirsPerLocalDir);\n\n      for (int j = 0; j < subDirsPerLocalDir; j++) {\n        list.add(i,null);\n      }\n\n      subDirs.put(i,list);\n    }\n\n  }\n\n  /** Produces a unique block id and File suitable for storing local intermediate results. */\n  public TempDataNodeId createTempLocalBlock() throws IOException {\n\n    TempDataNodeId blockId = new TempDataNodeId(UUID.randomUUID().toString());\n\n    while (getFile(blockId).exists()) {\n      blockId = new TempDataNodeId(UUID.randomUUID().toString());\n    };\n\n    return  blockId;\n  }\n\n\n  /** Looks up a file by hashing it into one of our local subdirectories. */\n  // This method should be kept in sync with\n  // org.apache.spark.network.shuffle.ExternalShuffleBlockResolver#getFile().\n  public File getFile(String filename) throws IOException {\n    // Figure out which local directory it hashes to, and which subdirectory in that\n    int hash = JavaUtils.nonNegativeHash(filename);\n    int dirId = hash % localDirs.size();\n    int subDirId = (hash / localDirs.size()) % subDirsPerLocalDir;\n\n    synchronized (this) {\n      File file = subDirs.get(dirId).get(subDirId);\n      if (file != null) {\n      \n      } else {\n        file = new File(localDirs.get(dirId), \"%02x\".format(String.valueOf(subDirId)));\n        if (!file.exists() && !file.mkdir()) {\n          throw new IOException(\"Failed to create local dir in $newDir.\");\n        }\n        subDirs.get(dirId).add(subDirId,file);\n      }\n    }\n\n    /**\n     *类似二维数组\n     */\n    return  new File(subDirs.get(dirId).get(subDirId),filename);\n  }\n\n  public File getFile(ConnectionId connid) throws IOException {\n    return getFile(connid.name);\n  }\n\n  /**TODO config root\n   * Create local directories for storing block data. These directories are\n   * located inside configured local directories and won't\n   * be deleted on JVM exit when using the external shuffle service.\n   */\n  private  List<File> createLocalDirs(MycatPropertyConf conf) {\n\n    String rootDirs = conf.getString(\"mycat.local.dirs\",\"datanode\");\n\n    String rdir[] = rootDirs.split(\",\");\n    List<File> dirs = new ArrayList<File>();\n    for (int i = 0; i <rdir.length ; i++) {\n      try {\n        File localDir = JavaUtils.createDirectory(rdir[i],\"datenode\");\n        dirs.add(localDir);\n      } catch(Exception e) {\n        LOG.error(\"Failed to create local dir in \"+ rdir[i] + \". Ignoring this directory.\");\n      }\n    }\n\n    return  dirs;\n  }\n\n  /** Cleanup local dirs and stop shuffle sender. */\n  public void stop() {\n    doStop();\n  }\n\n  private void doStop() {\n    if (deleteFilesOnStop) {\n      File localDir;\n      int i = 0;\n      System.out.println(localDirs.size());\n      while (i<localDirs.size()&&localDirs.size()>0){\n        localDir = localDirs.get(i);\n        //System.out.println(localDir);\n        if (localDir.isDirectory() && localDir.exists()) {\n          try {\n            JavaUtils.deleteRecursively(localDir);\n          } catch(Exception e) {\n            LOG.error(e.getMessage());\n          }\n        }\n        i++;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/storage/DeserializationStream.java",
    "content": "package io.mycat.memory.unsafe.storage;\n\n\n\n/**\n * Created by zagnix on 2016/6/3.\n */\npublic abstract class DeserializationStream {\n    /** The most general-purpose method to read an object. */\n    public abstract <T> T readObject();\n    /** Reads the object representing the key of a key-value pair. */\n    public <T> T readKey(){return readObject();}\n    /** Reads the object representing the value of a key-value pair. */\n    public <T> T readValue(){ return readObject();}\n    public abstract void close();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/storage/DiskRowWriter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.storage;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.*;\nimport java.nio.channels.FileChannel;\n\n/**\n * A class for writing JVM objects directly to a file on disk. This class allows data to be appended\n * to an existing block and can guarantee atomicity in the case of faults as it allows the caller to\n * revert partial writes.\n *\n * This class does not support concurrent writes. Also, once the writer has been opened it cannot be\n * reopened again.\n */\npublic  class DiskRowWriter extends OutputStream {\n  /** The file channel, used for repositioning / truncating the file. */\n  private static final Logger LOG = LoggerFactory.getLogger(DiskRowWriter.class);\n\n  private FileChannel channel = null;\n  private OutputStream bs = null;\n  private FileOutputStream fos  = null;\n  private TimeTrackingOutputStream ts  = null;\n  private SerializationStream objOut  = null;\n  private boolean initialized = false;\n  private boolean hasBeenClosed = false;\n  private boolean commitAndCloseHasBeenCalled = false;\n\n  /**\n   * Cursors used to represent positions in the file.\n   *\n   * xxxxxxxx|--------|---       |\n   *         ^        ^          ^\n   *         |        |        finalPosition\n   *         |      reportedPosition\n   *       initialPosition\n   *\n   * initialPosition: Offset in the file where we start writing. Immutable.\n   * reportedPosition: Position at the time of the last update to the write metrics.\n   * finalPosition: Offset where we stopped writing. Set on closeAndCommit() then never changed.\n   * -----: Current writes to the underlying file.\n   * xxxxx: Existing contents of the file.\n   */\n  private long initialPosition = 0;\n  private long finalPosition = -1;\n  private long reportedPosition = 0;\n\n  /**\n   * Keep track of number of records written and also use this to periodically\n   * output bytes written since the latter is expensive to do for each record.\n   */\n  private long numRecordsWritten = 0;\n\n  private  File file;\n  private SerializerInstance serializerInstance;\n  private int bufferSize;\n  private  OutputStream compressStream;\n  private boolean syncWrites;\n  // These write metrics concurrently shared with other active DiskBlockObjectWriters who\n  // are themselves performing writes. All updates must be relative.\n  /**ShuffleWriteMetrics  writeMetrics,*/\n  private ConnectionId blockId;\n\n\n  public DiskRowWriter(\n          File file,\n          SerializerInstance serializerInstance,\n          int bufferSize,\n          OutputStream compressStream ,\n          boolean syncWrites,\n          ConnectionId blockId) throws IOException {\n\n    this.file = file;\n    this.serializerInstance = serializerInstance;\n    this.bufferSize = bufferSize;\n    this.compressStream = compressStream;\n    this.syncWrites = syncWrites;\n    this.blockId = blockId;\n    initialPosition = file.length();\n    reportedPosition = initialPosition;\n  }\n\n\n  public DiskRowWriter open() throws FileNotFoundException {\n\n    if (hasBeenClosed) {\n      throw new IllegalStateException(\"Writer already closed. Cannot be reopened.\");\n    }\n\n    fos = new FileOutputStream(file,true);\n    ts = new TimeTrackingOutputStream(/**writeMetrics,*/ fos);\n    channel = fos.getChannel();\n    bs = new BufferedOutputStream(ts,bufferSize);\n    objOut = serializerInstance.serializeStream(bs);\n    initialized = true;\n\n    return this;\n\n  }\n\n\n  @Override\n  public void close() {\n    if (initialized) {\n      try {\n        if (syncWrites) {\n          //Force outstanding writes to disk and track how long it takes\n          objOut.flush();\n          long start = System.nanoTime();\n          fos.getFD().sync();\n          // writeMetrics.incWriteTime(System.nanoTime() - start);\n        }\n      } catch (IOException e) {\n        LOG.error(e.getMessage());\n      }finally {\n        objOut.close();\n      }\n      channel = null;\n      bs = null;\n      fos = null;\n      ts = null;\n      objOut = null;\n      initialized = false;\n      hasBeenClosed = true;\n    }\n  }\n\n  public boolean isOpen(){\n    return objOut != null;\n  }\n\n  /**\n   * Flush the partial writes and commit them as a single atomic block.\n   */\n  public void commitAndClose() throws IOException {\n    if (initialized) {\n      // NOTE: Because Kryo doesn’t flush the underlying stream we explicitly flush both the\n      // serializer stream and the lower level stream.\n      objOut.flush();\n      bs.flush();\n      close();\n      finalPosition = file.length();\n      // In certain compression codecs, more bytes are written after close() is called\n      //writeMetrics.incBytesWritten(finalPosition - reportedPosition)\n    } else {\n      finalPosition = file.length();\n    }\n    commitAndCloseHasBeenCalled = true;\n  }\n\n\n  /**\n   * Reverts writes that haven’t been flushed yet. Callers should invoke this function\n   * when there are runtime exceptions. This method will not throw, though it may be\n   * unsuccessful in truncating written data.\n   *\n   * @return the file that this DiskRowWriter wrote to.\n   */\n  public File revertPartialWritesAndClose() throws IOException {\n    // Discard current writes. We do this by flushing the outstanding writes and then\n    // truncating the file to its initial position.\n    try {\n      if (initialized) {\n        // writeMetrics.decBytesWritten(reportedPosition - initialPosition)\n        // writeMetrics.decRecordsWritten(numRecordsWritten)\n        objOut.flush();\n        bs.flush();\n        close();\n      }\n\n      FileOutputStream truncateStream = new FileOutputStream(file, true);\n      try {\n        truncateStream.getChannel().truncate(initialPosition);\n        return file;\n      } finally {\n        truncateStream.close();\n      }\n    } catch(Exception e) {\n      LOG.error(e.getMessage());\n      return file;\n    }\n  }\n\n  /**\n   * Writes a key-value pair.\n   */\n  private void write(Object key, Object value) throws IOException {\n    if (!initialized) {\n      open();\n    }\n\n    objOut.writeKey(key);\n    objOut.writeValue(value);\n    recordWritten();\n  }\n  @Override\n  public void write(int b){\n    throw new UnsupportedOperationException();\n  }\n  @Override\n  public void write(byte [] kvBytes ,int offs, int len) throws IOException {\n    if (!initialized) {\n      open();\n    }\n\n    bs.write(kvBytes,offs, len);\n  }\n\n  /**\n   * Notify the writer that a record worth of bytes has been written with OutputStream#write.\n   */\n  public void recordWritten() throws IOException {\n    numRecordsWritten += 1;\n//writeMetrics.incRecordsWritten(1)\n\n// TODO: call updateBytesWritten() less frequently.\n    if (numRecordsWritten % 32 == 0) {\n      updateBytesWritten();\n    }\n  }\n\n  /**\n   * Report the number of bytes written in this writer’s shuffle write metrics.\n   * Note that this is only valid before the underlying streams are closed.\n   */\n  private void updateBytesWritten() throws IOException {\n    long pos = channel.position();\n    //writeMetrics.incBytesWritten(pos - reportedPosition)\n    reportedPosition = pos;\n  }\n\n  @Override\n  public void flush() throws IOException {\n    objOut.flush();\n    bs.flush();\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/storage/DummySerializerInstance.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.storage;\n\n\n\nimport io.mycat.memory.unsafe.Platform;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.ByteBuffer;\n\n/**\n * Unfortunately, we need a serializer instance in order to construct a DiskRowWriter.\n * Our shuffle write path doesn't actually use this serializer (since we end up calling the\n * `write() OutputStream methods), but DiskRowWriter still calls some methods on it. To work\n * around this, we pass a dummy no-op serializer.\n */\n\npublic final class DummySerializerInstance extends SerializerInstance {\n\n  public static final DummySerializerInstance INSTANCE = new DummySerializerInstance();\n\n  private DummySerializerInstance() { }\n\n  @Override\n  public SerializationStream serializeStream(final OutputStream s) {\n    return new SerializationStream() {\n      @Override\n      public SerializationStream writeObject(Object o) {\n        return null;\n      }\n\n      @Override\n      public void flush() {\n        // Need to implement this because DiskObjectWriter uses it to flush the compression stream\n        try {\n          s.flush();\n        } catch (IOException e) {\n          Platform.throwException(e);\n        }\n      }\n//      public <T> SerializationStream writeObject(T t, T ev1) {\n//        throw new UnsupportedOperationException();\n//      }\n\n      @Override\n      public void close() {\n        // Need to implement this because DiskObjectWriter uses it to close the compression stream\n        try {\n          s.close();\n        } catch (IOException e) {\n          Platform.throwException(e);\n        }\n      }\n    };\n  }\n\n\n  public <T> ByteBuffer serialize(T t, T ev1) {\n    throw new UnsupportedOperationException();\n  }\n\n\n  public DeserializationStream deserializeStream(InputStream s) {\n    throw new UnsupportedOperationException();\n  }\n\n\n  public <T> T deserialize(ByteBuffer bytes, ClassLoader loader, T ev1) {\n    throw new UnsupportedOperationException();\n  }\n\n  public <T> T deserialize(ByteBuffer bytes, T ev1) {\n    throw new UnsupportedOperationException();\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/storage/SerializationStream.java",
    "content": "package io.mycat.memory.unsafe.storage;\n\nimport java.util.Iterator;\n\n/**\n * Created by zagnix on 2016/6/3.\n */\npublic abstract  class SerializationStream{\n\n    /** The most general-purpose method to write an object. */\n    public abstract <T> SerializationStream writeObject(T t);\n    /** Writes the object representing the key of a key-value pair. */\n    public <T>  SerializationStream writeKey(T key){\n       return writeObject(key);\n    }\n    /** Writes the object representing the value of a key-value pair. */\n    public <T> SerializationStream writeValue(T value){\n        return  writeObject(value);\n    }\n\n    public abstract void  flush();\n    public abstract void close();\n\n    public <T>  SerializationStream writeAll(Iterator<T> iter){\n        while (iter.hasNext()) {\n            writeObject(iter.next());\n        }\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/storage/SerializerInstance.java",
    "content": "package io.mycat.memory.unsafe.storage;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\n/**\n * Created by zagnix on 2016/6/3.\n */\npublic abstract  class SerializerInstance {\n    protected abstract SerializationStream serializeStream(OutputStream s );\n    protected abstract DeserializationStream deserializeStream(InputStream s);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/storage/SerializerManager.java",
    "content": "package io.mycat.memory.unsafe.storage;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\n/**\n * Created by zagnix on 2016/6/3.\n */\npublic class SerializerManager  {\n\n    /**\n     * Wrap an output stream for compression if block compression is enabled for its block type\n     */\n    public  OutputStream wrapForCompression(ConnectionId blockId , OutputStream s){\n        return  s;\n    }\n\n    /**\n     * Wrap an input stream for compression if block compression is enabled for its block type\n     */\n    public InputStream wrapForCompression(ConnectionId blockId, InputStream s){\n        return  s;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/storage/TempDataNodeId.java",
    "content": "package io.mycat.memory.unsafe.storage;\n\n/**\n * Created by zagnix on 2016/6/3.\n */\npublic class TempDataNodeId extends ConnectionId {\n\n    private String uuid;\n\n    public TempDataNodeId(String uuid) {\n        super();\n        this.name = uuid;\n        this.uuid = uuid;\n    }\n\n    @Override\n    public String getBlockName() {\n        return \"temp_local_\" + uuid;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/storage/TimeTrackingOutputStream.java",
    "content": "\n\npackage io.mycat.memory.unsafe.storage;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\n\n/**\n * Intercepts write calls and tracks total time spent writing in order to update shuffle write\n * metrics. Not thread safe.\n */\npublic final class TimeTrackingOutputStream extends OutputStream {\n\n  /**private final ShuffleWriteMetrics writeMetrics;*/\n  private final OutputStream outputStream;\n\n  public TimeTrackingOutputStream(OutputStream outputStream) {\n    this.outputStream = outputStream;\n  }\n\n  @Override\n  public void write(int b) throws IOException {\n    final long startTime = System.nanoTime();\n    outputStream.write(b);\n  }\n\n  @Override\n  public void write(byte[] b) throws IOException {\n    final long startTime = System.nanoTime();\n    outputStream.write(b);\n  }\n\n  @Override\n  public void write(byte[] b, int off, int len) throws IOException {\n    final long startTime = System.nanoTime();\n    outputStream.write(b, off, len);\n  }\n\n  @Override\n  public void flush() throws IOException {\n    final long startTime = System.nanoTime();\n    outputStream.flush();\n  }\n\n  @Override\n  public void close() throws IOException {\n    final long startTime = System.nanoTime();\n    outputStream.close();\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/types/ByteArray.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.types;\n\nimport io.mycat.memory.unsafe.Platform;\n\nimport java.util.Arrays;\n\npublic final class ByteArray {\n\n  public static final byte[] EMPTY_BYTE = new byte[0];\n\n  /**\n   * Writes the content of a byte array into a memory address, identified by an object and an\n   * offset. The target memory address must already been allocated, and have enough space to\n   * hold all the bytes in this string.\n   */\n  public static void writeToMemory(byte[] src, Object target, long targetOffset) {\n    Platform.copyMemory(src, Platform.BYTE_ARRAY_OFFSET, target, targetOffset, src.length);\n  }\n\n  /**\n   * Returns a 64-bit integer that can be used as the prefix used in sorting.\n   */\n  public static long getPrefix(byte[] bytes) {\n    if (bytes == null) {\n      return 0L;\n    } else {\n      final int minLen = Math.min(bytes.length, 8);\n      long p = 0;\n      for (int i = 0; i < minLen; ++i) {\n        p |= (128L + Platform.getByte(bytes, Platform.BYTE_ARRAY_OFFSET + i))\n            << (56 - 8 * i);\n      }\n      return p;\n    }\n  }\n\n  public static byte[] subStringSQL(byte[] bytes, int pos, int len) {\n    // This pos calculation is according to UTF8String#subStringSQL\n    if (pos > bytes.length) {\n      return EMPTY_BYTE;\n    }\n    int start = 0;\n    int end;\n    if (pos > 0) {\n      start = pos - 1;\n    } else if (pos < 0) {\n      start = bytes.length + pos;\n    }\n    if ((bytes.length - start) < len) {\n      end = bytes.length;\n    } else {\n      end = start + len;\n    }\n    start = Math.max(start, 0); // underflow\n    if (start >= end) {\n      return EMPTY_BYTE;\n    }\n    return Arrays.copyOfRange(bytes, start, end);\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/types/CalendarInterval.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.types;\n\nimport java.io.Serializable;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * The internal representation of interval type.\n */\npublic final class CalendarInterval implements Serializable {\n  public static final long MICROS_PER_MILLI = 1000L;\n  public static final long MICROS_PER_SECOND = MICROS_PER_MILLI * 1000;\n  public static final long MICROS_PER_MINUTE = MICROS_PER_SECOND * 60;\n  public static final long MICROS_PER_HOUR = MICROS_PER_MINUTE * 60;\n  public static final long MICROS_PER_DAY = MICROS_PER_HOUR * 24;\n  public static final long MICROS_PER_WEEK = MICROS_PER_DAY * 7;\n\n  /**\n   * A function to generate regex which matches interval string's unit part like \"3 years\".\n   *\n   * First, we can leave out some units in interval string, and we only care about the value of\n   * unit, so here we use non-capturing group to wrap the actual regex.\n   * At the beginning of the actual regex, we should match spaces before the unit part.\n   * Next is the number part, starts with an optional \"-\" to represent negative value. We use\n   * capturing group to wrap this part as we need the value later.\n   * Finally is the unit name, ends with an optional \"s\".\n   */\n  private static String unitRegex(String unit) {\n    return \"(?:\\\\s+(-?\\\\d+)\\\\s+\" + unit + \"s?)?\";\n  }\n\n  private static Pattern p = Pattern.compile(\"interval\" + unitRegex(\"year\") + unitRegex(\"month\") +\n    unitRegex(\"week\") + unitRegex(\"day\") + unitRegex(\"hour\") + unitRegex(\"minute\") +\n    unitRegex(\"second\") + unitRegex(\"millisecond\") + unitRegex(\"microsecond\"));\n\n  private static Pattern yearMonthPattern =\n    Pattern.compile(\"^(?:['|\\\"])?([+|-])?(\\\\d+)-(\\\\d+)(?:['|\\\"])?$\");\n\n  private static Pattern dayTimePattern =\n    Pattern.compile(\"^(?:['|\\\"])?([+|-])?(\\\\d+) (\\\\d+):(\\\\d+):(\\\\d+)(\\\\.(\\\\d+))?(?:['|\\\"])?$\");\n\n  private static Pattern quoteTrimPattern = Pattern.compile(\"^(?:['|\\\"])?(.*?)(?:['|\\\"])?$\");\n\n  private static long toLong(String s) {\n    if (s == null) {\n      return 0;\n    } else {\n      return Long.parseLong(s);\n    }\n  }\n\n  public static CalendarInterval fromString(String s) {\n    if (s == null) {\n      return null;\n    }\n    s = s.trim();\n    Matcher m = p.matcher(s);\n    if (!m.matches() || s.equals(\"interval\")) {\n      return null;\n    } else {\n      long months = toLong(m.group(1)) * 12 + toLong(m.group(2));\n      long microseconds = toLong(m.group(3)) * MICROS_PER_WEEK;\n      microseconds += toLong(m.group(4)) * MICROS_PER_DAY;\n      microseconds += toLong(m.group(5)) * MICROS_PER_HOUR;\n      microseconds += toLong(m.group(6)) * MICROS_PER_MINUTE;\n      microseconds += toLong(m.group(7)) * MICROS_PER_SECOND;\n      microseconds += toLong(m.group(8)) * MICROS_PER_MILLI;\n      microseconds += toLong(m.group(9));\n      return new CalendarInterval((int) months, microseconds);\n    }\n  }\n\n  public static long toLongWithRange(String fieldName,\n      String s, long minValue, long maxValue) throws IllegalArgumentException {\n    long result = 0;\n    if (s != null) {\n      result = Long.parseLong(s);\n      if (result < minValue || result > maxValue) {\n        throw new IllegalArgumentException(String.format(\"%s %d outside range [%d, %d]\",\n          fieldName, result, minValue, maxValue));\n      }\n    }\n    return result;\n  }\n\n  /**\n   * Parse YearMonth string in form: [-]YYYY-MM\n   *\n   * adapted from HiveIntervalYearMonth.valueOf\n   */\n  public static CalendarInterval fromYearMonthString(String s) throws IllegalArgumentException {\n    CalendarInterval result = null;\n    if (s == null) {\n      throw new IllegalArgumentException(\"Interval year-month string was null\");\n    }\n    s = s.trim();\n    Matcher m = yearMonthPattern.matcher(s);\n    if (!m.matches()) {\n      throw new IllegalArgumentException(\n        \"Interval string does not match year-month format of 'y-m': \" + s);\n    } else {\n      try {\n        int sign = m.group(1) != null && m.group(1).equals(\"-\") ? -1 : 1;\n        int years = (int) toLongWithRange(\"year\", m.group(2), 0, Integer.MAX_VALUE);\n        int months = (int) toLongWithRange(\"month\", m.group(3), 0, 11);\n        result = new CalendarInterval(sign * (years * 12 + months), 0);\n      } catch (Exception e) {\n        throw new IllegalArgumentException(\n          \"Error parsing interval year-month string: \" + e.getMessage(), e);\n      }\n    }\n    return result;\n  }\n\n  /**\n   * Parse dayTime string in form: [-]d HH:mm:ss.nnnnnnnnn\n   *\n   * adapted from HiveIntervalDayTime.valueOf\n   */\n  public static CalendarInterval fromDayTimeString(String s) throws IllegalArgumentException {\n    CalendarInterval result = null;\n    if (s == null) {\n      throw new IllegalArgumentException(\"Interval day-time string was null\");\n    }\n    s = s.trim();\n    Matcher m = dayTimePattern.matcher(s);\n    if (!m.matches()) {\n      throw new IllegalArgumentException(\n        \"Interval string does not match day-time format of 'd h:m:s.n': \" + s);\n    } else {\n      try {\n        int sign = m.group(1) != null && m.group(1).equals(\"-\") ? -1 : 1;\n        long days = toLongWithRange(\"day\", m.group(2), 0, Integer.MAX_VALUE);\n        long hours = toLongWithRange(\"hour\", m.group(3), 0, 23);\n        long minutes = toLongWithRange(\"minute\", m.group(4), 0, 59);\n        long seconds = toLongWithRange(\"second\", m.group(5), 0, 59);\n        // Hive allow nanosecond precision interval\n        long nanos = toLongWithRange(\"nanosecond\", m.group(7), 0L, 999999999L);\n        result = new CalendarInterval(0, sign * (\n          days * MICROS_PER_DAY + hours * MICROS_PER_HOUR + minutes * MICROS_PER_MINUTE +\n          seconds * MICROS_PER_SECOND + nanos / 1000L));\n      } catch (Exception e) {\n        throw new IllegalArgumentException(\n          \"Error parsing interval day-time string: \" + e.getMessage(), e);\n      }\n    }\n    return result;\n  }\n\n  public static CalendarInterval fromSingleUnitString(String unit, String s)\n      throws IllegalArgumentException {\n\n    CalendarInterval result = null;\n    if (s == null) {\n      throw new IllegalArgumentException(String.format(\"Interval %s string was null\", unit));\n    }\n    s = s.trim();\n    Matcher m = quoteTrimPattern.matcher(s);\n    if (!m.matches()) {\n      throw new IllegalArgumentException(\n        \"Interval string does not match day-time format of 'd h:m:s.n': \" + s);\n    } else {\n      try {\n        if (unit.equals(\"year\")) {\n          int year = (int) toLongWithRange(\"year\", m.group(1),\n            Integer.MIN_VALUE / 12, Integer.MAX_VALUE / 12);\n          result = new CalendarInterval(year * 12, 0L);\n\n        } else if (unit.equals(\"month\")) {\n          int month = (int) toLongWithRange(\"month\", m.group(1),\n            Integer.MIN_VALUE, Integer.MAX_VALUE);\n          result = new CalendarInterval(month, 0L);\n\n        } else if (unit.equals(\"week\")) {\n          long week = toLongWithRange(\"week\", m.group(1),\n                  Long.MIN_VALUE / MICROS_PER_WEEK, Long.MAX_VALUE / MICROS_PER_WEEK);\n          result = new CalendarInterval(0, week * MICROS_PER_WEEK);\n\n        } else if (unit.equals(\"day\")) {\n          long day = toLongWithRange(\"day\", m.group(1),\n            Long.MIN_VALUE / MICROS_PER_DAY, Long.MAX_VALUE / MICROS_PER_DAY);\n          result = new CalendarInterval(0, day * MICROS_PER_DAY);\n\n        } else if (unit.equals(\"hour\")) {\n          long hour = toLongWithRange(\"hour\", m.group(1),\n            Long.MIN_VALUE / MICROS_PER_HOUR, Long.MAX_VALUE / MICROS_PER_HOUR);\n          result = new CalendarInterval(0, hour * MICROS_PER_HOUR);\n\n        } else if (unit.equals(\"minute\")) {\n          long minute = toLongWithRange(\"minute\", m.group(1),\n            Long.MIN_VALUE / MICROS_PER_MINUTE, Long.MAX_VALUE / MICROS_PER_MINUTE);\n          result = new CalendarInterval(0, minute * MICROS_PER_MINUTE);\n\n        } else if (unit.equals(\"second\")) {\n          long micros = parseSecondNano(m.group(1));\n          result = new CalendarInterval(0, micros);\n\n        } else if (unit.equals(\"millisecond\")) {\n          long millisecond = toLongWithRange(\"millisecond\", m.group(1),\n                  Long.MIN_VALUE / MICROS_PER_MILLI, Long.MAX_VALUE / MICROS_PER_MILLI);\n          result = new CalendarInterval(0, millisecond * MICROS_PER_MILLI);\n\n        } else if (unit.equals(\"microsecond\")) {\n          long micros = Long.parseLong(m.group(1));\n          result = new CalendarInterval(0, micros);\n        }\n      } catch (Exception e) {\n        throw new IllegalArgumentException(\"Error parsing interval string: \" + e.getMessage(), e);\n      }\n    }\n    return result;\n  }\n\n  /**\n   * Parse second_nano string in ss.nnnnnnnnn format to microseconds\n   */\n  public static long parseSecondNano(String secondNano) throws IllegalArgumentException {\n    String[] parts = secondNano.split(\"\\\\.\");\n    if (parts.length == 1) {\n      return toLongWithRange(\"second\", parts[0], Long.MIN_VALUE / MICROS_PER_SECOND,\n        Long.MAX_VALUE / MICROS_PER_SECOND) * MICROS_PER_SECOND;\n\n    } else if (parts.length == 2) {\n      long seconds = parts[0].equals(\"\") ? 0L : toLongWithRange(\"second\", parts[0],\n        Long.MIN_VALUE / MICROS_PER_SECOND, Long.MAX_VALUE / MICROS_PER_SECOND);\n      long nanos = toLongWithRange(\"nanosecond\", parts[1], 0L, 999999999L);\n      return seconds * MICROS_PER_SECOND + nanos / 1000L;\n\n    } else {\n      throw new IllegalArgumentException(\n        \"Interval string does not match second-nano format of ss.nnnnnnnnn\");\n    }\n  }\n\n  public final int months;\n  public final long microseconds;\n\n  public CalendarInterval(int months, long microseconds) {\n    this.months = months;\n    this.microseconds = microseconds;\n  }\n\n  public CalendarInterval add(CalendarInterval that) {\n    int months = this.months + that.months;\n    long microseconds = this.microseconds + that.microseconds;\n    return new CalendarInterval(months, microseconds);\n  }\n\n  public CalendarInterval subtract(CalendarInterval that) {\n    int months = this.months - that.months;\n    long microseconds = this.microseconds - that.microseconds;\n    return new CalendarInterval(months, microseconds);\n  }\n\n  public CalendarInterval negate() {\n    return new CalendarInterval(-this.months, -this.microseconds);\n  }\n\n  @Override\n  public boolean equals(Object other) {\n    if (this == other) return true;\n    if (other == null || !(other instanceof CalendarInterval)) return false;\n\n    CalendarInterval o = (CalendarInterval) other;\n    return this.months == o.months && this.microseconds == o.microseconds;\n  }\n\n  @Override\n  public int hashCode() {\n    return 31 * months + (int) microseconds;\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder sb = new StringBuilder(\"interval\");\n\n    if (months != 0) {\n      appendUnit(sb, months / 12, \"year\");\n      appendUnit(sb, months % 12, \"month\");\n    }\n\n    if (microseconds != 0) {\n      long rest = microseconds;\n      appendUnit(sb, rest / MICROS_PER_WEEK, \"week\");\n      rest %= MICROS_PER_WEEK;\n      appendUnit(sb, rest / MICROS_PER_DAY, \"day\");\n      rest %= MICROS_PER_DAY;\n      appendUnit(sb, rest / MICROS_PER_HOUR, \"hour\");\n      rest %= MICROS_PER_HOUR;\n      appendUnit(sb, rest / MICROS_PER_MINUTE, \"minute\");\n      rest %= MICROS_PER_MINUTE;\n      appendUnit(sb, rest / MICROS_PER_SECOND, \"second\");\n      rest %= MICROS_PER_SECOND;\n      appendUnit(sb, rest / MICROS_PER_MILLI, \"millisecond\");\n      rest %= MICROS_PER_MILLI;\n      appendUnit(sb, rest, \"microsecond\");\n    }\n\n    return sb.toString();\n  }\n\n  private void appendUnit(StringBuilder sb, long value, String unit) {\n    if (value != 0) {\n      sb.append(' ').append(value).append(' ').append(unit).append('s');\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/types/UTF8String.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.types;\n\nimport com.esotericsoftware.kryo.Kryo;\nimport com.esotericsoftware.kryo.KryoSerializable;\nimport com.esotericsoftware.kryo.io.Input;\nimport com.esotericsoftware.kryo.io.Output;\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.array.ByteArrayMethods;\nimport io.mycat.memory.unsafe.hash.Murmur3_x86_32;\n\n\nimport javax.annotation.Nonnull;\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Map;\n\n\n/**\n * A UTF-8 String for internal Spark use.\n * <p>\n * A String encoded in UTF-8 as an Array[Byte], which can be used for comparison,\n * search, see http://en.wikipedia.org/wiki/UTF-8 for details.\n * <p>\n * Note: This is not designed for general use cases, should not be used outside SQL.\n */\npublic final class UTF8String implements Comparable<UTF8String>, Externalizable, KryoSerializable,\n  Cloneable {\n\n  // These are only updated by readExternal() or read()\n  @Nonnull\n  private Object base;\n  private long offset;\n  private int numBytes;\n\n  public Object getBaseObject() { return base; }\n  public long getBaseOffset() { return offset; }\n\n  private static int[] bytesOfCodePointInUTF8 = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n    4, 4, 4, 4, 4, 4, 4, 4,\n    5, 5, 5, 5,\n    6, 6};\n\n  private static boolean isLittleEndian = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;\n\n  private static final UTF8String COMMA_UTF8 = UTF8String.fromString(\",\");\n  public static final UTF8String EMPTY_UTF8 = UTF8String.fromString(\"\");\n\n  /**\n   * Creates an UTF8String from byte array, which should be encoded in UTF-8.\n   *\n   * Note: `bytes` will be hold by returned UTF8String.\n   */\n  public static UTF8String fromBytes(byte[] bytes) {\n    if (bytes != null) {\n      return new UTF8String(bytes, Platform.BYTE_ARRAY_OFFSET, bytes.length);\n    } else {\n      return null;\n    }\n  }\n\n  /**\n   * Creates an UTF8String from byte array, which should be encoded in UTF-8.\n   *\n   * Note: `bytes` will be hold by returned UTF8String.\n   */\n  public static UTF8String fromBytes(byte[] bytes, int offset, int numBytes) {\n    if (bytes != null) {\n      return new UTF8String(bytes, Platform.BYTE_ARRAY_OFFSET + offset, numBytes);\n    } else {\n      return null;\n    }\n  }\n\n  /**\n   * Creates an UTF8String from given address (base and offset) and length.\n   */\n  public static UTF8String fromAddress(Object base, long offset, int numBytes) {\n    return new UTF8String(base, offset, numBytes);\n  }\n\n  /**\n   * Creates an UTF8String from String.\n   */\n  public static UTF8String fromString(String str) {\n    return str == null ? null : fromBytes(str.getBytes(StandardCharsets.UTF_8));\n  }\n\n  /**\n   * Creates an UTF8String that contains `length` spaces.\n   */\n  public static UTF8String blankString(int length) {\n    byte[] spaces = new byte[length];\n    Arrays.fill(spaces, (byte) ' ');\n    return fromBytes(spaces);\n  }\n\n  protected UTF8String(Object base, long offset, int numBytes) {\n    this.base = base;\n    this.offset = offset;\n    this.numBytes = numBytes;\n  }\n\n  // for serialization\n  public UTF8String() {\n    this(null, 0, 0);\n  }\n\n  /**\n   * Writes the content of this string into a memory address, identified by an object and an offset.\n   * The target memory address must already been allocated, and have enough space to hold all the\n   * bytes in this string.\n   */\n  public void writeToMemory(Object target, long targetOffset) {\n    Platform.copyMemory(base, offset, target, targetOffset, numBytes);\n  }\n\n  public void writeTo(ByteBuffer buffer) {\n    assert(buffer.hasArray());\n    byte[] target = buffer.array();\n    int offset = buffer.arrayOffset();\n    int pos = buffer.position();\n    writeToMemory(target, Platform.BYTE_ARRAY_OFFSET + offset + pos);\n    buffer.position(pos + numBytes);\n  }\n\n  /**\n   * Returns the number of bytes for a code point with the first byte as `b`\n   * @param b The first byte of a code point\n   */\n  private static int numBytesForFirstByte(final byte b) {\n    final int offset = (b & 0xFF) - 192;\n    return (offset >= 0) ? bytesOfCodePointInUTF8[offset] : 1;\n  }\n\n  /**\n   * Returns the number of bytes\n   */\n  public int numBytes() {\n    return numBytes;\n  }\n\n  /**\n   * Returns the number of code points in it.\n   */\n  public int numChars() {\n    int len = 0;\n    for (int i = 0; i < numBytes; i += numBytesForFirstByte(getByte(i))) {\n      len += 1;\n    }\n    return len;\n  }\n\n  /**\n   * Returns a 64-bit integer that can be used as the prefix used in sorting.\n   */\n  public long getPrefix() {\n    // Since JVMs are either 4-byte aligned or 8-byte aligned, we check the size of the string.\n    // If size is 0, just return 0.\n    // If size is between 0 and 4 (inclusive), assume data is 4-byte aligned under the hood and\n    // use a getInt to fetch the prefix.\n    // If size is greater than 4, assume we have at least 8 bytes of data to fetch.\n    // After getting the data, we use a mask to mask out data that is not part of the string.\n    long p;\n    long mask = 0;\n    if (isLittleEndian) {\n      if (numBytes >= 8) {\n        p = Platform.getLong(base, offset);\n      } else if (numBytes > 4) {\n        p = Platform.getLong(base, offset);\n        mask = (1L << (8 - numBytes) * 8) - 1;\n      } else if (numBytes > 0) {\n        p = (long) Platform.getInt(base, offset);\n        mask = (1L << (8 - numBytes) * 8) - 1;\n      } else {\n        p = 0;\n      }\n      p = Long.reverseBytes(p);\n    } else {\n      // byteOrder == ByteOrder.BIG_ENDIAN\n      if (numBytes >= 8) {\n        p = Platform.getLong(base, offset);\n      } else if (numBytes > 4) {\n        p = Platform.getLong(base, offset);\n        mask = (1L << (8 - numBytes) * 8) - 1;\n      } else if (numBytes > 0) {\n        p = ((long) Platform.getInt(base, offset)) << 32;\n        mask = (1L << (8 - numBytes) * 8) - 1;\n      } else {\n        p = 0;\n      }\n    }\n    p &= ~mask;\n    return p;\n  }\n\n  /**\n   * Returns the underline bytes, will be a copy of it if it's part of another array.\n   */\n  public byte[] getBytes() {\n    // avoid copy if `base` is `byte[]`\n    if (offset == Platform.BYTE_ARRAY_OFFSET && base instanceof byte[]\n      && ((byte[]) base).length == numBytes) {\n      return (byte[]) base;\n    } else {\n      byte[] bytes = new byte[numBytes];\n      Platform.copyMemory(base, offset, bytes, Platform.BYTE_ARRAY_OFFSET, numBytes);\n      return bytes;\n    }\n  }\n\n  /**\n   * Returns a substring of this.\n   * @param start the position of first code point\n   * @param until the position after last code point, exclusive.\n   */\n  public UTF8String substring(final int start, final int until) {\n    if (until <= start || start >= numBytes) {\n      return EMPTY_UTF8;\n    }\n\n    int i = 0;\n    int c = 0;\n    while (i < numBytes && c < start) {\n      i += numBytesForFirstByte(getByte(i));\n      c += 1;\n    }\n\n    int j = i;\n    while (i < numBytes && c < until) {\n      i += numBytesForFirstByte(getByte(i));\n      c += 1;\n    }\n\n    if (i > j) {\n      byte[] bytes = new byte[i - j];\n      Platform.copyMemory(base, offset + j, bytes, Platform.BYTE_ARRAY_OFFSET, i - j);\n      return fromBytes(bytes);\n    } else {\n      return EMPTY_UTF8;\n    }\n  }\n\n  public UTF8String substringSQL(int pos, int length) {\n    // Information regarding the pos calculation:\n    // Hive and SQL use one-based indexing for SUBSTR arguments but also accept zero and\n    // negative indices for start positions. If a start index i is greater than 0, it\n    // refers to element i-1 in the sequence. If a start index i is less than 0, it refers\n    // to the -ith element before the end of the sequence. If a start index i is 0, it\n    // refers to the first element.\n    int len = numChars();\n    int start = (pos > 0) ? pos -1 : ((pos < 0) ? len + pos : 0);\n    int end = (length == Integer.MAX_VALUE) ? len : start + length;\n    return substring(start, end);\n  }\n\n  /**\n   * Returns whether this contains `substring` or not.\n   */\n  public boolean contains(final UTF8String substring) {\n    if (substring.numBytes == 0) {\n      return true;\n    }\n\n    byte first = substring.getByte(0);\n    for (int i = 0; i <= numBytes - substring.numBytes; i++) {\n      if (getByte(i) == first && matchAt(substring, i)) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  /**\n   * Returns the byte at position `i`.\n   */\n  private byte getByte(int i) {\n    return Platform.getByte(base, offset + i);\n  }\n\n  private boolean matchAt(final UTF8String s, int pos) {\n    if (s.numBytes + pos > numBytes || pos < 0) {\n      return false;\n    }\n    return ByteArrayMethods.arrayEquals(base, offset + pos, s.base, s.offset, s.numBytes);\n  }\n\n  public boolean startsWith(final UTF8String prefix) {\n    return matchAt(prefix, 0);\n  }\n\n  public boolean endsWith(final UTF8String suffix) {\n    return matchAt(suffix, numBytes - suffix.numBytes);\n  }\n\n  /**\n   * Returns the upper case of this string\n   */\n  public UTF8String toUpperCase() {\n    if (numBytes == 0) {\n      return EMPTY_UTF8;\n    }\n\n    byte[] bytes = new byte[numBytes];\n    bytes[0] = (byte) Character.toTitleCase(getByte(0));\n    for (int i = 0; i < numBytes; i++) {\n      byte b = getByte(i);\n      if (numBytesForFirstByte(b) != 1) {\n        // fallback\n        return toUpperCaseSlow();\n      }\n      int upper = Character.toUpperCase((int) b);\n      if (upper > 127) {\n        // fallback\n        return toUpperCaseSlow();\n      }\n      bytes[i] = (byte) upper;\n    }\n    return fromBytes(bytes);\n  }\n\n  private UTF8String toUpperCaseSlow() {\n    return fromString(toString().toUpperCase());\n  }\n\n  /**\n   * Returns the lower case of this string\n   */\n  public UTF8String toLowerCase() {\n    if (numBytes == 0) {\n      return EMPTY_UTF8;\n    }\n\n    byte[] bytes = new byte[numBytes];\n    bytes[0] = (byte) Character.toTitleCase(getByte(0));\n    for (int i = 0; i < numBytes; i++) {\n      byte b = getByte(i);\n      if (numBytesForFirstByte(b) != 1) {\n        // fallback\n        return toLowerCaseSlow();\n      }\n      int lower = Character.toLowerCase((int) b);\n      if (lower > 127) {\n        // fallback\n        return toLowerCaseSlow();\n      }\n      bytes[i] = (byte) lower;\n    }\n    return fromBytes(bytes);\n  }\n\n  private UTF8String toLowerCaseSlow() {\n    return fromString(toString().toLowerCase());\n  }\n\n  /**\n   * Returns the title case of this string, that could be used as title.\n   */\n  public UTF8String toTitleCase() {\n    if (numBytes == 0) {\n      return EMPTY_UTF8;\n    }\n\n    byte[] bytes = new byte[numBytes];\n    for (int i = 0; i < numBytes; i++) {\n      byte b = getByte(i);\n      if (i == 0 || getByte(i - 1) == ' ') {\n        if (numBytesForFirstByte(b) != 1) {\n          // fallback\n          return toTitleCaseSlow();\n        }\n        int upper = Character.toTitleCase(b);\n        if (upper > 127) {\n          // fallback\n          return toTitleCaseSlow();\n        }\n        bytes[i] = (byte) upper;\n      } else {\n        bytes[i] = b;\n      }\n    }\n    return fromBytes(bytes);\n  }\n\n  private UTF8String toTitleCaseSlow() {\n    StringBuffer sb = new StringBuffer();\n    String s = toString();\n    sb.append(s);\n    sb.setCharAt(0, Character.toTitleCase(sb.charAt(0)));\n    for (int i = 1; i < s.length(); i++) {\n      if (sb.charAt(i - 1) == ' ') {\n        sb.setCharAt(i, Character.toTitleCase(sb.charAt(i)));\n      }\n    }\n    return fromString(sb.toString());\n  }\n\n  /*\n   * Returns the index of the string `match` in this String. This string has to be a comma separated\n   * list. If `match` contains a comma 0 will be returned. If the `match` isn't part of this String,\n   * 0 will be returned, else the index of match (1-based index)\n   */\n  public int findInSet(UTF8String match) {\n    if (match.contains(COMMA_UTF8)) {\n      return 0;\n    }\n\n    int n = 1, lastComma = -1;\n    for (int i = 0; i < numBytes; i++) {\n      if (getByte(i) == (byte) ',') {\n        if (i - (lastComma + 1) == match.numBytes &&\n          ByteArrayMethods.arrayEquals(base, offset + (lastComma + 1), match.base, match.offset,\n            match.numBytes)) {\n          return n;\n        }\n        lastComma = i;\n        n++;\n      }\n    }\n    if (numBytes - (lastComma + 1) == match.numBytes &&\n      ByteArrayMethods.arrayEquals(base, offset + (lastComma + 1), match.base, match.offset,\n        match.numBytes)) {\n      return n;\n    }\n    return 0;\n  }\n\n  /**\n   * Copy the bytes from the current UTF8String, and make a new UTF8String.\n   * @param start the start position of the current UTF8String in bytes.\n   * @param end the end position of the current UTF8String in bytes.\n   * @return a new UTF8String in the position of [start, end] of current UTF8String bytes.\n   */\n  private UTF8String copyUTF8String(int start, int end) {\n    int len = end - start + 1;\n    byte[] newBytes = new byte[len];\n    Platform.copyMemory(base, offset + start, newBytes, Platform.BYTE_ARRAY_OFFSET, len);\n    return UTF8String.fromBytes(newBytes);\n  }\n\n  public UTF8String trim() {\n    int s = 0;\n    int e = this.numBytes - 1;\n    // skip all of the space (0x20) in the left side\n    while (s < this.numBytes && getByte(s) <= 0x20 && getByte(s) >= 0x00) s++;\n    // skip all of the space (0x20) in the right side\n    while (e >= 0 && getByte(e) <= 0x20 && getByte(e) >= 0x00) e--;\n    if (s > e) {\n      // empty string\n      return UTF8String.fromBytes(new byte[0]);\n    } else {\n      return copyUTF8String(s, e);\n    }\n  }\n\n  public UTF8String trimLeft() {\n    int s = 0;\n    // skip all of the space (0x20) in the left side\n    while (s < this.numBytes && getByte(s) <= 0x20 && getByte(s) >= 0x00) s++;\n    if (s == this.numBytes) {\n      // empty string\n      return UTF8String.fromBytes(new byte[0]);\n    } else {\n      return copyUTF8String(s, this.numBytes - 1);\n    }\n  }\n\n  public UTF8String trimRight() {\n    int e = numBytes - 1;\n    // skip all of the space (0x20) in the right side\n    while (e >= 0 && getByte(e) <= 0x20 && getByte(e) >= 0x00) e--;\n\n    if (e < 0) {\n      // empty string\n      return UTF8String.fromBytes(new byte[0]);\n    } else {\n      return copyUTF8String(0, e);\n    }\n  }\n\n  public UTF8String reverse() {\n    byte[] result = new byte[this.numBytes];\n\n    int i = 0; // position in byte\n    while (i < numBytes) {\n      int len = numBytesForFirstByte(getByte(i));\n      Platform.copyMemory(this.base, this.offset + i, result,\n        Platform.BYTE_ARRAY_OFFSET + result.length - i - len, len);\n\n      i += len;\n    }\n\n    return UTF8String.fromBytes(result);\n  }\n\n  public UTF8String repeat(int times) {\n    if (times <= 0) {\n      return EMPTY_UTF8;\n    }\n\n    byte[] newBytes = new byte[numBytes * times];\n    Platform.copyMemory(this.base, this.offset, newBytes, Platform.BYTE_ARRAY_OFFSET, numBytes);\n\n    int copied = 1;\n    while (copied < times) {\n      int toCopy = Math.min(copied, times - copied);\n      System.arraycopy(newBytes, 0, newBytes, copied * numBytes, numBytes * toCopy);\n      copied += toCopy;\n    }\n\n    return UTF8String.fromBytes(newBytes);\n  }\n\n  /**\n   * Returns the position of the first occurrence of substr in\n   * current string from the specified position (0-based index).\n   *\n   * @param v the string to be searched\n   * @param start the start position of the current string for searching\n   * @return the position of the first occurrence of substr, if not found, -1 returned.\n   */\n  public int indexOf(UTF8String v, int start) {\n    if (v.numBytes() == 0) {\n      return 0;\n    }\n\n    // locate to the start position.\n    int i = 0; // position in byte\n    int c = 0; // position in character\n    while (i < numBytes && c < start) {\n      i += numBytesForFirstByte(getByte(i));\n      c += 1;\n    }\n\n    do {\n      if (i + v.numBytes > numBytes) {\n        return -1;\n      }\n      if (ByteArrayMethods.arrayEquals(base, offset + i, v.base, v.offset, v.numBytes)) {\n        return c;\n      }\n      i += numBytesForFirstByte(getByte(i));\n      c += 1;\n    } while (i < numBytes);\n\n    return -1;\n  }\n\n  /**\n   * Find the `str` from left to right.\n   */\n  private int find(UTF8String str, int start) {\n    assert (str.numBytes > 0);\n    while (start <= numBytes - str.numBytes) {\n      if (ByteArrayMethods.arrayEquals(base, offset + start, str.base, str.offset, str.numBytes)) {\n        return start;\n      }\n      start += 1;\n    }\n    return -1;\n  }\n\n  /**\n   * Find the `str` from right to left.\n   */\n  private int rfind(UTF8String str, int start) {\n    assert (str.numBytes > 0);\n    while (start >= 0) {\n      if (ByteArrayMethods.arrayEquals(base, offset + start, str.base, str.offset, str.numBytes)) {\n        return start;\n      }\n      start -= 1;\n    }\n    return -1;\n  }\n\n  /**\n   * Returns the substring from string str before count occurrences of the delimiter delim.\n   * If count is positive, everything the left of the final delimiter (counting from left) is\n   * returned. If count is negative, every to the right of the final delimiter (counting from the\n   * right) is returned. subStringIndex performs a case-sensitive match when searching for delim.\n   */\n  public UTF8String subStringIndex(UTF8String delim, int count) {\n    if (delim.numBytes == 0 || count == 0) {\n      return EMPTY_UTF8;\n    }\n    if (count > 0) {\n      int idx = -1;\n      while (count > 0) {\n        idx = find(delim, idx + 1);\n        if (idx >= 0) {\n          count --;\n        } else {\n          // can not find enough delim\n          return this;\n        }\n      }\n      if (idx == 0) {\n        return EMPTY_UTF8;\n      }\n      byte[] bytes = new byte[idx];\n      Platform.copyMemory(base, offset, bytes, Platform.BYTE_ARRAY_OFFSET, idx);\n      return fromBytes(bytes);\n\n    } else {\n      int idx = numBytes - delim.numBytes + 1;\n      count = -count;\n      while (count > 0) {\n        idx = rfind(delim, idx - 1);\n        if (idx >= 0) {\n          count --;\n        } else {\n          // can not find enough delim\n          return this;\n        }\n      }\n      if (idx + delim.numBytes == numBytes) {\n        return EMPTY_UTF8;\n      }\n      int size = numBytes - delim.numBytes - idx;\n      byte[] bytes = new byte[size];\n      Platform.copyMemory(base, offset + idx + delim.numBytes, bytes, Platform.BYTE_ARRAY_OFFSET, size);\n      return fromBytes(bytes);\n    }\n  }\n\n  /**\n   * Returns str, right-padded with pad to a length of len\n   * For example:\n   *   ('hi', 5, '??') =&gt; 'hi???'\n   *   ('hi', 1, '??') =&gt; 'h'\n   */\n  public UTF8String rpad(int len, UTF8String pad) {\n    int spaces = len - this.numChars(); // number of char need to pad\n    if (spaces <= 0 || pad.numBytes() == 0) {\n      // no padding at all, return the substring of the current string\n      return substring(0, len);\n    } else {\n      int padChars = pad.numChars();\n      int count = spaces / padChars; // how many padding string needed\n      // the partial string of the padding\n      UTF8String remain = pad.substring(0, spaces - padChars * count);\n\n      byte[] data = new byte[this.numBytes + pad.numBytes * count + remain.numBytes];\n      Platform.copyMemory(this.base, this.offset, data, Platform.BYTE_ARRAY_OFFSET, this.numBytes);\n      int offset = this.numBytes;\n      int idx = 0;\n      while (idx < count) {\n        Platform.copyMemory(pad.base, pad.offset, data, Platform.BYTE_ARRAY_OFFSET + offset, pad.numBytes);\n        ++ idx;\n        offset += pad.numBytes;\n      }\n      Platform.copyMemory(remain.base, remain.offset, data, Platform.BYTE_ARRAY_OFFSET + offset, remain.numBytes);\n\n      return UTF8String.fromBytes(data);\n    }\n  }\n\n  /**\n   * Returns str, left-padded with pad to a length of len.\n   * For example:\n   *   ('hi', 5, '??') =&gt; '???hi'\n   *   ('hi', 1, '??') =&gt; 'h'\n   */\n  public UTF8String lpad(int len, UTF8String pad) {\n    int spaces = len - this.numChars(); // number of char need to pad\n    if (spaces <= 0 || pad.numBytes() == 0) {\n      // no padding at all, return the substring of the current string\n      return substring(0, len);\n    } else {\n      int padChars = pad.numChars();\n      int count = spaces / padChars; // how many padding string needed\n      // the partial string of the padding\n      UTF8String remain = pad.substring(0, spaces - padChars * count);\n\n      byte[] data = new byte[this.numBytes + pad.numBytes * count + remain.numBytes];\n\n      int offset = 0;\n      int idx = 0;\n      while (idx < count) {\n        Platform.copyMemory(pad.base, pad.offset, data, Platform.BYTE_ARRAY_OFFSET + offset, pad.numBytes);\n        ++ idx;\n        offset += pad.numBytes;\n      }\n      Platform.copyMemory(remain.base, remain.offset, data, Platform.BYTE_ARRAY_OFFSET + offset, remain.numBytes);\n      offset += remain.numBytes;\n      Platform.copyMemory(this.base, this.offset, data, Platform.BYTE_ARRAY_OFFSET + offset, numBytes());\n\n      return UTF8String.fromBytes(data);\n    }\n  }\n\n  /**\n   * Concatenates input strings together into a single string. Returns null if any input is null.\n   */\n  public static UTF8String concat(UTF8String... inputs) {\n    // Compute the total length of the result.\n    int totalLength = 0;\n    for (int i = 0; i < inputs.length; i++) {\n      if (inputs[i] != null) {\n        totalLength += inputs[i].numBytes;\n      } else {\n        return null;\n      }\n    }\n\n    // Allocate a new byte array, and copy the inputs one by one into it.\n    final byte[] result = new byte[totalLength];\n    int offset = 0;\n    for (int i = 0; i < inputs.length; i++) {\n      int len = inputs[i].numBytes;\n      Platform.copyMemory(\n        inputs[i].base, inputs[i].offset,\n        result, Platform.BYTE_ARRAY_OFFSET + offset,\n        len);\n      offset += len;\n    }\n    return fromBytes(result);\n  }\n\n  /**\n   * Concatenates input strings together into a single string using the separator.\n   * A null input is skipped. For example, concat(\",\", \"a\", null, \"c\") would yield \"a,c\".\n   */\n  public static UTF8String concatWs(UTF8String separator, UTF8String... inputs) {\n    if (separator == null) {\n      return null;\n    }\n\n    int numInputBytes = 0;  // total number of bytes from the inputs\n    int numInputs = 0;      // number of non-null inputs\n    for (int i = 0; i < inputs.length; i++) {\n      if (inputs[i] != null) {\n        numInputBytes += inputs[i].numBytes;\n        numInputs++;\n      }\n    }\n\n    if (numInputs == 0) {\n      // Return an empty string if there is no input, or all the inputs are null.\n      return fromBytes(new byte[0]);\n    }\n\n    // Allocate a new byte array, and copy the inputs one by one into it.\n    // The size of the new array is the size of all inputs, plus the separators.\n    final byte[] result = new byte[numInputBytes + (numInputs - 1) * separator.numBytes];\n    int offset = 0;\n\n    for (int i = 0, j = 0; i < inputs.length; i++) {\n      if (inputs[i] != null) {\n        int len = inputs[i].numBytes;\n        Platform.copyMemory(\n          inputs[i].base, inputs[i].offset,\n          result, Platform.BYTE_ARRAY_OFFSET + offset,\n          len);\n        offset += len;\n\n        j++;\n        // Add separator if this is not the last input.\n        if (j < numInputs) {\n          Platform.copyMemory(\n            separator.base, separator.offset,\n            result, Platform.BYTE_ARRAY_OFFSET + offset,\n            separator.numBytes);\n          offset += separator.numBytes;\n        }\n      }\n    }\n    return fromBytes(result);\n  }\n\n  public UTF8String[] split(UTF8String pattern, int limit) {\n    String[] splits = toString().split(pattern.toString(), limit);\n    UTF8String[] res = new UTF8String[splits.length];\n    for (int i = 0; i < res.length; i++) {\n      res[i] = fromString(splits[i]);\n    }\n    return res;\n  }\n\n  // TODO: Need to use `Code Point` here instead of Char in case the character longer than 2 bytes\n  public UTF8String translate(Map<Character, Character> dict) {\n    String srcStr = this.toString();\n\n    StringBuilder sb = new StringBuilder();\n    for(int k = 0; k< srcStr.length(); k++) {\n      if (null == dict.get(srcStr.charAt(k))) {\n        sb.append(srcStr.charAt(k));\n      } else if ('\\0' != dict.get(srcStr.charAt(k))){\n        sb.append(dict.get(srcStr.charAt(k)));\n      }\n    }\n    return fromString(sb.toString());\n  }\n\n  @Override\n  public String toString() {\n    return new String(getBytes(), StandardCharsets.UTF_8);\n  }\n\n  @Override\n  public UTF8String clone() {\n    return fromBytes(getBytes());\n  }\n\n  @Override\n  public int compareTo(@Nonnull final UTF8String other) {\n    int len = Math.min(numBytes, other.numBytes);\n    // TODO: compare 8 bytes as unsigned long\n    for (int i = 0; i < len; i ++) {\n      // In UTF-8, the byte should be unsigned, so we should compare them as unsigned int.\n      int res = (getByte(i) & 0xFF) - (other.getByte(i) & 0xFF);\n      if (res != 0) {\n        return res;\n      }\n    }\n    return numBytes - other.numBytes;\n  }\n\n  public int compare(final UTF8String other) {\n    return compareTo(other);\n  }\n\n  @Override\n  public boolean equals(final Object other) {\n    if (other instanceof UTF8String) {\n      UTF8String o = (UTF8String) other;\n      if (numBytes != o.numBytes) {\n        return false;\n      }\n      return ByteArrayMethods.arrayEquals(base, offset, o.base, o.offset, numBytes);\n    } else {\n      return false;\n    }\n  }\n\n  /**\n   * Levenshtein distance is a metric for measuring the distance of two strings. The distance is\n   * defined by the minimum number of single-character edits (i.e. insertions, deletions or\n   * substitutions) that are required to change one of the strings into the other.\n   */\n  public int levenshteinDistance(UTF8String other) {\n    // Implementation adopted from org.apache.common.lang3.StringUtils.getLevenshteinDistance\n\n    int n = numChars();\n    int m = other.numChars();\n\n    if (n == 0) {\n      return m;\n    } else if (m == 0) {\n      return n;\n    }\n\n    UTF8String s, t;\n\n    if (n <= m) {\n      s = this;\n      t = other;\n    } else {\n      s = other;\n      t = this;\n      int swap;\n      swap = n;\n      n = m;\n      m = swap;\n    }\n\n    int[] p = new int[n + 1];\n    int[] d = new int[n + 1];\n    int[] swap;\n\n    int i, i_bytes, j, j_bytes, num_bytes_j, cost;\n\n    for (i = 0; i <= n; i++) {\n      p[i] = i;\n    }\n\n    for (j = 0, j_bytes = 0; j < m; j_bytes += num_bytes_j, j++) {\n      num_bytes_j = numBytesForFirstByte(t.getByte(j_bytes));\n      d[0] = j + 1;\n\n      for (i = 0, i_bytes = 0; i < n; i_bytes += numBytesForFirstByte(s.getByte(i_bytes)), i++) {\n        if (s.getByte(i_bytes) != t.getByte(j_bytes) ||\n              num_bytes_j != numBytesForFirstByte(s.getByte(i_bytes))) {\n          cost = 1;\n        } else {\n          cost = (ByteArrayMethods.arrayEquals(t.base, t.offset + j_bytes, s.base,\n              s.offset + i_bytes, num_bytes_j)) ? 0 : 1;\n        }\n        d[i + 1] = Math.min(Math.min(d[i] + 1, p[i + 1] + 1), p[i] + cost);\n      }\n\n      swap = p;\n      p = d;\n      d = swap;\n    }\n\n    return p[n];\n  }\n\n  @Override\n  public int hashCode() {\n    return Murmur3_x86_32.hashUnsafeBytes(base, offset, numBytes, 42);\n  }\n\n  /**\n   * Soundex mapping table\n   */\n  private static final byte[] US_ENGLISH_MAPPING = {'0', '1', '2', '3', '0', '1', '2', '7',\n    '0', '2', '2', '4', '5', '5', '0', '1', '2', '6', '2', '3', '0', '1', '7', '2', '0', '2'};\n\n  /**\n   * Encodes a string into a Soundex value. Soundex is an encoding used to relate similar names,\n   * but can also be used as a general purpose scheme to find word with similar phonemes.\n   * https://en.wikipedia.org/wiki/Soundex\n   */\n  public UTF8String soundex() {\n    if (numBytes == 0) {\n      return EMPTY_UTF8;\n    }\n\n    byte b = getByte(0);\n    if ('a' <= b && b <= 'z') {\n      b -= 32;\n    } else if (b < 'A' || 'Z' < b) {\n      // first character must be a letter\n      return this;\n    }\n    byte[] sx = {'0', '0', '0', '0'};\n    sx[0] = b;\n    int sxi = 1;\n    int idx = b - 'A';\n    byte lastCode = US_ENGLISH_MAPPING[idx];\n\n    for (int i = 1; i < numBytes; i++) {\n      b = getByte(i);\n      if ('a' <= b && b <= 'z') {\n        b -= 32;\n      } else if (b < 'A' || 'Z' < b) {\n        // not a letter, skip it\n        lastCode = '0';\n        continue;\n      }\n      idx = b - 'A';\n      byte code = US_ENGLISH_MAPPING[idx];\n      if (code == '7') {\n        // ignore it\n      } else {\n        if (code != '0' && code != lastCode) {\n          sx[sxi++] = code;\n          if (sxi > 3) break;\n        }\n        lastCode = code;\n      }\n    }\n    return UTF8String.fromBytes(sx);\n  }\n\n  public void writeExternal(ObjectOutput out) throws IOException {\n    byte[] bytes = getBytes();\n    out.writeInt(bytes.length);\n    out.write(bytes);\n  }\n\n  public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {\n    offset = Platform.BYTE_ARRAY_OFFSET;\n    numBytes = in.readInt();\n    base = new byte[numBytes];\n    in.readFully((byte[]) base);\n  }\n\n  @Override\n  public void write(Kryo kryo, Output out) {\n    byte[] bytes = getBytes();\n    out.writeInt(bytes.length);\n    out.write(bytes);\n  }\n\n  @Override\n  public void read(Kryo kryo, Input in) {\n    this.offset = Platform.BYTE_ARRAY_OFFSET;\n    this.numBytes = in.readInt();\n    this.base = new byte[numBytes];\n    in.read((byte[]) base);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/ByteUnit.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.mycat.memory.unsafe.utils;\n\npublic enum ByteUnit {\n  BYTE (1),\n  KiB (1024L),\n  MiB ((long) Math.pow(1024L, 2L)),\n  GiB ((long) Math.pow(1024L, 3L)),\n  TiB ((long) Math.pow(1024L, 4L)),\n  PiB ((long) Math.pow(1024L, 5L)), ;\n\n  ByteUnit(long multiplier) {\n    this.multiplier = multiplier;\n  }\n\n  // Interpret the provided number (d) with suffix (u) as this unit type.\n  // E.g. KiB.interpret(1, MiB) interprets 1MiB as its KiB representation = 1024k\n  public long convertFrom(long d, ByteUnit u) {\n    return u.convertTo(d, this);\n  }\n\n  // Convert the provided number (d) interpreted as this unit type to unit type (u).\n  public long convertTo(long d, ByteUnit u) {\n    if (multiplier > u.multiplier) {\n      long ratio = multiplier / u.multiplier;\n      if (Long.MAX_VALUE / ratio < d) {\n        throw new IllegalArgumentException(\"Conversion of \" + d + \" exceeds Long.MAX_VALUE in \"\n          + name() + \". Try a larger unit (e.g. MiB instead of KiB)\");\n      }\n      return d * ratio;\n    } else {\n      // Perform operations in this order to avoid potential overflow\n      // when computing d * multiplier\n      return d / (u.multiplier / multiplier);\n    }\n  }\n\n  public double toBytes(long d) {\n    if (d < 0) {\n      throw new IllegalArgumentException(\"Negative size value. Size must be positive: \" + d);\n    }\n    return d * multiplier;\n  }\n\n  public long toKiB(long d) { return convertTo(d, KiB); }\n  public long toMiB(long d) { return convertTo(d, MiB); }\n  public long toGiB(long d) { return convertTo(d, GiB); }\n  public long toTiB(long d) { return convertTo(d, TiB); }\n  public long toPiB(long d) { return convertTo(d, PiB); }\n\n  private final long multiplier;\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/BytesTools.java",
    "content": "package io.mycat.memory.unsafe.utils;\n\n/**\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\nimport com.google.common.annotations.VisibleForTesting;\n\nimport io.mycat.memory.unsafe.Platform;\nimport sun.misc.Unsafe;\n\nimport java.io.UnsupportedEncodingException;\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\nimport java.nio.charset.IllegalCharsetNameException;\nimport java.nio.charset.UnsupportedCharsetException;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Utility class that handles byte arrays, conversions to/from other types,\n */\n@SuppressWarnings(\"restriction\")\npublic class BytesTools {\n\n    //HConstants.UTF8_ENCODING should be updated if this changed\n    /** When we encode strings, we always specify UTF8 encoding */\n    private static final String UTF8_ENCODING = \"UTF-8\";\n\n    //HConstants.UTF8_CHARSET should be updated if this changed\n     /** When we encode strings, we always specify UTF8 encoding */\n    private static final Charset UTF8_CHARSET = Charset.forName(UTF8_ENCODING);\n\n    /**\n     * Size of boolean in bytes\n     */\n    public static final int SIZEOF_BOOLEAN = Byte.SIZE / Byte.SIZE;\n\n    /**\n     * Size of byte in bytes\n     */\n    public static final int SIZEOF_BYTE = SIZEOF_BOOLEAN;\n\n    /**\n     * Size of char in bytes\n     */\n    public static final int SIZEOF_CHAR = Character.SIZE / Byte.SIZE;\n\n    /**\n     * Size of double in bytes\n     */\n    public static final int SIZEOF_DOUBLE = Double.SIZE / Byte.SIZE;\n\n    /**\n     * Size of float in bytes\n     */\n    public static final int SIZEOF_FLOAT = Float.SIZE / Byte.SIZE;\n\n    /**\n     * Size of int in bytes\n     */\n    public static final int SIZEOF_INT = Integer.SIZE / Byte.SIZE;\n\n    /**\n     * Size of long in bytes\n     */\n    public static final int SIZEOF_LONG = Long.SIZE / Byte.SIZE;\n\n    /**\n     * Size of short in bytes\n     */\n    public static final int SIZEOF_SHORT = Short.SIZE / Byte.SIZE;\n\n    /**\n     * Convert a byte array  to a int value\n     * @param buf\n     * @return int\n     * @throws NumberFormatException\n     */\n\n    public static int getInt(byte[] buf) throws NumberFormatException {\n        return getInt(buf, 0, buf.length);\n    }\n\n    public static int getInt(byte[] buf, int offset, int endPos) throws NumberFormatException {\n        byte base = 10;\n\n        int s;\n        for(s = offset; s < endPos && Character.isWhitespace((char)buf[s]); ++s) {\n            ;\n        }\n        if(s == endPos) {\n            throw new NumberFormatException(toString(buf));\n        } else {\n            boolean negative = false;\n            if((char)buf[s] == 45) {\n                negative = true;\n                ++s;\n            } else if((char)buf[s] == 43) {\n                ++s;\n            }\n\n            int save = s;\n            int cutoff = 2147483647 / base;\n            int cutlim = 2147483647 % base;\n            if(negative) {\n                ++cutlim;\n            }\n\n            boolean overflow = false;\n\n            int i;\n            for(i = 0; s < endPos; ++s) {\n                char c = (char)buf[s];\n                if(Character.isDigit(c)) {\n                    c = (char)(c - 48);\n                } else {\n                    if(!Character.isLetter(c)) {\n                        break;\n                    }\n\n                    c = (char)(Character.toUpperCase(c) - 65 + 10);\n                }\n\n                if(c >= base) {\n                    break;\n                }\n\n                if(i <= cutoff && (i != cutoff || c <= cutlim)) {\n                    i *= base;\n                    i += c;\n                } else {\n                    overflow = true;\n                }\n            }\n\n            if(s == save) {\n                throw new NumberFormatException(toString(buf));\n            } else if(overflow) {\n                throw new NumberFormatException(toString(buf));\n            } else {\n                return negative?-i:i;\n            }\n        }\n    }\n\n    /**\n     * Convert a byte array to a long value\n     * @param buf\n     * @return\n     * @throws NumberFormatException\n     */\n    public static long getLong(byte[] buf) throws NumberFormatException {\n        return getLong(buf, 0, buf.length);\n    }\n\n    public static long getLong(byte[] buf, int offset, int endpos) throws NumberFormatException {\n        byte base = 10;\n\n        int s;\n        for(s = offset; s < endpos && Character.isWhitespace((char)buf[s]); ++s) {\n            ;\n        }\n\n        if(s == endpos) {\n            throw new NumberFormatException(toString(buf));\n        } else {\n            boolean negative = false;\n            if((char)buf[s] == 45) {\n                negative = true;\n                ++s;\n            } else if((char)buf[s] == 43) {\n                ++s;\n            }\n\n            int save = s;\n            long cutoff = 9223372036854775807L / (long)base;\n            long cutlim = (long)((int)(9223372036854775807L % (long)base));\n            if(negative) {\n                ++cutlim;\n            }\n\n            boolean overflow = false;\n\n            long i;\n            for(i = 0L; s < endpos; ++s) {\n                char c = (char)buf[s];\n                if(Character.isDigit(c)) {\n                    c = (char)(c - 48);\n                } else {\n                    if(!Character.isLetter(c)) {\n                        break;\n                    }\n                    c = (char)(Character.toUpperCase(c) - 65 + 10);\n                }\n\n                if(c >= base) {\n                    break;\n                }\n\n                if(i <= cutoff && (i != cutoff || (long)c <= cutlim)) {\n                    i *= (long)base;\n                    i += (long)c;\n                } else {\n                    overflow = true;\n                }\n            }\n\n            if(s == save) {\n                throw new NumberFormatException(toString(buf));\n            } else if(overflow) {\n                throw new NumberFormatException(toString(buf));\n            } else {\n                return negative?-i:i;\n            }\n        }\n    }\n\n    /**\n     * Convert a byte array  to a short value\n     * @param buf\n     * @return\n     * @throws NumberFormatException\n     */\n    public static short getShort(byte[] buf) throws NumberFormatException {\n        return getShort(buf, 0, buf.length);\n    }\n\n    public static short getShort(byte[] buf, int offset, int endpos) throws NumberFormatException {\n        byte base = 10;\n\n        int s;\n        for(s = offset; s < endpos && Character.isWhitespace((char)buf[s]); ++s) {\n            ;\n        }\n\n        if(s == endpos) {\n            throw new NumberFormatException(toString(buf));\n        } else {\n            boolean negative = false;\n            if((char)buf[s] == 45) {\n                negative = true;\n                ++s;\n            } else if((char)buf[s] == 43) {\n                ++s;\n            }\n\n            int save = s;\n            short cutoff = (short)(32767 / base);\n            short cutlim = (short)(32767 % base);\n            if(negative) {\n                ++cutlim;\n            }\n\n            boolean overflow = false;\n\n            short i;\n            for(i = 0; s < endpos; ++s) {\n                char c = (char)buf[s];\n                if(Character.isDigit(c)) {\n                    c = (char)(c - 48);\n                } else {\n                    if(!Character.isLetter(c)) {\n                        break;\n                    }\n\n                    c = (char)(Character.toUpperCase(c) - 65 + 10);\n                }\n\n                if(c >= base) {\n                    break;\n                }\n\n                if(i <= cutoff && (i != cutoff || c <= cutlim)) {\n                    i = (short)(i * base);\n                    i = (short)(i + c);\n                } else {\n                    overflow = true;\n                }\n            }\n\n            if(s == save) {\n                throw new NumberFormatException(toString(buf));\n            } else if(overflow) {\n                throw new NumberFormatException(toString(buf));\n            } else {\n                return negative?(short)(-i):i;\n            }\n        }\n    }\n\n    /**\n     *  Convert a byte array  to a float value\n     * @param src\n     * @return\n     * @throws UnsupportedEncodingException\n     */\n    public static float getFloat(byte [] src) throws UnsupportedEncodingException {\n        return Float.parseFloat(new String(src,\"US-ASCII\"));\n    }\n\n    /**\n     * Convert a byte array  to a double value\n     * @param src\n     * @return\n     * @throws UnsupportedEncodingException\n     */\n\n    public static double getDouble(byte [] src) throws UnsupportedEncodingException {\n        return  Double.parseDouble(new String(src,\"US-ASCII\"));\n    }\n\n    /**\n     * Convert a long value to a byte array\n     * @param l\n     * @return\n     * @throws UnsupportedEncodingException\n     */\n\n\n    public static byte[] long2Bytes(long l) throws UnsupportedEncodingException {\n        String lstr = Long.toString(l);\n        return lstr.getBytes(\"US-ASCII\");\n    }\n\n    /**\n     * Convert a int value to a byte array\n     * @param i\n     * @return\n     * @throws UnsupportedEncodingException\n     */\n\n    public static byte[] int2Bytes(int i) throws UnsupportedEncodingException {\n        String istr = Integer.toString(i);\n        return istr.getBytes(\"US-ASCII\");\n    }\n\n    /**\n     * Convert a short value to a byte array\n     * @param i\n     * @return\n     * @throws UnsupportedEncodingException\n     */\n\n    public static byte[] short2Bytes(short i) throws UnsupportedEncodingException {\n        String sstr = Short.toString(i);\n        return sstr.getBytes(\"US-ASCII\");\n    }\n\n    /**\n     * Convert a float value to a byte array\n     * @param f\n     * @return\n     * @throws UnsupportedEncodingException\n     */\n    public static byte[] float2Bytes(float f) throws UnsupportedEncodingException {\n        String fstr = Float.toString(f);\n        return fstr.getBytes(\"US-ASCII\");\n    }\n\n    /**\n     * Convert a double value to a byte array\n     * @param d\n     * @return\n     * @throws UnsupportedEncodingException\n     */\n    public static byte[] double2Bytes(double d) throws UnsupportedEncodingException {\n        String dstr = Double.toString(d);\n        return dstr.getBytes(\"US-ASCII\");\n    }\n\n    /**\n     * Returns a new byte array, copied from the given {@code buf},\n     * from the index 0 (inclusive) to the limit (exclusive),\n     * regardless of the current position.\n     * The position and the other index parameters are not changed.\n     *\n     * @param buf a byte buffer\n     * @return the byte array\n     */\n    public static byte[] toBytes(ByteBuffer buf) {\n        ByteBuffer dup = buf.duplicate();\n        dup.position(0);\n        return readBytes(dup);\n    }\n\n    private static byte[] readBytes(ByteBuffer buf) {\n        byte [] result = new byte[buf.remaining()];\n        buf.get(result);\n        return result;\n    }\n\n    /**\n     * @param b Presumed UTF-8 encoded byte array.\n     * @return String made from <code>b</code>\n     */\n    public static String toString(final byte [] b) {\n        if (b == null) {\n            return null;\n        }\n        return toString(b, 0, b.length);\n    }\n\n    /**\n     * Joins two byte arrays together using a separator.\n     * @param b1 The first byte array.\n     * @param sep The separator to use.\n     * @param b2 The second byte array.\n     */\n    public static String toString(final byte [] b1,\n                                  String sep,\n                                  final byte [] b2) {\n        return toString(b1, 0, b1.length) + sep + toString(b2, 0, b2.length);\n    }\n\n    /**\n     * This method will convert utf8 encoded bytes into a string. If\n     * the given byte array is null, this method will return null.\n     *\n     * @param b Presumed UTF-8 encoded byte array.\n     * @param off offset into array\n     * @return String made from <code>b</code> or null\n     */\n    public static String toString(final byte [] b, int off) {\n        if (b == null) {\n            return null;\n        }\n        int len = b.length - off;\n        if (len <= 0) {\n            return \"\";\n        }\n        return new String(b, off, len, UTF8_CHARSET);\n    }\n\n    /**\n     * This method will convert utf8 encoded bytes into a string. If\n     * the given byte array is null, this method will return null.\n     *\n     * @param b Presumed UTF-8 encoded byte array.\n     * @param off offset into array\n     * @param len length of utf-8 sequence\n     * @return String made from <code>b</code> or null\n     */\n    public static String toString(final byte [] b, int off, int len) {\n        if (b == null) {\n            return null;\n        }\n        if (len == 0) {\n            return \"\";\n        }\n        return new String(b, off, len, UTF8_CHARSET);\n    }\n\n    /**\n     * Write a printable representation of a byte array.\n     *\n     * @param b byte array\n     * @return string\n     * @see #toStringBinary(byte[], int, int)\n     */\n    public static String toStringBinary(final byte [] b) {\n        if (b == null)\n            return \"null\";\n        return toStringBinary(b, 0, b.length);\n    }\n\n    /**\n     * Converts the given byte buffer to a printable representation,\n     * from the index 0 (inclusive) to the limit (exclusive),\n     * regardless of the current position.\n     * The position and the other index parameters are not changed.\n     *\n     * @param buf a byte buffer\n     * @return a string representation of the buffer's binary contents\n     * @see #toBytes(ByteBuffer)\n     */\n    public static String toStringBinary(ByteBuffer buf) {\n        if (buf == null)\n            return \"null\";\n        if (buf.hasArray()) {\n            return toStringBinary(buf.array(), buf.arrayOffset(), buf.limit());\n        }\n        return toStringBinary(toBytes(buf));\n    }\n\n    private static final char[] HEX_CHARS_UPPER = {\n            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'\n    };\n\n    /**\n     * Write a printable representation of a byte array. Non-printable\n     * characters are hex escaped in the format \\\\x%02X, eg:\n     * \\x00 \\x05 etc\n     *\n     * @param b array to write out\n     * @param off offset to start at\n     * @param len length to write\n     * @return string output\n     */\n    public static String toStringBinary(final byte [] b, int off, int len) {\n        StringBuilder result = new StringBuilder();\n        // Just in case we are passed a 'len' that is > buffer length...\n        if (off >= b.length) return result.toString();\n        if (off + len > b.length) len = b.length - off;\n        for (int i = off; i < off + len ; ++i) {\n            int ch = b[i] & 0xFF;\n            if (ch >= ' ' && ch <= '~' && ch != '\\\\') {\n                result.append((char)ch);\n            } else {\n                result.append(\"\\\\x\");\n                result.append(HEX_CHARS_UPPER[ch / 0x10]);\n                result.append(HEX_CHARS_UPPER[ch % 0x10]);\n            }\n        }\n        return result.toString();\n    }\n\n    private static boolean isHexDigit(char c) {\n        return\n                (c >= 'A' && c <= 'F') ||\n                        (c >= '0' && c <= '9');\n    }\n\n    /**\n     * Takes a ASCII digit in the range A-F0-9 and returns\n     * the corresponding integer/ordinal value.\n     * @param ch  The hex digit.\n     * @return The converted hex value as a byte.\n     */\n    public static byte toBinaryFromHex(byte ch) {\n        if (ch >= 'A' && ch <= 'F')\n            return (byte) ((byte)10 + (byte) (ch - 'A'));\n        // else\n        return (byte) (ch - '0');\n    }\n\n    public static byte [] toBytesBinary(String in) {\n        // this may be bigger than we need, but let's be safe.\n        byte [] b = new byte[in.length()];\n        int size = 0;\n        for (int i = 0; i < in.length(); ++i) {\n            char ch = in.charAt(i);\n            if (ch == '\\\\' && in.length() > i+1 && in.charAt(i+1) == 'x') {\n                // ok, take next 2 hex digits.\n                char hd1 = in.charAt(i+2);\n                char hd2 = in.charAt(i+3);\n\n                // they need to be A-F0-9:\n                if (!isHexDigit(hd1) ||\n                        !isHexDigit(hd2)) {\n                    // bogus escape code, ignore:\n                    continue;\n                }\n                // turn hex ASCII digit -> number\n                byte d = (byte) ((toBinaryFromHex((byte)hd1) << 4) + toBinaryFromHex((byte)hd2));\n\n                b[size++] = d;\n                i += 3; // skip 3\n            } else {\n                b[size++] = (byte) ch;\n            }\n        }\n        // resize:\n        byte [] b2 = new byte[size];\n        System.arraycopy(b, 0, b2, 0, size);\n        return b2;\n    }\n\n    /**\n     * Converts a string to a UTF-8 byte array.\n     * @param s string\n     * @return the byte array\n     */\n    public static byte[] toBytes(String s) {\n        return s.getBytes(UTF8_CHARSET);\n    }\n\n    /**\n     * Convert a boolean to a byte array. True becomes -1\n     * and false becomes 0.\n     *\n     * @param b value\n     * @return <code>b</code> encoded in a byte array.\n     */\n    public static byte [] toBytes(final boolean b) {\n        return new byte[] { b ? (byte) -1 : (byte) 0 };\n    }\n\n    /**\n     * Reverses {@link #toBytes(boolean)}\n     * @param b array\n     * @return True or false.\n     */\n    public static boolean toBoolean(final byte [] b) {\n        if (b.length != 1) {\n            throw new IllegalArgumentException(\"Array has wrong size: \" + b.length);\n        }\n        return b[0] != (byte) 0;\n    }\n\n    /**\n     * Convert a long value to a byte array using big-endian.\n     *\n     * @param val value to convert\n     * @return the byte array\n     */\n    public static byte[] toBytes(long val) {\n        byte [] b = new byte[8];\n        for (int i = 7; i > 0; i--) {\n            b[i] = (byte) val;\n            val >>>= 8;\n        }\n        b[0] = (byte) val;\n        return b;\n    }\n\n    /**\n     * @param left left operand\n     * @param right right operand\n     * @return 0 if equal, &lt; 0 if left is less than right, etc.\n     */\n    public static int compareTo(final byte [] left, final byte [] right) {\n        return LexicographicalComparerHolder.BEST_COMPARER.\n                compareTo(left, 0, left.length, right, 0, right.length);\n    }\n\n    /**\n     * Lexicographically compare two arrays.\n     *\n     * @param buffer1 left operand\n     * @param buffer2 right operand\n     * @param offset1 Where to start comparing in the left buffer\n     * @param offset2 Where to start comparing in the right buffer\n     * @param length1 How much to compare from the left buffer\n     * @param length2 How much to compare from the right buffer\n     * @return 0 if equal, &lt; 0 if left is less than right, etc.\n     */\n    public static int compareTo(byte[] buffer1, int offset1, int length1,\n                                byte[] buffer2, int offset2, int length2) {\n        return LexicographicalComparerHolder.BEST_COMPARER.\n                compareTo(buffer1, offset1, length1, buffer2, offset2, length2);\n    }\n\n    interface Comparer<T> {\n        int compareTo(\n                T buffer1, int offset1, int length1, T buffer2, int offset2, int length2\n        );\n    }\n\n    @VisibleForTesting\n    static Comparer<byte[]> lexicographicalComparerJavaImpl() {\n        return LexicographicalComparerHolder.PureJavaComparer.INSTANCE;\n    }\n\n    /**\n     * Provides a lexicographical comparer implementation; either a Java\n     * implementation or a faster implementation based on {@link Unsafe}.\n     *\n     * <p>Uses reflection to gracefully fall back to the Java implementation if\n     * {@code Unsafe} isn't available.\n     */\n    @VisibleForTesting\n    static class LexicographicalComparerHolder {\n        static final String UNSAFE_COMPARER_NAME =\n                LexicographicalComparerHolder.class.getName() + \"$UnsafeComparer\";\n\n        static final Comparer<byte[]> BEST_COMPARER = getBestComparer();\n        /**\n         * Returns the Unsafe-using Comparer, or falls back to the pure-Java\n         * implementation if unable to do so.\n         */\n        static Comparer<byte[]> getBestComparer() {\n            try {\n                Class<?> theClass = Class.forName(UNSAFE_COMPARER_NAME);\n\n                // yes, UnsafeComparer does implement Comparer<byte[]>\n                @SuppressWarnings(\"unchecked\")\n                Comparer<byte[]> comparer =\n                        (Comparer<byte[]>) theClass.getEnumConstants()[0];\n                return comparer;\n            } catch (Throwable t) { // ensure we really catch *everything*\n                return lexicographicalComparerJavaImpl();\n            }\n        }\n\n        enum PureJavaComparer implements Comparer<byte[]> {\n            INSTANCE;\n\n            @Override\n            public int compareTo(byte[] buffer1, int offset1, int length1,\n                                 byte[] buffer2, int offset2, int length2) {\n                // Short circuit equal case\n                if (buffer1 == buffer2 &&\n                        offset1 == offset2 &&\n                        length1 == length2) {\n                    return 0;\n                }\n                // Bring WritableComparator code local\n                int end1 = offset1 + length1;\n                int end2 = offset2 + length2;\n                for (int i = offset1, j = offset2; i < end1 && j < end2; i++, j++) {\n                    int a = (buffer1[i] & 0xff);\n                    int b = (buffer2[j] & 0xff);\n                    if (a != b) {\n                        return a - b;\n                    }\n                }\n                return length1 - length2;\n            }\n        }\n    }\n\n    /**\n     * @param left left operand\n     * @param right right operand\n     * @return True if equal\n     */\n    public static boolean equals(final byte [] left, final byte [] right) {\n        // Could use Arrays.equals?\n        //noinspection SimplifiableConditionalExpression\n        if (left == right) return true;\n        if (left == null || right == null) return false;\n        if (left.length != right.length) return false;\n        if (left.length == 0) return true;\n\n        // Since we're often comparing adjacent sorted data,\n        // it's usual to have equal arrays except for the very last byte\n        // so check that first\n        if (left[left.length - 1] != right[right.length - 1]) return false;\n\n        return compareTo(left, right) == 0;\n    }\n\n    public static boolean equals(final byte[] left, int leftOffset, int leftLen,\n                                 final byte[] right, int rightOffset, int rightLen) {\n        // short circuit case\n        if (left == right &&\n                leftOffset == rightOffset &&\n                leftLen == rightLen) {\n            return true;\n        }\n        // different lengths fast check\n        if (leftLen != rightLen) {\n            return false;\n        }\n        if (leftLen == 0) {\n            return true;\n        }\n\n        // Since we're often comparing adjacent sorted data,\n        // it's usual to have equal arrays except for the very last byte\n        // so check that first\n        if (left[leftOffset + leftLen - 1] != right[rightOffset + rightLen - 1]) return false;\n\n        return LexicographicalComparerHolder.BEST_COMPARER.\n                compareTo(left, leftOffset, leftLen, right, rightOffset, rightLen) == 0;\n    }\n\n\n    /**\n     * @param a left operand\n     * @param buf right operand\n     * @return True if equal\n     */\n    public static boolean equals(byte[] a, ByteBuffer buf) {\n        if (a == null) return buf == null;\n        if (buf == null) return false;\n        if (a.length != buf.remaining()) return false;\n\n        // Thou shalt not modify the original byte buffer in what should be read only operations.\n        ByteBuffer b = buf.duplicate();\n        for (byte anA : a) {\n            if (anA != b.get()) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n\n    /**\n     * Return true if the byte array on the right is a prefix of the byte\n     * array on the left.\n     */\n    public static boolean startsWith(byte[] bytes, byte[] prefix) {\n        return bytes != null && prefix != null &&\n                bytes.length >= prefix.length &&\n                LexicographicalComparerHolder.BEST_COMPARER.\n                        compareTo(bytes, 0, prefix.length, prefix, 0, prefix.length) == 0;\n    }\n\n\n    /**\n     * @param a first third\n     * @param b second third\n     * @param c third third\n     * @return New array made from a, b and c\n     */\n    public static byte [] add(final byte [] a, final byte [] b, final byte [] c) {\n        byte [] result = new byte[a.length + b.length + c.length];\n        System.arraycopy(a, 0, result, 0, a.length);\n        System.arraycopy(b, 0, result, a.length, b.length);\n        System.arraycopy(c, 0, result, a.length + b.length, c.length);\n        return result;\n    }\n\n    /**\n     * @param arrays all the arrays to concatenate together.\n     * @return New array made from the concatenation of the given arrays.\n     */\n    public static byte [] add(final byte [][] arrays) {\n        int length = 0;\n        for (int i = 0; i < arrays.length; i++) {\n            length += arrays[i].length;\n        }\n        byte [] result = new byte[length];\n        int index = 0;\n        for (int i = 0; i < arrays.length; i++) {\n            System.arraycopy(arrays[i], 0, result, index, arrays[i].length);\n            index += arrays[i].length;\n        }\n        return result;\n    }\n\n    /**\n     * Split passed range.  Expensive operation relatively.  Uses BigInteger math.\n     * Useful splitting ranges for MapReduce jobs.\n     * @param a Beginning of range\n     * @param b End of range\n     * @param num Number of times to split range.  Pass 1 if you want to split\n     * the range in two; i.e. one split.\n     * @return Array of dividing values\n     */\n\n\n\n    /**\n     * @param t operands\n     * @return Array of byte arrays made from passed array of Text\n     */\n    public static byte [][] toByteArrays(final String [] t) {\n        byte [][] result = new byte[t.length][];\n        for (int i = 0; i < t.length; i++) {\n            result[i] = BytesTools.toBytes(t[i]);\n        }\n        return result;\n    }\n\n    /**\n     * @param t operands\n     * @return Array of binary byte arrays made from passed array of binary strings\n     */\n    public static byte[][] toBinaryByteArrays(final String[] t) {\n        byte[][] result = new byte[t.length][];\n        for (int i = 0; i < t.length; i++) {\n            result[i] = BytesTools.toBytesBinary(t[i]);\n        }\n        return result;\n    }\n\n    /**\n     * @param column operand\n     * @return A byte array of a byte array where first and only entry is\n     * <code>column</code>\n     */\n    public static byte [][] toByteArrays(final String column) {\n        return toByteArrays(toBytes(column));\n    }\n\n    /**\n     * @param column operand\n     * @return A byte array of a byte array where first and only entry is\n     * <code>column</code>\n     */\n    public static byte [][] toByteArrays(final byte [] column) {\n        byte [][] result = new byte[1][];\n        result[0] = column;\n        return result;\n    }\n\n\n    public static byte [] paddingInt(byte [] a){\n\n        if(a == null){\n            return null;\n        }\n\n        if (a.length==SIZEOF_INT){\n            return  a;\n        }\n\n        byte [] b = new byte[SIZEOF_INT];\n        if (Platform.littleEndian){\n            for (int i = 0; i < SIZEOF_INT-a.length; i++) {\n                b[i] = 0x00;\n            }\n            System.arraycopy(a, 0, b,SIZEOF_INT-a.length, a.length);\n        }else {\n            System.arraycopy(a, 0, b, 0, a.length);\n            for (int i = a.length; i < SIZEOF_INT; i++) {\n                b[i] = 0x00;\n            }\n        }\n        return  b;\n    }\n\n    public static byte [] paddingLong(byte [] a){\n        if(a == null){\n            return null;\n        }\n\n        if (a.length==SIZEOF_LONG){\n            return  a;\n        }\n\n        byte [] b = new byte[SIZEOF_LONG];\n        if (Platform.littleEndian){\n            for (int i = 0; i < SIZEOF_LONG-a.length; i++) {\n                b[i] = 0x00;\n            }\n            System.arraycopy(a, 0, b,SIZEOF_LONG-a.length, a.length);\n        }else {\n            System.arraycopy(a, 0, b, 0, a.length);\n            for (int i = a.length; i < SIZEOF_LONG; i++) {\n                b[i] = 0x00;\n            }\n        }\n        return b;\n    }\n\n    public static byte [] paddingShort(byte [] a){\n\n        if(a == null){\n            return null;\n        }\n\n        if (a.length==SIZEOF_SHORT){\n            return  a;\n        }\n        byte [] b = new byte[SIZEOF_SHORT];\n        if (Platform.littleEndian){\n            for (int i = 0; i < SIZEOF_SHORT-a.length; i++) {\n                b[i] = 0x00;\n            }\n            System.arraycopy(a, 0, b, SIZEOF_SHORT-a.length, a.length);\n        }else {\n            System.arraycopy(a, 0, b, 0, a.length);\n            for (int i = a.length; i < SIZEOF_SHORT; i++) {\n                b[i] = 0x00;\n            }\n        }\n        return b;\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/JavaUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils;\n\nimport com.google.common.base.Preconditions;\nimport com.google.common.collect.ImmutableMap;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.StandardCharsets;\nimport java.util.UUID;\nimport java.util.concurrent.TimeUnit;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Utility class,\n */\n\npublic class JavaUtils {\n  private static final Logger logger = LoggerFactory.getLogger(JavaUtils.class);\n\n  /**\n   * Define a default value for driver memory here since this value is referenced across the code\n   * base and nearly all files already use Utils.scala\n   */\n  public static final long DEFAULT_DRIVER_MEM_MB = 1024;\n\n  private static int MAX_DIR_CREATION_ATTEMPTS = 10;\n\n  /** Closes the given object, ignoring IOExceptions. */\n  public static void closeQuietly(Closeable closeable) {\n    try {\n      if (closeable != null) {\n        closeable.close();\n      }\n    } catch (IOException e) {\n      logger.error(\"IOException should not have been thrown.\", e);\n    }\n  }\n\n\n  /*\n   * Delete a file or directory and its contents recursively.\n   * Don't follow directories if they are symlinks.\n   * Throws an exception if deletion is unsuccessful.\n   */\n  public static void deleteRecursively(File file) throws IOException {\n    if (file == null) { return; }\n\n    if (file.isDirectory() && !isSymlink(file)) {\n      IOException savedIOException = null;\n      for (File child : listFilesSafely(file)) {\n        try {\n          deleteRecursively(child);\n        } catch (IOException e) {\n          // In case of multiple exceptions, only last one will be thrown\n          savedIOException = e;\n        }\n      }\n      if (savedIOException != null) {\n        throw savedIOException;\n      }\n    }\n\n    boolean deleted = file.delete();\n    // Delete can also fail if the file simply did not exist.\n    if (!deleted && file.exists()) {\n      throw new IOException(\"Failed to delete: \" + file.getAbsolutePath());\n    }\n  }\n\n  private static File[] listFilesSafely(File file) throws IOException {\n    if (file.exists()) {\n      File[] files = file.listFiles();\n      if (files == null) {\n        throw new IOException(\"Failed to list files for dir: \" + file);\n      }\n      return files;\n    } else {\n      return new File[0];\n    }\n  }\n\n  private static boolean isSymlink(File file) throws IOException {\n    Preconditions.checkNotNull(file);\n    File fileInCanonicalDir = null;\n    if (file.getParent() == null) {\n      fileInCanonicalDir = file;\n    } else {\n      fileInCanonicalDir = new File(file.getParentFile().getCanonicalFile(), file.getName());\n    }\n    return !fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile());\n  }\n\n  private static final ImmutableMap<String, TimeUnit> timeSuffixes =\n    ImmutableMap.<String, TimeUnit>builder()\n      .put(\"us\", TimeUnit.MICROSECONDS)\n      .put(\"ms\", TimeUnit.MILLISECONDS)\n      .put(\"s\", TimeUnit.SECONDS)\n      .put(\"m\", TimeUnit.MINUTES)\n      .put(\"min\", TimeUnit.MINUTES)\n      .put(\"h\", TimeUnit.HOURS)\n      .put(\"d\", TimeUnit.DAYS)\n      .build();\n\n  private static final ImmutableMap<String, ByteUnit> byteSuffixes =\n    ImmutableMap.<String, ByteUnit>builder()\n      .put(\"b\", ByteUnit.BYTE)\n      .put(\"k\", ByteUnit.KiB)\n      .put(\"kb\", ByteUnit.KiB)\n      .put(\"m\", ByteUnit.MiB)\n      .put(\"mb\", ByteUnit.MiB)\n      .put(\"g\", ByteUnit.GiB)\n      .put(\"gb\", ByteUnit.GiB)\n      .put(\"t\", ByteUnit.TiB)\n      .put(\"tb\", ByteUnit.TiB)\n      .put(\"p\", ByteUnit.PiB)\n      .put(\"pb\", ByteUnit.PiB)\n      .build();\n\n  /**\n   * Convert a passed time string (e.g. 50s, 100ms, or 250us) to a time count in the given unit.\n   * The unit is also considered the default if the given string does not specify a unit.\n   */\n  public static long timeStringAs(String str, TimeUnit unit) {\n    String lower = str.toLowerCase().trim();\n\n    try {\n      Matcher m = Pattern.compile(\"(-?[0-9]+)([a-z]+)?\").matcher(lower);\n      if (!m.matches()) {\n        throw new NumberFormatException(\"Failed to parse time string: \" + str);\n      }\n\n      long val = Long.parseLong(m.group(1));\n      String suffix = m.group(2);\n\n      // Check for invalid suffixes\n      if (suffix != null && !timeSuffixes.containsKey(suffix)) {\n        throw new NumberFormatException(\"Invalid suffix: \\\"\" + suffix + \"\\\"\");\n      }\n\n      // If suffix is valid use that, otherwise none was provided and use the default passed\n      return unit.convert(val, suffix != null ? timeSuffixes.get(suffix) : unit);\n    } catch (NumberFormatException e) {\n      String timeError = \"Time must be specified as seconds (s), \" +\n              \"milliseconds (ms), microseconds (us), minutes (m or min), hour (h), or day (d). \" +\n              \"E.g. 50s, 100ms, or 250us.\";\n\n      throw new NumberFormatException(timeError + \"\\n\" + e.getMessage());\n    }\n  }\n\n  /**\n   * Convert a time parameter such as (50s, 100ms, or 250us) to milliseconds for internal use. If\n   * no suffix is provided, the passed number is assumed to be in ms.\n   */\n  public static long timeStringAsMs(String str) {\n    return timeStringAs(str, TimeUnit.MILLISECONDS);\n  }\n\n  /**\n   * Convert a time parameter such as (50s, 100ms, or 250us) to seconds for internal use. If\n   * no suffix is provided, the passed number is assumed to be in seconds.\n   */\n  public static long timeStringAsSec(String str) {\n    return timeStringAs(str, TimeUnit.SECONDS);\n  }\n\n  /**\n   * Convert a passed byte string (e.g. 50b, 100kb, or 250mb) to the given. If no suffix is\n   * provided, a direct conversion to the provided unit is attempted.\n   */\n  public static long byteStringAs(String str, ByteUnit unit) {\n    String lower = str.toLowerCase().trim();\n\n    try {\n      Matcher m = Pattern.compile(\"([0-9]+)([a-z]+)?\").matcher(lower);\n      Matcher fractionMatcher = Pattern.compile(\"([0-9]+\\\\.[0-9]+)([a-z]+)?\").matcher(lower);\n\n      if (m.matches()) {\n        long val = Long.parseLong(m.group(1));\n        String suffix = m.group(2);\n\n        // Check for invalid suffixes\n        if (suffix != null && !byteSuffixes.containsKey(suffix)) {\n          throw new NumberFormatException(\"Invalid suffix: \\\"\" + suffix + \"\\\"\");\n        }\n\n        // If suffix is valid use that, otherwise none was provided and use the default passed\n        return unit.convertFrom(val, suffix != null ? byteSuffixes.get(suffix) : unit);\n      } else if (fractionMatcher.matches()) {\n        throw new NumberFormatException(\"Fractional values are not supported. Input was: \"\n          + fractionMatcher.group(1));\n      } else {\n        throw new NumberFormatException(\"Failed to parse byte string: \" + str);\n      }\n\n    } catch (NumberFormatException e) {\n      String byteError = \"Size must be specified as bytes (b), \" +\n        \"kibibytes (k), mebibytes (m), gibibytes (g), tebibytes (t), or pebibytes(p). \" +\n        \"E.g. 50b, 100k, or 250m.\";\n\n      throw new NumberFormatException(byteError + \"\\n\" + e.getMessage());\n    }\n  }\n\n  /**\n   * Convert a passed byte string (e.g. 50b, 100k, or 250m) to bytes for\n   * internal use.\n   *\n   * If no suffix is provided, the passed number is assumed to be in bytes.\n   */\n  public static long byteStringAsBytes(String str) {\n    return byteStringAs(str, ByteUnit.BYTE);\n  }\n\n  /**\n   * Convert a passed byte string (e.g. 50b, 100k, or 250m) to kibibytes for\n   * internal use.\n   *\n   * If no suffix is provided, the passed number is assumed to be in kibibytes.\n   */\n  public static long byteStringAsKb(String str) {\n    return byteStringAs(str, ByteUnit.KiB);\n  }\n\n  /**\n   * Convert a passed byte string (e.g. 50b, 100k, or 250m) to mebibytes for\n   * internal use.\n   *\n   * If no suffix is provided, the passed number is assumed to be in mebibytes.\n   */\n  public static long byteStringAsMb(String str) {\n    return byteStringAs(str, ByteUnit.MiB);\n  }\n\n  /**\n   * Convert a passed byte string (e.g. 50b, 100k, or 250m) to gibibytes for\n   * internal use.\n   *\n   * If no suffix is provided, the passed number is assumed to be in gibibytes.\n   */\n  public static long byteStringAsGb(String str) {\n    return byteStringAs(str, ByteUnit.GiB);\n  }\n\n\n  public static String bytesToString(long size) {\n    long TB = 1L << 40;\n    long GB = 1L << 30;\n    long MB = 1L << 20;\n    long KB = 1L << 10;\n    double value = 0;\n    String unit = null;\n\n    if (size >= 2*TB) {\n      value = size/TB;\n      unit = \"TB\";\n    } else if (size >= 2*GB) {\n      value = size/GB;\n      unit = \"GB\";\n    } else if (size >= 2*MB) {\n      value = size/MB;\n      unit = \"MB\";\n    } else if (size >= 2*KB) {\n      value = size/KB;\n      unit = \"KB\";\n    } else {\n      value = size;\n      unit = \"B\";\n    }\n\n    return value + \" \" + unit;\n  }\n\n\n\n  public static String bytesToString2(long size) {\n    long TB = 1L << 40;\n    long GB = 1L << 30;\n    long MB = 1L << 20;\n    long KB = 1L << 10;\n    long value = 0;\n    String unit = null;\n\n    if (size >= 2*TB) {\n      value = (size/TB);\n      unit = \"TB\";\n    } else if (size >= 2*GB) {\n      value = (size/GB);\n      unit = \"GB\";\n    } else if (size >= 2*MB) {\n      value =  (size/MB);\n      unit = \"MB\";\n    } else if (size >= 2*KB) {\n      value =  (size/KB);\n      unit = \"KB\";\n    } else {\n      value =   size;\n      unit = \"B\";\n    }\n\n    return value + unit;\n  }\n\n\n  public static File createDirectory(String rootDir, String blockmgr) throws IOException {\n\n    int attempts = 0;\n    int maxAttempts = MAX_DIR_CREATION_ATTEMPTS;\n    File dir = null;\n    while (dir == null) {\n      attempts += 1;\n      if (attempts > maxAttempts) {\n        throw new IOException(\"Failed to create a temp directory (under \" + rootDir + \") after \" +\n                maxAttempts + \" attempts!\");\n      }\n      try {\n        dir = new File(rootDir, blockmgr + \"-\" + UUID.randomUUID().toString());\n        if (dir.exists() || !dir.mkdirs()) {\n          dir = null;\n        }\n      } catch (Exception e) {\n        logger.error(e.getMessage());\n      }\n    }\n\n    return dir.getCanonicalFile();\n  }\n\n  /* Calculates 'x' modulo 'mod', takes to consideration sign of x,\n* i.e. if 'x' is negative, than 'x' % 'mod' is negative too\n* so function return (x % mod) + mod in that case.\n*/\n  public static int nonNegativeMod(int x,int mod) {\n    int rawMod = x % mod;\n    int temp;\n    if (rawMod < 0)\n      temp= mod ;\n    else\n      temp =0;\n    return (rawMod + temp);\n  }\n\n\n  public static int nonNegativeHash(Object obj) {\n    // Required ?\n    if (obj == null) return 0;\n\n    int hash = obj.hashCode();\n    // math.abs fails for Int.MinValue\n    int hashAbs = 0;\n\n    if (Integer.MAX_VALUE!= hash && Integer.MIN_VALUE != hash)\n      hashAbs =  Math.abs(hash);\n    else\n      hashAbs = 0;\n\n    // Nothing else to guard against ?\n    return hashAbs;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/MycatPropertyConf.java",
    "content": "package io.mycat.memory.unsafe.utils;\n\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Created by zagnix on 2016/6/2.\n */\npublic class MycatPropertyConf {\n\n    private ConcurrentHashMap settings = new ConcurrentHashMap<String, String>();\n\n    public MycatPropertyConf(){\n\n    }\n\n    /** Set a configuration variable. */\n    public MycatPropertyConf set(String key, String value) {\n        set(key, value, false);\n        return  this;\n    }\n\n\n    public MycatPropertyConf set(String key, String value, boolean silent){\n\n        if (key == null) {\n            throw new NullPointerException(\"null key\");\n        }\n        if (value == null) {\n            throw new NullPointerException(\"null value for \" + key);\n        }\n\n        if (!silent) {\n        }\n\n        settings.put(key, value);\n        return this;\n    }\n\n    public long getSizeAsBytes(String s, long i) {\n        String value = (String) settings.get(s);\n        if(value !=null){\n            return byteStringAsBytes(value);\n        }\n        return i;\n    }\n\n    public long getSizeAsBytes(String s, String defaultValue) {\n        String value = (String) settings.get(s);\n        if(value !=null){\n            return byteStringAsBytes(value);\n        }\n        return byteStringAsBytes(defaultValue);\n    }\n\n\n    public double getDouble(String s, double v) {\n        return v;\n    }\n\n    public boolean getBoolean(String s, boolean b) {\n        String value = (String) settings.get(s);\n        if(value !=null){\n\n            if(value.equals(\"true\")){\n                return true;\n            }else{\n                return false;\n            }\n        }\n        return b;\n    }\n\n\n    public long getLong(String s, long l) {\n        return l;\n    }\n\n    public boolean contains(String s) {\n        return  true;\n    }\n\n    public int getInt(String s, int i) {\n        return  i;\n    }\n\n    /**\n     * Convert a passed byte string (e.g. 50b, 100k, or 250m) to bytes for internal use.\n     *\n     * If no suffix is provided, the passed number is assumed to be in bytes.\n     */\n    public Long byteStringAsBytes(String str) {\n       return JavaUtils.byteStringAsBytes(str);\n    }\n\n    /**\n     * Convert a passed byte string (e.g. 50b, 100k, or 250m) to kibibytes for internal use.\n     *\n     * If no suffix is provided, the passed number is assumed to be in kibibytes.\n     */\n    public Long byteStringAsKb(String str){\n       return JavaUtils.byteStringAsKb(str);\n    }\n\n    /**\n     * Convert a passed byte string (e.g. 50b, 100k, or 250m) to mebibytes for internal use.\n     *\n     * If no suffix is provided, the passed number is assumed to be in mebibytes.\n     */\n    public Long byteStringAsMb(String str) {\n       return JavaUtils.byteStringAsMb(str);\n    }\n\n    /**\n     * Convert a passed byte string (e.g. 50b, 100k, or 250m, 500g) to gibibytes for internal use.\n     *\n     * If no suffix is provided, the passed number is assumed to be in gibibytes.\n     */\n    public Long byteStringAsGb(String str) {\n        return  JavaUtils.byteStringAsGb(str);\n    }\n\n    /**\n     * Convert a Java memory parameter passed to -Xmx (such as 300m or 1g) to a number of mebibytes.\n     */\n    public int memoryStringToMb(String str){\n        // Convert to bytes, rather than directly to MB, because when no units are specified the unit\n        // is assumed to be bytes\n       return (int) (JavaUtils.byteStringAsBytes(str) / 1024 / 1024);\n    }\n\n    public String getString(String s, String defaultValue) {\n\n        String value = (String) settings.get(s);\n        if(value !=null){\n            return value;\n        }\n        return defaultValue;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/AbstractScalaRowIterator.java",
    "content": "package io.mycat.memory.unsafe.utils.sort;\n\nimport java.util.Iterator;\n\n/**\n * Created by zagnix 2016/6/6.\n */\npublic class AbstractScalaRowIterator<T> implements Iterator<T> {\n    @Override\n    public boolean hasNext() {\n        return false;\n    }\n\n    @Override\n    public T next() {\n        return null;\n    }\n\n    @Override\n    public void remove() {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/PrefixComparator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\n/**\n * Compares 8-byte key prefixes in prefix sort. Subclasses may implement type-specific\n * comparisons, such as lexicographic comparison for strings.\n */\n\npublic abstract class PrefixComparator {\n  public abstract int compare(long prefix1, long prefix2);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/PrefixComparators.java",
    "content": "\n\npackage io.mycat.memory.unsafe.utils.sort;\n\nimport com.google.common.primitives.UnsignedLongs;\nimport io.mycat.memory.unsafe.types.ByteArray;\n\n\npublic class PrefixComparators {\n  private PrefixComparators() {}\n\n  public static final PrefixComparator STRING = new UnsignedPrefixComparator();\n  public static final PrefixComparator STRING_DESC = new UnsignedPrefixComparatorDesc();\n  public static final PrefixComparator BINARY = new UnsignedPrefixComparator();\n  public static final PrefixComparator BINARY_DESC = new UnsignedPrefixComparatorDesc();\n  public static final PrefixComparator LONG = new SignedPrefixComparator();\n  public static final PrefixComparator LONG_DESC = new SignedPrefixComparatorDesc();\n  public static final PrefixComparator DOUBLE = new UnsignedPrefixComparator();\n  public static final PrefixComparator DOUBLE_DESC = new UnsignedPrefixComparatorDesc();\n\n  public static final PrefixComparator RadixSortDemo = new RadixSortDemo();\n\n\n\n  public static final class BinaryPrefixComparator {\n    public static long computePrefix(byte[] bytes) {\n      return ByteArray.getPrefix(bytes);\n    }\n  }\n\n  public static final class DoublePrefixComparator {\n    /**\n     * Converts the double into a value that compares correctly as an unsigned long. For more\n     * details see http://stereopsis.com/radix.html.\n     */\n    public static long computePrefix(double value) {\n      // Java's doubleToLongBits already canonicalizes all NaN values to the smallest possible\n      // positive NaN, so there's nothing special we need to do for NaNs.\n      long bits = Double.doubleToLongBits(value);\n      // Negative floats compare backwards due to their sign-magnitude representation, so flip\n      // all the bits in this case.\n      long mask = -(bits >>> 63) | 0x8000000000000000L;\n      return bits ^ mask;\n    }\n  }\n\n  /**\n   * Provides radix sort parameters. Comparators implementing this also are indicating that the\n   * ordering they define is compatible with radix sort.\n   */\n  public abstract static class RadixSortSupport extends PrefixComparator {\n    /** @return Whether the sort should be descending in binary sort order. */\n    public abstract boolean sortDescending();\n\n    /** @return Whether the sort should take into account the sign bit. */\n    public abstract boolean sortSigned();\n  }\n\n  public static final  class RadixSortDemo extends PrefixComparators.RadixSortSupport{\n\n    @Override\n    public boolean sortDescending() {\n      return false;\n    }\n\n    @Override\n    public boolean sortSigned() {\n      return false;\n    }\n\n    @Override\n    public int compare(long prefix1, long prefix2) {\n      return PrefixComparators.BINARY.compare(prefix1 & 0xffffff0000L, prefix1 & 0xffffff0000L);\n    }\n  }\n  //\n  // Standard prefix comparator implementations\n  //\n\n  public static final class UnsignedPrefixComparator extends RadixSortSupport {\n    @Override public boolean sortDescending() { return false; }\n    @Override public boolean sortSigned() { return false; }\n    @Override\n    public int compare(long aPrefix, long bPrefix) {\n      return UnsignedLongs.compare(aPrefix, bPrefix);\n    }\n  }\n\n  public static final class UnsignedPrefixComparatorDesc extends RadixSortSupport {\n    @Override public boolean sortDescending() { return true; }\n    @Override public boolean sortSigned() { return false; }\n    @Override\n    public int compare(long bPrefix, long aPrefix) {\n      return UnsignedLongs.compare(aPrefix, bPrefix);\n    }\n  }\n\n  public static final class SignedPrefixComparator extends RadixSortSupport {\n    @Override public boolean sortDescending() { return false; }\n    @Override public boolean sortSigned() { return true; }\n    @Override\n    public int compare(long a, long b) {\n      return (a < b) ? -1 : (a > b) ? 1 : 0;\n    }\n  }\n\n  public static final class SignedPrefixComparatorDesc extends RadixSortSupport {\n    @Override public boolean sortDescending() { return true; }\n    @Override public boolean sortSigned() { return true; }\n    @Override\n    public int compare(long b, long a) {\n      return (a < b) ? -1 : (a > b) ? 1 : 0;\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/RadixSort.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.array.LongArray;\n\npublic class RadixSort {\n\n  /**\n   * Sorts a given array of longs using least-significant-digit radix sort. This routine assumes\n   * you have extra space at the end of the array at least equal to the number of records. The\n   * sort is destructive and may relocate the data positioned within the array.\n   *\n   * @param array array of long elements followed by at least that many empty slots.\n   * @param numRecords number of data records in the array.\n   * @param startByteIndex the first byte (in range [0, 7]) to sort each long by, counting from the\n   *                       least significant byte.\n   * @param endByteIndex the last byte (in range [0, 7]) to sort each long by, counting from the\n   *                     least significant byte. Must be greater than startByteIndex.\n   * @param desc whether this is a descending (binary-order) sort.\n   * @param signed whether this is a signed (two's complement) sort.\n   *\n   * @return The starting index of the sorted data within the given array. We return this instead\n   *         of always copying the data back to position zero for efficiency.\n   */\n  public static int sort(\n          LongArray array, int numRecords, int startByteIndex, int endByteIndex,\n          boolean desc, boolean signed) {\n    assert startByteIndex >= 0 : \"startByteIndex (\" + startByteIndex + \") should >= 0\";\n    assert endByteIndex <= 7 : \"endByteIndex (\" + endByteIndex + \") should <= 7\";\n    assert endByteIndex > startByteIndex;\n    assert numRecords * 2 <= array.size();\n    int inIndex = 0;\n    int outIndex = numRecords;\n    if (numRecords > 0) {\n      long[][] counts = getCounts(array, numRecords, startByteIndex, endByteIndex);\n      for (int i = startByteIndex; i <= endByteIndex; i++) {\n        if (counts[i] != null) {\n          sortAtByte(\n            array, numRecords, counts[i], i, inIndex, outIndex,\n            desc, signed && i == endByteIndex);\n          int tmp = inIndex;\n          inIndex = outIndex;\n          outIndex = tmp;\n        }\n      }\n    }\n    return inIndex;\n  }\n\n  /**\n   * Performs a partial sort by copying data into destination offsets for each byte value at the\n   * specified byte offset.\n   *\n   * @param array array to partially sort.\n   * @param numRecords number of data records in the array.\n   * @param counts counts for each byte value. This routine destructively modifies this array.\n   * @param byteIdx the byte in a long to sort at, counting from the least significant byte.\n   * @param inIndex the starting index in the array where input data is located.\n   * @param outIndex the starting index where sorted output data should be written.\n   * @param desc whether this is a descending (binary-order) sort.\n   * @param signed whether this is a signed (two's complement) sort (only applies to last byte).\n   */\n  private static void sortAtByte(\n      LongArray array, int numRecords, long[] counts, int byteIdx, int inIndex, int outIndex,\n      boolean desc, boolean signed) {\n    assert counts.length == 256;\n    long[] offsets = transformCountsToOffsets(\n      counts, numRecords, array.getBaseOffset() + outIndex * 8, 8, desc, signed);\n    Object baseObject = array.getBaseObject();\n    long baseOffset = array.getBaseOffset() + inIndex * 8;\n    long maxOffset = baseOffset + numRecords * 8;\n    for (long offset = baseOffset; offset < maxOffset; offset += 8) {\n      long value = Platform.getLong(baseObject, offset);\n      int bucket = (int)((value >>> (byteIdx * 8)) & 0xff);\n      Platform.putLong(baseObject, offsets[bucket], value);\n      offsets[bucket] += 8;\n    }\n  }\n\n  /**\n   * Computes a value histogram for each byte in the given array.\n   *\n   * @param array array to count records in.\n   * @param numRecords number of data records in the array.\n   * @param startByteIndex the first byte to compute counts for (the prior are skipped).\n   * @param endByteIndex the last byte to compute counts for.\n   *\n   * @return an array of eight 256-byte count arrays, one for each byte starting from the least\n   *         significant byte. If the byte does not need sorting the array will be null.\n   */\n  private static long[][] getCounts(\n      LongArray array, int numRecords, int startByteIndex, int endByteIndex) {\n    long[][] counts = new long[8][];\n    // Optimization: do a fast pre-pass to determine which byte indices we can skip for sorting.\n    // If all the byte values at a particular index are the same we don't need to count it.\n    long bitwiseMax = 0;\n    long bitwiseMin = -1L;\n    long maxOffset = array.getBaseOffset() + numRecords * 8;\n    Object baseObject = array.getBaseObject();\n    for (long offset = array.getBaseOffset(); offset < maxOffset; offset += 8) {\n      long value = Platform.getLong(baseObject, offset);\n      bitwiseMax |= value;\n      bitwiseMin &= value;\n    }\n    long bitsChanged = bitwiseMin ^ bitwiseMax;\n    // Compute counts for each byte index.\n    for (int i = startByteIndex; i <= endByteIndex; i++) {\n      if (((bitsChanged >>> (i * 8)) & 0xff) != 0) {\n        counts[i] = new long[256];\n        // TODO(ekl) consider computing all the counts in one pass.\n        for (long offset = array.getBaseOffset(); offset < maxOffset; offset += 8) {\n          counts[i][(int)((Platform.getLong(baseObject, offset) >>> (i * 8)) & 0xff)]++;\n        }\n      }\n    }\n    return counts;\n  }\n\n  /**\n   * Transforms counts into the proper unsafe output offsets for the sort type.\n   *\n   * @param counts counts for each byte value. This routine destructively modifies this array.\n   * @param numRecords number of data records in the original data array.\n   * @param outputOffset output offset in bytes from the base array object.\n   * @param bytesPerRecord size of each record (8 for plain sort, 16 for key-prefix sort).\n   * @param desc whether this is a descending (binary-order) sort.\n   * @param signed whether this is a signed (two's complement) sort.\n   *\n   * @return the input counts array.\n   */\n  private static long[] transformCountsToOffsets(\n      long[] counts, int numRecords, long outputOffset, int bytesPerRecord,\n      boolean desc, boolean signed) {\n    assert counts.length == 256;\n    int start = signed ? 128 : 0;  // output the negative records first (values 129-255).\n    if (desc) {\n      int pos = numRecords;\n      for (int i = start; i < start + 256; i++) {\n        pos -= counts[i & 0xff];\n        counts[i & 0xff] = outputOffset + pos * bytesPerRecord;\n      }\n    } else {\n      int pos = 0;\n      for (int i = start; i < start + 256; i++) {\n        long tmp = counts[i & 0xff];\n        counts[i & 0xff] = outputOffset + pos * bytesPerRecord;\n        pos += tmp;\n      }\n    }\n    return counts;\n  }\n\n  /**\n   * Specialization of sort() for key-prefix arrays. In this type of array, each record consists\n   * of two longs, only the second of which is sorted on.\n   */\n  public static int sortKeyPrefixArray(\n      LongArray array,\n      int numRecords,\n      int startByteIndex,\n      int endByteIndex,\n      boolean desc,\n      boolean signed) {\n    assert startByteIndex >= 0 : \"startByteIndex (\" + startByteIndex + \") should >= 0\";\n    assert endByteIndex <= 7 : \"endByteIndex (\" + endByteIndex + \") should <= 7\";\n    assert endByteIndex > startByteIndex;\n    assert numRecords * 4 <= array.size();\n    int inIndex = 0;\n    int outIndex = numRecords * 2;\n    if (numRecords > 0) {\n      long[][] counts = getKeyPrefixArrayCounts(array, numRecords, startByteIndex, endByteIndex);\n      for (int i = startByteIndex; i <= endByteIndex; i++) {\n        if (counts[i] != null) {\n          sortKeyPrefixArrayAtByte(\n            array, numRecords, counts[i], i, inIndex, outIndex,\n            desc, signed && i == endByteIndex);\n          int tmp = inIndex;\n          inIndex = outIndex;\n          outIndex = tmp;\n        }\n      }\n    }\n    return inIndex;\n  }\n\n  /**\n   * Specialization of getCounts() for key-prefix arrays. We could probably combine this with\n   * getCounts with some added parameters but that seems to hurt in benchmarks.\n   */\n  private static long[][] getKeyPrefixArrayCounts(\n      LongArray array, int numRecords, int startByteIndex, int endByteIndex) {\n    long[][] counts = new long[8][];\n    long bitwiseMax = 0;\n    long bitwiseMin = -1L;\n    long limit = array.getBaseOffset() + numRecords * 16;\n    Object baseObject = array.getBaseObject();\n    for (long offset = array.getBaseOffset(); offset < limit; offset += 16) {\n      long value = Platform.getLong(baseObject, offset + 8);\n      bitwiseMax |= value;\n      bitwiseMin &= value;\n    }\n    long bitsChanged = bitwiseMin ^ bitwiseMax;\n    for (int i = startByteIndex; i <= endByteIndex; i++) {\n      if (((bitsChanged >>> (i * 8)) & 0xff) != 0) {\n        counts[i] = new long[256];\n        for (long offset = array.getBaseOffset(); offset < limit; offset += 16) {\n          counts[i][(int)((Platform.getLong(baseObject, offset + 8) >>> (i * 8)) & 0xff)]++;\n        }\n      }\n    }\n    return counts;\n  }\n\n  /**\n   * Specialization of sortAtByte() for key-prefix arrays.\n   */\n  private static void sortKeyPrefixArrayAtByte(\n      LongArray array, int numRecords, long[] counts, int byteIdx, int inIndex, int outIndex,\n      boolean desc, boolean signed) {\n    assert counts.length == 256;\n    long[] offsets = transformCountsToOffsets(\n      counts, numRecords, array.getBaseOffset() + outIndex * 8, 16, desc, signed);\n    Object baseObject = array.getBaseObject();\n    long baseOffset = array.getBaseOffset() + inIndex * 8;\n    long maxOffset = baseOffset + numRecords * 16;\n    for (long offset = baseOffset; offset < maxOffset; offset += 16) {\n      long key = Platform.getLong(baseObject, offset);\n      long prefix = Platform.getLong(baseObject, offset + 8);\n      int bucket = (int)((prefix >>> (byteIdx * 8)) & 0xff);\n      long dest = offsets[bucket];\n      Platform.putLong(baseObject, dest, key);\n      Platform.putLong(baseObject, dest + 8, prefix);\n      offsets[bucket] += 16;\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/RecordComparator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\n/**\n * Compares records for ordering. In cases where the entire sorting key can fit in the 8-byte\n * prefix, this may simply return 0.\n */\npublic abstract class RecordComparator {\n\n  /**\n   * Compare two records for order.\n   *\n   * @return a negative integer, zero, or a positive integer as the first record is less than,\n   *         equal to, or greater than the second.\n   */\n  public abstract int compare(\n    Object leftBaseObject,\n    long leftBaseOffset,\n    Object rightBaseObject,\n    long rightBaseOffset);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/RecordPointerAndKeyPrefix.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\npublic final class RecordPointerAndKeyPrefix {\n  /**\n   * A pointer to a record; see {@link io.mycat.memory.unsafe.memory} for a\n   * description of how these addresses are encoded.\n   */\n  public long recordPointer;\n\n  /**\n   * A key prefix, for use in comparisons.\n   */\n  public long keyPrefix;\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/RowPrefixComputer.java",
    "content": "package io.mycat.memory.unsafe.utils.sort;\n\n\nimport io.mycat.memory.unsafe.row.StructType;\nimport io.mycat.memory.unsafe.row.UnsafeRow;\nimport io.mycat.memory.unsafe.utils.BytesTools;\nimport io.mycat.sqlengine.mpp.ColMeta;\nimport io.mycat.sqlengine.mpp.OrderCol;\n\nimport javax.annotation.Nonnull;\nimport java.io.UnsupportedEncodingException;\n\n/**\n * Created by zagnix on 2016/6/20.\n */\npublic class RowPrefixComputer extends UnsafeExternalRowSorter.PrefixComputer {\n    @Nonnull\n    private final StructType schema;\n    private final ColMeta colMeta;\n\n    public RowPrefixComputer(StructType schema){\n        this.schema = schema;\n        /**\n         * 通过计算得到排序关键词的第一个在行的索引下标\n         */\n        OrderCol[] orderCols = schema.getOrderCols();\n\n        if (orderCols != null && orderCols.length > 0){\n            this.colMeta = orderCols[0].colMeta;\n        }else {\n            this.colMeta = null;\n        }\n    }\n\n    protected long computePrefix(UnsafeRow row) throws UnsupportedEncodingException {\n\n        if(this.colMeta == null){\n            return 0;\n        }\n\n        int orderIndexType = colMeta.colType;\n\n        byte[] rowIndexElem  = null;\n\t\t\n\t\t  if(!row.isNullAt(colMeta.colIndex)) {\n              rowIndexElem = row.getBinary(colMeta.colIndex);\n              /**\n               * 这里注意一下，order by 排序的第一个字段\n               */\n              switch (orderIndexType) {\n                  case ColMeta.COL_TYPE_INT:\n                  case ColMeta.COL_TYPE_LONG:\n                  case ColMeta.COL_TYPE_INT24:\n                      return BytesTools.getInt(rowIndexElem);\n                  case ColMeta.COL_TYPE_SHORT:\n                      return BytesTools.getShort(rowIndexElem);\n                  case ColMeta.COL_TYPE_LONGLONG:\n                      return BytesTools.getLong(rowIndexElem);\n                  case ColMeta.COL_TYPE_FLOAT:\n                      return PrefixComparators.DoublePrefixComparator.\n                          computePrefix(BytesTools.getFloat(rowIndexElem));\n                  case ColMeta.COL_TYPE_DOUBLE:\n                  case ColMeta.COL_TYPE_DECIMAL:\n                  case ColMeta.COL_TYPE_NEWDECIMAL:\n                      return PrefixComparators.DoublePrefixComparator.\n                              computePrefix(BytesTools.getDouble(rowIndexElem));\n                  case ColMeta.COL_TYPE_DATE:\n                  case ColMeta.COL_TYPE_TIMSTAMP:\n                  case ColMeta.COL_TYPE_TIME:\n                  case ColMeta.COL_TYPE_YEAR:\n                  case ColMeta.COL_TYPE_DATETIME:\n                  case ColMeta.COL_TYPE_NEWDATE:\n                  case ColMeta.COL_TYPE_BIT:\n                  case ColMeta.COL_TYPE_VAR_STRING:\n                  case ColMeta.COL_TYPE_STRING:\n                      // ENUM和SET类型都是字符串，按字符串处理\n                  case ColMeta.COL_TYPE_ENUM:\n                  case ColMeta.COL_TYPE_SET:\n                      return PrefixComparators.BinaryPrefixComparator.computePrefix(rowIndexElem);\n                     //BLOB相关类型和GEOMETRY类型不支持排序，略掉\n              }\n          } else {\n        \t  rowIndexElem = new byte[1];\n        \t  rowIndexElem[0] = UnsafeRow.NULL_MARK;\n        \t  return PrefixComparators.BinaryPrefixComparator.computePrefix(rowIndexElem);\n          }\n\t\t  \n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/SortDataFormat.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\n/**\n * Abstraction for sorting an arbitrary input buffer of data. This interface requires determining\n * the sort key for a given element index, as well as swapping elements and moving data from one\n * buffer to another.\n *\n * Example format: an array of numbers, where each element is also the key.\n * See [[KVArraySortDataFormat]] for a more exciting format.\n *\n * Note: Declaring and instantiating multiple subclasses of this class would prevent JIT inlining\n * overridden methods and hence decrease the shuffle performance.\n *\n * @tparam K Type of the sort key of each element\n * @tparam Buffer Internal data structure used by a particular format (e.g., Array[Int]).\n */\n// TODO: Making Buffer a real trait would be a better abstraction, but adds some complexity.\n\npublic  abstract class SortDataFormat<K,Buffer> {\n\n  /**\n   * Creates a new mutable key for reuse. This should be implemented if you want to override\n   * [[getKey(Buffer, Int, K)]].\n   */\n  public abstract K newKey();\n\n  /** Return the sort key for the element at the given index. */\n  protected abstract K getKey(Buffer data, int pos);\n\n  /**\n   * Returns the sort key for the element at the given index and reuse the input key if possible.\n   * The default implementation ignores the reuse parameter and invokes [[getKey(Buffer, Int]].\n   * If you want to override this method, you must implement [[newKey()]].\n   */\n  protected K getKey(Buffer data, int pos, K reuse) {\n    return getKey(data, pos);\n  }\n\n  /** Swap two elements. */\n  protected abstract  void swap(Buffer data, int pos0,int pos1);\n\n  /** Copy a single element from src(srcPos) to dst(dstPos). */\n  protected abstract  void  copyElement(Buffer src, int srcPos,Buffer dst ,int dstPos);\n\n  /**\n   * Copy a range of elements starting at src(srcPos) to dst, starting at dstPos.\n   * Overlapping ranges are allowed.\n   */\n  protected abstract  void  copyRange(Buffer src, int srcPos,Buffer dst, int dstPos, int length);\n\n  /**\n   * Allocates a Buffer that can hold up to 'length' elements.\n   * All elements of the buffer should be considered invalid until data is explicitly copied in.\n   */\n  protected abstract  Buffer  allocate(int length);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/SortPrefixUtils.java",
    "content": "package io.mycat.memory.unsafe.utils.sort;\n\n\nimport io.mycat.memory.unsafe.row.StructType;\n\n/**\n * Created by zagnix on 2016/6/6.\n */\npublic final class SortPrefixUtils {\n    public static boolean canSortFullyWithPrefix(long apply) {\n        return true;\n    }\n\n    public static PrefixComparator getPrefixComparator(StructType keySchema) {\n        return null;\n    }\n\n    public static UnsafeExternalRowSorter.PrefixComputer createPrefixGenerator(StructType keySchema) {\n        return null;\n    }\n\n    /**\n     * A dummy prefix comparator which always claims that prefixes are equal. This is used in cases\n     * where we don't know how to generate or compare prefixes for a SortOrder.\n     */\n    private class NoOpPrefixComparator extends PrefixComparator {\n\n        @Override\n        public int compare(long prefix1, long prefix2) {\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/Sorter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\nimport java.util.Comparator;\n\n/**\n * A simple wrapper over the Java implementation [[TimSort]].\n *\n * The Java implementation is package private, and hence it cannot be called outside package\n * org.opencloudb.memory.unsafe.utils.sort. This is a simple wrapper of it that is available to mycat.\n */\npublic class Sorter<K,Buffer> {\n\n  private TimSort timSort = null;\n\n  public  Sorter(SortDataFormat<K,Buffer> s){\n    timSort = new TimSort(s);\n  }\n\n  /**\n   * Sorts the input buffer within range [lo, hi).\n   */\n  public void sort(Buffer a, int lo, int hi,  Comparator<K> c) {\n    timSort.sort(a, lo, hi, c);\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/TestSorter.java",
    "content": "package io.mycat.memory.unsafe.utils.sort;\n\nimport io.mycat.memory.MyCatMemory;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.MemoryManager;\nimport io.mycat.memory.unsafe.row.BufferHolder;\nimport io.mycat.memory.unsafe.row.StructType;\nimport io.mycat.memory.unsafe.row.UnsafeRow;\nimport io.mycat.memory.unsafe.row.UnsafeRowWriter;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\nimport io.mycat.sqlengine.mpp.ColMeta;\nimport io.mycat.sqlengine.mpp.OrderCol;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.concurrent.CountDownLatch;\n\n/**\n * Created by zagnix on 16-7-9.\n */\npublic class TestSorter implements Runnable {\n    private static final Logger logger = LoggerFactory.getLogger(TestSorter.class);\n\n    private static  final  int TEST_SIZE = 1000000;\n    private static  int TASK_SIZE = 100;\n    private static  CountDownLatch countDownLatch = new CountDownLatch(100);\n    public  void  runSorter( MyCatMemory myCatMemory,\n                             MemoryManager memoryManager,\n                             MycatPropertyConf conf) throws NoSuchFieldException, IllegalAccessException, IOException {\n        DataNodeMemoryManager dataNodeMemoryManager = new DataNodeMemoryManager(memoryManager,\n                Thread.currentThread().getId());\n        /**\n         * 1.schema ,模拟一个field字段值\n         *\n         */\n        int fieldCount = 3;\n        ColMeta colMeta = null;\n        Map<String, ColMeta> colMetaMap = new HashMap<String, ColMeta>(fieldCount);\n        colMeta = new ColMeta(0, ColMeta.COL_TYPE_STRING);\n        colMetaMap.put(\"id\", colMeta);\n        colMeta = new ColMeta(1, ColMeta.COL_TYPE_STRING);\n        colMetaMap.put(\"name\", colMeta);\n        colMeta = new ColMeta(2, ColMeta.COL_TYPE_STRING);\n        colMetaMap.put(\"age\", colMeta);\n\n\n        OrderCol[] orderCols = new OrderCol[1];\n        OrderCol orderCol = new OrderCol(colMetaMap.get(\"id\"),\n                OrderCol.COL_ORDER_TYPE_ASC);\n        orderCols[0] = orderCol;\n        /**\n         * 2 .PrefixComputer\n         */\n        StructType schema = new StructType(colMetaMap, fieldCount);\n        schema.setOrderCols(orderCols);\n\n        UnsafeExternalRowSorter.PrefixComputer prefixComputer =\n                new RowPrefixComputer(schema);\n\n        /**\n         * 3 .PrefixComparator 默认是ASC，可以选择DESC\n         */\n        final PrefixComparator prefixComparator = PrefixComparators.LONG;\n\n        UnsafeExternalRowSorter sorter =\n                new UnsafeExternalRowSorter(dataNodeMemoryManager,\n                        myCatMemory,\n                        schema,\n                        prefixComparator,\n                        prefixComputer,\n                        conf.getSizeAsBytes(\"mycat.buffer.pageSize\",\"1m\"),\n                        true, /**使用基数排序？true or false*/\n                        true);\n        UnsafeRow unsafeRow;\n        BufferHolder bufferHolder;\n        UnsafeRowWriter unsafeRowWriter;\n        String line = \"testUnsafeRow\";\n        final Random rand = new Random(42);\n        for (int i = 0; i < TEST_SIZE; i++) {\n            unsafeRow = new UnsafeRow(3);\n            bufferHolder = new BufferHolder(unsafeRow);\n            unsafeRowWriter = new UnsafeRowWriter(bufferHolder,3);\n            bufferHolder.reset();\n\n            String key = getRandomString(rand.nextInt(300)+100);\n\n            unsafeRowWriter.write(0,key.getBytes());\n            unsafeRowWriter.write(1, line.getBytes());\n            unsafeRowWriter.write(2, (\"35\" + 1).getBytes());\n\n            unsafeRow.setTotalSize(bufferHolder.totalSize());\n            sorter.insertRow(unsafeRow);\n        }\n        Iterator<UnsafeRow> iter = sorter.sort();\n        UnsafeRow row = null;\n        int indexprint = 0;\n        while (iter.hasNext()) {\n            row = iter.next();\n            indexprint++;\n        }\n\n        sorter.cleanupResources();\n        countDownLatch.countDown();\n\n        System.out.println(\"Thread ID :\" + Thread.currentThread().getId() + \"Index : \" + indexprint);\n    }\n\n\n    public static String getRandomString(int length) { //length表示生成字符串的长度\n        String base = \"abcdefghijklmnopqrstuvwxyz0123456789\";\n        Random random = new Random();\n        StringBuffer sb = new StringBuffer();\n        for (int i = 0; i < length; i++) {\n            int number = random.nextInt(base.length());\n            sb.append(base.charAt(number));\n        }\n        return sb.toString();\n    }\n    final  MyCatMemory myCatMemory ;\n    final  MemoryManager memoryManager;\n    final  MycatPropertyConf conf;\n\n\n        public TestSorter( MyCatMemory myCatMemory, MemoryManager memoryManager,MycatPropertyConf conf) throws NoSuchFieldException, IllegalAccessException {\n            this.myCatMemory = myCatMemory;\n            this.memoryManager = memoryManager;\n            this.conf = conf;\n        }\n\n        @Override\n        public void run() {\n            try {\n                runSorter(myCatMemory,memoryManager,conf);\n            } catch (NoSuchFieldException e) {\n                logger.error(e.getMessage());\n            } catch (IllegalAccessException e) {\n                logger.error(e.getMessage());\n            } catch (IOException e) {\n                logger.error(e.getMessage());\n            }\n        }\n\n    public static void main(String[] args) throws Exception {\n\n        MyCatMemory myCatMemory ;\n        MemoryManager memoryManager;\n        MycatPropertyConf conf;\n\n        myCatMemory = new MyCatMemory();\n        memoryManager = myCatMemory.getResultMergeMemoryManager();\n        conf = myCatMemory.getConf();\n\n        for (int i = 0; i < TASK_SIZE; i++) {\n            Thread thread = new Thread(new TestSorter(myCatMemory,memoryManager,conf));\n            thread.start();\n        }\n\n        while (countDownLatch.getCount() != 0){\n            System.err.println(\"count ========================>\" + countDownLatch.getCount());\n            Thread.sleep(1000);\n        }\n\n        System.err.println(TASK_SIZE + \" tasks sorter finished ok !!!!!!!!!\");\n\n        System.exit(1);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/TimSort.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Based on TimSort.java from the Android Open Source Project\n *\n *  Copyright (C) 2008 The Android Open Source Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\nimport java.util.Comparator;\n\n/**\n * A port of the Android TimSort class, which utilizes a \"stable, adaptive, iterative mergesort.\"\n * See the method comment on sort() for more details.\n *\n * This has been kept in Java with the original style in order to match very closely with the\n * Android source code, and thus be easy to verify correctness. The class is package private. We put\n * a simple Scala wrapper {@link io.mycat.memory.unsafe.utils.sort.Sorter}, which is available to\n * The purpose of the port is to generalize the interface to the sort to accept input data formats\n * besides simple arrays where every element is sorted individually. For instance, the AppendOnlyMap\n * uses this to sort an Array with alternating elements of the form [key, value, key, value].\n * This generalization comes with minimal overhead -- see SortDataFormat for more information.\n *\n * We allow key reuse to prevent creating many key objects -- see SortDataFormat.\n *\n * @see io.mycat.memory.unsafe.utils.sort.SortDataFormat\n * @see io.mycat.memory.unsafe.utils.sort.Sorter\n */\nclass TimSort<K, Buffer> {\n\n  /**\n   * This is the minimum sized sequence that will be merged.  Shorter\n   * sequences will be lengthened by calling binarySort.  If the entire\n   * array is less than this length, no merges will be performed.\n   *\n   * This constant should be a power of two.  It was 64 in Tim Peter's C\n   * implementation, but 32 was empirically determined to work better in\n   * this implementation.  In the unlikely event that you set this constant\n   * to be a number that's not a power of two, you'll need to change the\n   * minRunLength computation.\n   *\n   * If you decrease this constant, you must change the stackLen\n   * computation in the TimSort constructor, or you risk an\n   * ArrayOutOfBounds exception.  See listsort.txt for a discussion\n   * of the minimum stack length required as a function of the length\n   * of the array being sorted and the minimum merge sequence length.\n   */\n  private static final int MIN_MERGE = 32;\n\n  private final SortDataFormat<K, Buffer> s;\n\n  public TimSort(SortDataFormat<K, Buffer> sortDataFormat) {\n    this.s = sortDataFormat;\n  }\n\n  /**\n   * A stable, adaptive, iterative mergesort that requires far fewer than\n   * n lg(n) comparisons when running on partially sorted arrays, while\n   * offering performance comparable to a traditional mergesort when run\n   * on random arrays.  Like all proper mergesorts, this sort is stable and\n   * runs O(n log n) time (worst case).  In the worst case, this sort requires\n   * temporary storage space for n/2 object references; in the best case,\n   * it requires only a small constant amount of space.\n   *\n   * This implementation was adapted from Tim Peters's list sort for\n   * Python, which is described in detail here:\n   *\n   *   http://svn.python.org/projects/python/trunk/Objects/listsort.txt\n   *\n   * Tim's C code may be found here:\n   *\n   *   http://svn.python.org/projects/python/trunk/Objects/listobject.c\n   *\n   * The underlying techniques are described in this paper (and may have\n   * even earlier origins):\n   *\n   *  \"Optimistic Sorting and Information Theoretic Complexity\"\n   *  Peter McIlroy\n   *  SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms),\n   *  pp 467-474, Austin, Texas, 25-27 January 1993.\n   *\n   * While the API to this class consists solely of static methods, it is\n   * (privately) instantiable; a TimSort instance holds the state of an ongoing\n   * sort, assuming the input array is large enough to warrant the full-blown\n   * TimSort. Small arrays are sorted in place, using a binary insertion sort.\n   *\n   * @author Josh Bloch\n   */\n  public void sort(Buffer a, int lo, int hi, Comparator<? super K> c) {\n    assert c != null;\n\n    int nRemaining  = hi - lo;\n    if (nRemaining < 2)\n      return;  // Arrays of size 0 and 1 are always sorted\n\n    // If array is small, do a \"mini-TimSort\" with no merges\n    if (nRemaining < MIN_MERGE) {\n      int initRunLen = countRunAndMakeAscending(a, lo, hi, c);\n      binarySort(a, lo, hi, lo + initRunLen, c);\n      return;\n    }\n\n    /**\n     * March over the array once, left to right, finding natural runs,\n     * extending short natural runs to minRun elements, and merging runs\n     * to maintain stack invariant.\n     */\n    SortState sortState = new SortState(a, c, hi - lo);\n    int minRun = minRunLength(nRemaining);\n    do {\n      // Identify next run\n      int runLen = countRunAndMakeAscending(a, lo, hi, c);\n\n      // If run is short, extend to min(minRun, nRemaining)\n      if (runLen < minRun) {\n        int force = nRemaining <= minRun ? nRemaining : minRun;\n        binarySort(a, lo, lo + force, lo + runLen, c);\n        runLen = force;\n      }\n\n      // Push run onto pending-run stack, and maybe merge\n      sortState.pushRun(lo, runLen);\n      sortState.mergeCollapse();\n\n      // Advance to find next run\n      lo += runLen;\n      nRemaining -= runLen;\n    } while (nRemaining != 0);\n\n    // Merge all remaining runs to complete sort\n    assert lo == hi;\n    sortState.mergeForceCollapse();\n    assert sortState.stackSize == 1;\n  }\n\n  /**\n   * Sorts the specified portion of the specified array using a binary\n   * insertion sort.  This is the best method for sorting small numbers\n   * of elements.  It requires O(n log n) compares, but O(n^2) data\n   * movement (worst case).\n   *\n   * If the initial part of the specified range is already sorted,\n   * this method can take advantage of it: the method assumes that the\n   * elements from index {@code lo}, inclusive, to {@code start},\n   * exclusive are already sorted.\n   *\n   * @param a the array in which a range is to be sorted\n   * @param lo the index of the first element in the range to be sorted\n   * @param hi the index after the last element in the range to be sorted\n   * @param start the index of the first element in the range that is\n   *        not already known to be sorted ({@code lo <= start <= hi})\n   * @param c comparator to used for the sort\n   */\n  @SuppressWarnings(\"fallthrough\")\n  private void binarySort(Buffer a, int lo, int hi, int start, Comparator<? super K> c) {\n    assert lo <= start && start <= hi;\n    if (start == lo)\n      start++;\n\n    K key0 = s.newKey();\n    K key1 = s.newKey();\n\n    Buffer pivotStore = s.allocate(1);\n    for ( ; start < hi; start++) {\n      s.copyElement(a, start, pivotStore, 0);\n      K pivot = s.getKey(pivotStore, 0, key0);\n\n      // Set left (and right) to the index where a[start] (pivot) belongs\n      int left = lo;\n      int right = start;\n      assert left <= right;\n      /*\n       * Invariants:\n       *   pivot >= all in [lo, left).\n       *   pivot <  all in [right, start).\n       */\n      while (left < right) {\n        int mid = (left + right) >>> 1;\n        if (c.compare(pivot, s.getKey(a, mid, key1)) < 0)\n          right = mid;\n        else\n          left = mid + 1;\n      }\n      assert left == right;\n\n      /*\n       * The invariants still hold: pivot >= all in [lo, left) and\n       * pivot < all in [left, start), so pivot belongs at left.  Note\n       * that if there are elements equal to pivot, left points to the\n       * first slot after them -- that's why this sort is stable.\n       * Slide elements over to make room for pivot.\n       */\n      int n = start - left;  // The number of elements to move\n      // Switch is just an optimization for arraycopy in default case\n      switch (n) {\n        case 2:  s.copyElement(a, left + 1, a, left + 2);\n        case 1:  s.copyElement(a, left, a, left + 1);\n          break;\n        default: s.copyRange(a, left, a, left + 1, n);\n      }\n      s.copyElement(pivotStore, 0, a, left);\n    }\n  }\n\n  /**\n   * Returns the length of the run beginning at the specified position in\n   * the specified array and reverses the run if it is descending (ensuring\n   * that the run will always be ascending when the method returns).\n   *\n   * A run is the longest ascending sequence with:\n   *\n   *    a[lo] <= a[lo + 1] <= a[lo + 2] <= ...\n   *\n   * or the longest descending sequence with:\n   *\n   *    a[lo] >  a[lo + 1] >  a[lo + 2] >  ...\n   *\n   * For its intended use in a stable mergesort, the strictness of the\n   * definition of \"descending\" is needed so that the call can safely\n   * reverse a descending sequence without violating stability.\n   *\n   * @param a the array in which a run is to be counted and possibly reversed\n   * @param lo index of the first element in the run\n   * @param hi index after the last element that may be contained in the run.\n  It is required that {@code lo < hi}.\n   * @param c the comparator to used for the sort\n   * @return  the length of the run beginning at the specified position in\n   *          the specified array\n   */\n  private int countRunAndMakeAscending(Buffer a, int lo, int hi, Comparator<? super K> c) {\n    assert lo < hi;\n    int runHi = lo + 1;\n    if (runHi == hi)\n      return 1;\n\n    K key0 = s.newKey();\n    K key1 = s.newKey();\n\n    // Find end of run, and reverse range if descending\n    if (c.compare(s.getKey(a, runHi++, key0), s.getKey(a, lo, key1)) < 0) { // Descending\n      while (runHi < hi && c.compare(s.getKey(a, runHi, key0), s.getKey(a, runHi - 1, key1)) < 0)\n        runHi++;\n      reverseRange(a, lo, runHi);\n    } else {                              // Ascending\n      while (runHi < hi && c.compare(s.getKey(a, runHi, key0), s.getKey(a, runHi - 1, key1)) >= 0)\n        runHi++;\n    }\n\n    return runHi - lo;\n  }\n\n  /**\n   * Reverse the specified range of the specified array.\n   *\n   * @param a the array in which a range is to be reversed\n   * @param lo the index of the first element in the range to be reversed\n   * @param hi the index after the last element in the range to be reversed\n   */\n  private void reverseRange(Buffer a, int lo, int hi) {\n    hi--;\n    while (lo < hi) {\n      s.swap(a, lo, hi);\n      lo++;\n      hi--;\n    }\n  }\n\n  /**\n   * Returns the minimum acceptable run length for an array of the specified\n   * length. Natural runs shorter than this will be extended with\n   * {@link #binarySort}.\n   *\n   * Roughly speaking, the computation is:\n   *\n   *  If n < MIN_MERGE, return n (it's too small to bother with fancy stuff).\n   *  Else if n is an exact power of 2, return MIN_MERGE/2.\n   *  Else return an int k, MIN_MERGE/2 <= k <= MIN_MERGE, such that n/k\n   *   is close to, but strictly less than, an exact power of 2.\n   *\n   * For the rationale, see listsort.txt.\n   *\n   * @param n the length of the array to be sorted\n   * @return the length of the minimum run to be merged\n   */\n  private int minRunLength(int n) {\n    assert n >= 0;\n    int r = 0;      // Becomes 1 if any 1 bits are shifted off\n    while (n >= MIN_MERGE) {\n      r |= (n & 1);\n      n >>= 1;\n    }\n    return n + r;\n  }\n\n  private class SortState {\n\n    /**\n     * The Buffer being sorted.\n     */\n    private final Buffer a;\n\n    /**\n     * Length of the sort Buffer.\n     */\n    private final int aLength;\n\n    /**\n     * The comparator for this sort.\n     */\n    private final Comparator<? super K> c;\n\n    /**\n     * When we get into galloping mode, we stay there until both runs win less\n     * often than MIN_GALLOP consecutive times.\n     */\n    private static final int  MIN_GALLOP = 7;\n\n    /**\n     * This controls when we get *into* galloping mode.  It is initialized\n     * to MIN_GALLOP.  The mergeLo and mergeHi methods nudge it higher for\n     * random data, and lower for highly structured data.\n     */\n    private int minGallop = MIN_GALLOP;\n\n    /**\n     * Maximum initial size of tmp array, which is used for merging.  The array\n     * can grow to accommodate demand.\n     *\n     * Unlike Tim's original C version, we do not allocate this much storage\n     * when sorting smaller arrays.  This change was required for performance.\n     */\n    private static final int INITIAL_TMP_STORAGE_LENGTH = 256;\n\n    /**\n     * Temp storage for merges.\n     */\n    private Buffer tmp; // Actual runtime type will be Object[], regardless of T\n\n    /**\n     * Length of the temp storage.\n     */\n    private int tmpLength = 0;\n\n    /**\n     * A stack of pending runs yet to be merged.  Run i starts at\n     * address base[i] and extends for len[i] elements.  It's always\n     * true (so long as the indices are in bounds) that:\n     *\n     *     runBase[i] + runLen[i] == runBase[i + 1]\n     *\n     * so we could cut the storage for this, but it's a minor amount,\n     * and keeping all the info explicit simplifies the code.\n     */\n    private int stackSize = 0;  // Number of pending runs on stack\n    private final int[] runBase;\n    private final int[] runLen;\n\n    /**\n     * Creates a TimSort instance to maintain the state of an ongoing sort.\n     *\n     * @param a the array to be sorted\n     * @param c the comparator to determine the order of the sort\n     */\n    private SortState(Buffer a, Comparator<? super K> c, int len) {\n      this.aLength = len;\n      this.a = a;\n      this.c = c;\n\n      // Allocate temp storage (which may be increased later if necessary)\n      tmpLength = len < 2 * INITIAL_TMP_STORAGE_LENGTH ? len >>> 1 : INITIAL_TMP_STORAGE_LENGTH;\n      tmp = s.allocate(tmpLength);\n\n      /*\n       * Allocate runs-to-be-merged stack (which cannot be expanded).  The\n       * stack length requirements are described in listsort.txt.  The C\n       * version always uses the same stack length (85), but this was\n       * measured to be too expensive when sorting \"mid-sized\" arrays (e.g.,\n       * 100 elements) in Java.  Therefore, we use smaller (but sufficiently\n       * large) stack lengths for smaller arrays.  The \"magic numbers\" in the\n       * computation below must be changed if MIN_MERGE is decreased.  See\n       * the MIN_MERGE declaration above for more information.\n       */\n      int stackLen = (len <    120  ?  5 :\n                      len <   1542  ? 10 :\n                      len < 119151  ? 19 : 40);\n      runBase = new int[stackLen];\n      runLen = new int[stackLen];\n    }\n\n    /**\n     * Pushes the specified run onto the pending-run stack.\n     *\n     * @param runBase index of the first element in the run\n     * @param runLen  the number of elements in the run\n     */\n    private void pushRun(int runBase, int runLen) {\n      this.runBase[stackSize] = runBase;\n      this.runLen[stackSize] = runLen;\n      stackSize++;\n    }\n\n    /**\n     * Examines the stack of runs waiting to be merged and merges adjacent runs\n     * until the stack invariants are reestablished:\n     *\n     *     1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1]\n     *     2. runLen[i - 2] > runLen[i - 1]\n     *\n     * This method is called each time a new run is pushed onto the stack,\n     * so the invariants are guaranteed to hold for i < stackSize upon\n     * entry to the method.\n     */\n    private void mergeCollapse() {\n      while (stackSize > 1) {\n        int n = stackSize - 2;\n        if ( (n >= 1 && runLen[n-1] <= runLen[n] + runLen[n+1])\n          || (n >= 2 && runLen[n-2] <= runLen[n] + runLen[n-1])) {\n          if (runLen[n - 1] < runLen[n + 1])\n            n--;\n        } else if (runLen[n] > runLen[n + 1]) {\n          break; // Invariant is established\n        }\n        mergeAt(n);\n      }\n    }\n\n    /**\n     * Merges all runs on the stack until only one remains.  This method is\n     * called once, to complete the sort.\n     */\n    private void mergeForceCollapse() {\n      while (stackSize > 1) {\n        int n = stackSize - 2;\n        if (n > 0 && runLen[n - 1] < runLen[n + 1])\n          n--;\n        mergeAt(n);\n      }\n    }\n\n    /**\n     * Merges the two runs at stack indices i and i+1.  Run i must be\n     * the penultimate or antepenultimate run on the stack.  In other words,\n     * i must be equal to stackSize-2 or stackSize-3.\n     *\n     * @param i stack index of the first of the two runs to merge\n     */\n    private void mergeAt(int i) {\n      assert stackSize >= 2;\n      assert i >= 0;\n      assert i == stackSize - 2 || i == stackSize - 3;\n\n      int base1 = runBase[i];\n      int len1 = runLen[i];\n      int base2 = runBase[i + 1];\n      int len2 = runLen[i + 1];\n      assert len1 > 0 && len2 > 0;\n      assert base1 + len1 == base2;\n\n      /*\n       * Record the length of the combined runs; if i is the 3rd-last\n       * run now, also slide over the last run (which isn't involved\n       * in this merge).  The current run (i+1) goes away in any case.\n       */\n      runLen[i] = len1 + len2;\n      if (i == stackSize - 3) {\n        runBase[i + 1] = runBase[i + 2];\n        runLen[i + 1] = runLen[i + 2];\n      }\n      stackSize--;\n\n      K key0 = s.newKey();\n\n      /*\n       * Find where the first element of run2 goes in run1. Prior elements\n       * in run1 can be ignored (because they're already in place).\n       */\n      int k = gallopRight(s.getKey(a, base2, key0), a, base1, len1, 0, c);\n      assert k >= 0;\n      base1 += k;\n      len1 -= k;\n      if (len1 == 0)\n        return;\n\n      /*\n       * Find where the last element of run1 goes in run2. Subsequent elements\n       * in run2 can be ignored (because they're already in place).\n       */\n      len2 = gallopLeft(s.getKey(a, base1 + len1 - 1, key0), a, base2, len2, len2 - 1, c);\n      assert len2 >= 0;\n      if (len2 == 0)\n        return;\n\n      // Merge remaining runs, using tmp array with min(len1, len2) elements\n      if (len1 <= len2)\n        mergeLo(base1, len1, base2, len2);\n      else\n        mergeHi(base1, len1, base2, len2);\n    }\n\n    /**\n     * Locates the position at which to insert the specified key into the\n     * specified sorted range; if the range contains an element equal to key,\n     * returns the index of the leftmost equal element.\n     *\n     * @param key the key whose insertion point to search for\n     * @param a the array in which to search\n     * @param base the index of the first element in the range\n     * @param len the length of the range; must be > 0\n     * @param hint the index at which to begin the search, 0 <= hint < n.\n     *     The closer hint is to the result, the faster this method will run.\n     * @param c the comparator used to order the range, and to search\n     * @return the int k,  0 <= k <= n such that a[b + k - 1] < key <= a[b + k],\n     *    pretending that a[b - 1] is minus infinity and a[b + n] is infinity.\n     *    In other words, key belongs at index b + k; or in other words,\n     *    the first k elements of a should precede key, and the last n - k\n     *    should follow it.\n     */\n    private int gallopLeft(K key, Buffer a, int base, int len, int hint, Comparator<? super K> c) {\n      assert len > 0 && hint >= 0 && hint < len;\n      int lastOfs = 0;\n      int ofs = 1;\n      K key0 = s.newKey();\n\n      if (c.compare(key, s.getKey(a, base + hint, key0)) > 0) {\n        // Gallop right until a[base+hint+lastOfs] < key <= a[base+hint+ofs]\n        int maxOfs = len - hint;\n        while (ofs < maxOfs && c.compare(key, s.getKey(a, base + hint + ofs, key0)) > 0) {\n          lastOfs = ofs;\n          ofs = (ofs << 1) + 1;\n          if (ofs <= 0)   // int overflow\n            ofs = maxOfs;\n        }\n        if (ofs > maxOfs)\n          ofs = maxOfs;\n\n        // Make offsets relative to base\n        lastOfs += hint;\n        ofs += hint;\n      } else { // key <= a[base + hint]\n        // Gallop left until a[base+hint-ofs] < key <= a[base+hint-lastOfs]\n        final int maxOfs = hint + 1;\n        while (ofs < maxOfs && c.compare(key, s.getKey(a, base + hint - ofs, key0)) <= 0) {\n          lastOfs = ofs;\n          ofs = (ofs << 1) + 1;\n          if (ofs <= 0)   // int overflow\n            ofs = maxOfs;\n        }\n        if (ofs > maxOfs)\n          ofs = maxOfs;\n\n        // Make offsets relative to base\n        int tmp = lastOfs;\n        lastOfs = hint - ofs;\n        ofs = hint - tmp;\n      }\n      assert -1 <= lastOfs && lastOfs < ofs && ofs <= len;\n\n      /*\n       * Now a[base+lastOfs] < key <= a[base+ofs], so key belongs somewhere\n       * to the right of lastOfs but no farther right than ofs.  Do a binary\n       * search, with invariant a[base + lastOfs - 1] < key <= a[base + ofs].\n       */\n      lastOfs++;\n      while (lastOfs < ofs) {\n        int m = lastOfs + ((ofs - lastOfs) >>> 1);\n\n        if (c.compare(key, s.getKey(a, base + m, key0)) > 0)\n          lastOfs = m + 1;  // a[base + m] < key\n        else\n          ofs = m;          // key <= a[base + m]\n      }\n      assert lastOfs == ofs;    // so a[base + ofs - 1] < key <= a[base + ofs]\n      return ofs;\n    }\n\n    /**\n     * Like gallopLeft, except that if the range contains an element equal to\n     * key, gallopRight returns the index after the rightmost equal element.\n     *\n     * @param key the key whose insertion point to search for\n     * @param a the array in which to search\n     * @param base the index of the first element in the range\n     * @param len the length of the range; must be > 0\n     * @param hint the index at which to begin the search, 0 <= hint < n.\n     *     The closer hint is to the result, the faster this method will run.\n     * @param c the comparator used to order the range, and to search\n     * @return the int k,  0 <= k <= n such that a[b + k - 1] <= key < a[b + k]\n     */\n    private int gallopRight(K key, Buffer a, int base, int len, int hint, Comparator<? super K> c) {\n      assert len > 0 && hint >= 0 && hint < len;\n\n      int ofs = 1;\n      int lastOfs = 0;\n      K key1 = s.newKey();\n\n      if (c.compare(key, s.getKey(a, base + hint, key1)) < 0) {\n        // Gallop left until a[b+hint - ofs] <= key < a[b+hint - lastOfs]\n        int maxOfs = hint + 1;\n        while (ofs < maxOfs && c.compare(key, s.getKey(a, base + hint - ofs, key1)) < 0) {\n          lastOfs = ofs;\n          ofs = (ofs << 1) + 1;\n          if (ofs <= 0)   // int overflow\n            ofs = maxOfs;\n        }\n        if (ofs > maxOfs)\n          ofs = maxOfs;\n\n        // Make offsets relative to b\n        int tmp = lastOfs;\n        lastOfs = hint - ofs;\n        ofs = hint - tmp;\n      } else { // a[b + hint] <= key\n        // Gallop right until a[b+hint + lastOfs] <= key < a[b+hint + ofs]\n        int maxOfs = len - hint;\n        while (ofs < maxOfs && c.compare(key, s.getKey(a, base + hint + ofs, key1)) >= 0) {\n          lastOfs = ofs;\n          ofs = (ofs << 1) + 1;\n          if (ofs <= 0)   // int overflow\n            ofs = maxOfs;\n        }\n        if (ofs > maxOfs)\n          ofs = maxOfs;\n\n        // Make offsets relative to b\n        lastOfs += hint;\n        ofs += hint;\n      }\n      assert -1 <= lastOfs && lastOfs < ofs && ofs <= len;\n\n      /*\n       * Now a[b + lastOfs] <= key < a[b + ofs], so key belongs somewhere to\n       * the right of lastOfs but no farther right than ofs.  Do a binary\n       * search, with invariant a[b + lastOfs - 1] <= key < a[b + ofs].\n       */\n      lastOfs++;\n      while (lastOfs < ofs) {\n        int m = lastOfs + ((ofs - lastOfs) >>> 1);\n\n        if (c.compare(key, s.getKey(a, base + m, key1)) < 0)\n          ofs = m;          // key < a[b + m]\n        else\n          lastOfs = m + 1;  // a[b + m] <= key\n      }\n      assert lastOfs == ofs;    // so a[b + ofs - 1] <= key < a[b + ofs]\n      return ofs;\n    }\n\n    /**\n     * Merges two adjacent runs in place, in a stable fashion.  The first\n     * element of the first run must be greater than the first element of the\n     * second run (a[base1] > a[base2]), and the last element of the first run\n     * (a[base1 + len1-1]) must be greater than all elements of the second run.\n     *\n     * For performance, this method should be called only when len1 <= len2;\n     * its twin, mergeHi should be called if len1 >= len2.  (Either method\n     * may be called if len1 == len2.)\n     *\n     * @param base1 index of first element in first run to be merged\n     * @param len1  length of first run to be merged (must be > 0)\n     * @param base2 index of first element in second run to be merged\n     *        (must be aBase + aLen)\n     * @param len2  length of second run to be merged (must be > 0)\n     */\n    private void mergeLo(int base1, int len1, int base2, int len2) {\n      assert len1 > 0 && len2 > 0 && base1 + len1 == base2;\n\n      // Copy first run into temp array\n      Buffer a = this.a; // For performance\n      Buffer tmp = ensureCapacity(len1);\n      s.copyRange(a, base1, tmp, 0, len1);\n\n      int cursor1 = 0;       // Indexes into tmp array\n      int cursor2 = base2;   // Indexes int a\n      int dest = base1;      // Indexes int a\n\n      // Move first element of second run and deal with degenerate cases\n      s.copyElement(a, cursor2++, a, dest++);\n      if (--len2 == 0) {\n        s.copyRange(tmp, cursor1, a, dest, len1);\n        return;\n      }\n      if (len1 == 1) {\n        s.copyRange(a, cursor2, a, dest, len2);\n        s.copyElement(tmp, cursor1, a, dest + len2); // Last elt of run 1 to end of merge\n        return;\n      }\n\n      K key0 = s.newKey();\n      K key1 = s.newKey();\n\n      Comparator<? super K> c = this.c;  // Use local variable for performance\n      int minGallop = this.minGallop;    //  \"    \"       \"     \"      \"\n      outer:\n      while (true) {\n        int count1 = 0; // Number of times in a row that first run won\n        int count2 = 0; // Number of times in a row that second run won\n\n        /*\n         * Do the straightforward thing until (if ever) one run starts\n         * winning consistently.\n         */\n        do {\n          assert len1 > 1 && len2 > 0;\n          if (c.compare(s.getKey(a, cursor2, key0), s.getKey(tmp, cursor1, key1)) < 0) {\n            s.copyElement(a, cursor2++, a, dest++);\n            count2++;\n            count1 = 0;\n            if (--len2 == 0)\n              break outer;\n          } else {\n            s.copyElement(tmp, cursor1++, a, dest++);\n            count1++;\n            count2 = 0;\n            if (--len1 == 1)\n              break outer;\n          }\n        } while ((count1 | count2) < minGallop);\n\n        /*\n         * One run is winning so consistently that galloping may be a\n         * huge win. So try that, and continue galloping until (if ever)\n         * neither run appears to be winning consistently anymore.\n         */\n        do {\n          assert len1 > 1 && len2 > 0;\n          count1 = gallopRight(s.getKey(a, cursor2, key0), tmp, cursor1, len1, 0, c);\n          if (count1 != 0) {\n            s.copyRange(tmp, cursor1, a, dest, count1);\n            dest += count1;\n            cursor1 += count1;\n            len1 -= count1;\n            if (len1 <= 1) // len1 == 1 || len1 == 0\n              break outer;\n          }\n          s.copyElement(a, cursor2++, a, dest++);\n          if (--len2 == 0)\n            break outer;\n\n          count2 = gallopLeft(s.getKey(tmp, cursor1, key0), a, cursor2, len2, 0, c);\n          if (count2 != 0) {\n            s.copyRange(a, cursor2, a, dest, count2);\n            dest += count2;\n            cursor2 += count2;\n            len2 -= count2;\n            if (len2 == 0)\n              break outer;\n          }\n          s.copyElement(tmp, cursor1++, a, dest++);\n          if (--len1 == 1)\n            break outer;\n          minGallop--;\n        } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);\n        if (minGallop < 0)\n          minGallop = 0;\n        minGallop += 2;  // Penalize for leaving gallop mode\n      }  // End of \"outer\" loop\n      this.minGallop = minGallop < 1 ? 1 : minGallop;  // Write back to field\n\n      if (len1 == 1) {\n        assert len2 > 0;\n        s.copyRange(a, cursor2, a, dest, len2);\n        s.copyElement(tmp, cursor1, a, dest + len2); //  Last elt of run 1 to end of merge\n      } else if (len1 == 0) {\n        throw new IllegalArgumentException(\n            \"Comparison method violates its general contract!\");\n      } else {\n        assert len2 == 0;\n        assert len1 > 1;\n        s.copyRange(tmp, cursor1, a, dest, len1);\n      }\n    }\n\n    /**\n     * Like mergeLo, except that this method should be called only if\n     * len1 >= len2; mergeLo should be called if len1 <= len2.  (Either method\n     * may be called if len1 == len2.)\n     *\n     * @param base1 index of first element in first run to be merged\n     * @param len1  length of first run to be merged (must be > 0)\n     * @param base2 index of first element in second run to be merged\n     *        (must be aBase + aLen)\n     * @param len2  length of second run to be merged (must be > 0)\n     */\n    private void mergeHi(int base1, int len1, int base2, int len2) {\n      assert len1 > 0 && len2 > 0 && base1 + len1 == base2;\n\n      // Copy second run into temp array\n      Buffer a = this.a; // For performance\n      Buffer tmp = ensureCapacity(len2);\n      s.copyRange(a, base2, tmp, 0, len2);\n\n      int cursor1 = base1 + len1 - 1;  // Indexes into a\n      int cursor2 = len2 - 1;          // Indexes into tmp array\n      int dest = base2 + len2 - 1;     // Indexes into a\n\n      K key0 = s.newKey();\n      K key1 = s.newKey();\n\n      // Move last element of first run and deal with degenerate cases\n      s.copyElement(a, cursor1--, a, dest--);\n      if (--len1 == 0) {\n        s.copyRange(tmp, 0, a, dest - (len2 - 1), len2);\n        return;\n      }\n      if (len2 == 1) {\n        dest -= len1;\n        cursor1 -= len1;\n        s.copyRange(a, cursor1 + 1, a, dest + 1, len1);\n        s.copyElement(tmp, cursor2, a, dest);\n        return;\n      }\n\n      Comparator<? super K> c = this.c;  // Use local variable for performance\n      int minGallop = this.minGallop;    //  \"    \"       \"     \"      \"\n      outer:\n      while (true) {\n        int count1 = 0; // Number of times in a row that first run won\n        int count2 = 0; // Number of times in a row that second run won\n\n        /*\n         * Do the straightforward thing until (if ever) one run\n         * appears to win consistently.\n         */\n        do {\n          assert len1 > 0 && len2 > 1;\n          if (c.compare(s.getKey(tmp, cursor2, key0), s.getKey(a, cursor1, key1)) < 0) {\n            s.copyElement(a, cursor1--, a, dest--);\n            count1++;\n            count2 = 0;\n            if (--len1 == 0)\n              break outer;\n          } else {\n            s.copyElement(tmp, cursor2--, a, dest--);\n            count2++;\n            count1 = 0;\n            if (--len2 == 1)\n              break outer;\n          }\n        } while ((count1 | count2) < minGallop);\n\n        /*\n         * One run is winning so consistently that galloping may be a\n         * huge win. So try that, and continue galloping until (if ever)\n         * neither run appears to be winning consistently anymore.\n         */\n        do {\n          assert len1 > 0 && len2 > 1;\n          count1 = len1 - gallopRight(s.getKey(tmp, cursor2, key0), a, base1, len1, len1 - 1, c);\n          if (count1 != 0) {\n            dest -= count1;\n            cursor1 -= count1;\n            len1 -= count1;\n            s.copyRange(a, cursor1 + 1, a, dest + 1, count1);\n            if (len1 == 0)\n              break outer;\n          }\n          s.copyElement(tmp, cursor2--, a, dest--);\n          if (--len2 == 1)\n            break outer;\n\n          count2 = len2 - gallopLeft(s.getKey(a, cursor1, key0), tmp, 0, len2, len2 - 1, c);\n          if (count2 != 0) {\n            dest -= count2;\n            cursor2 -= count2;\n            len2 -= count2;\n            s.copyRange(tmp, cursor2 + 1, a, dest + 1, count2);\n            if (len2 <= 1)  // len2 == 1 || len2 == 0\n              break outer;\n          }\n          s.copyElement(a, cursor1--, a, dest--);\n          if (--len1 == 0)\n            break outer;\n          minGallop--;\n        } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);\n        if (minGallop < 0)\n          minGallop = 0;\n        minGallop += 2;  // Penalize for leaving gallop mode\n      }  // End of \"outer\" loop\n      this.minGallop = minGallop < 1 ? 1 : minGallop;  // Write back to field\n\n      if (len2 == 1) {\n        assert len1 > 0;\n        dest -= len1;\n        cursor1 -= len1;\n        s.copyRange(a, cursor1 + 1, a, dest + 1, len1);\n        s.copyElement(tmp, cursor2, a, dest); // Move first elt of run2 to front of merge\n      } else if (len2 == 0) {\n        throw new IllegalArgumentException(\n            \"Comparison method violates its general contract!\");\n      } else {\n        assert len1 == 0;\n        assert len2 > 0;\n        s.copyRange(tmp, 0, a, dest - (len2 - 1), len2);\n      }\n    }\n\n    /**\n     * Ensures that the external array tmp has at least the specified\n     * number of elements, increasing its size if necessary.  The size\n     * increases exponentially to ensure amortized linear time complexity.\n     *\n     * @param minCapacity the minimum required capacity of the tmp array\n     * @return tmp, whether or not it grew\n     */\n    private Buffer ensureCapacity(int minCapacity) {\n      if (tmpLength < minCapacity) {\n        // Compute smallest power of 2 > minCapacity\n        int newSize = minCapacity;\n        newSize |= newSize >> 1;\n        newSize |= newSize >> 2;\n        newSize |= newSize >> 4;\n        newSize |= newSize >> 8;\n        newSize |= newSize >> 16;\n        newSize++;\n\n        if (newSize < 0) // Not bloody likely!\n          newSize = minCapacity;\n        else\n          newSize = Math.min(newSize, aLength >>> 1);\n\n        tmp = s.allocate(newSize);\n        tmpLength = newSize;\n      }\n      return tmp;\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/UnsafeExternalRowSorter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\nimport io.mycat.memory.MyCatMemory;\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.row.StructType;\nimport io.mycat.memory.unsafe.row.UnsafeRow;\nimport io.mycat.sqlengine.mpp.OrderCol;\nimport io.mycat.sqlengine.mpp.RowDataPacketSorter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.annotation.Nonnull;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Iterator;\nimport java.util.List;\n\npublic final class UnsafeExternalRowSorter {\n\n    private final Logger logger = LoggerFactory.getLogger(UnsafeExternalSorter.class);\n\n  private long numRowsInserted = 0;\n  private final StructType schema;\n  private final PrefixComputer prefixComputer;\n  private final UnsafeExternalSorter sorter;\n  private final  PrefixComparator prefixComparator;\n  private final RecordComparator recordComparator;\n\n\n  public abstract static class PrefixComputer {\n    protected abstract long computePrefix(UnsafeRow row) throws UnsupportedEncodingException;\n  }\n\n  public UnsafeExternalRowSorter(DataNodeMemoryManager dataNodeMemoryManager,\n                                 @Nonnull MyCatMemory myCatMemory,\n                                 StructType schema,\n                                 PrefixComparator prefixComparator,\n                                 PrefixComputer prefixComputer,\n                                 long pageSizeBytes,\n                                 boolean canUseRadixSort,\n                                 boolean enableSort) throws IOException {\n    this.schema = schema;\n    this.prefixComputer = prefixComputer;\n      this.prefixComparator = prefixComparator;\n      this.recordComparator =  new RowComparator(schema);\n    sorter = UnsafeExternalSorter.create(\n            dataNodeMemoryManager,\n            myCatMemory.getBlockManager(),\n           myCatMemory.getSerializerManager(),\n            recordComparator,\n      prefixComparator,\n      myCatMemory.getConf().getSizeAsBytes(\"mycat.pointer.array.len\",\"1K\"),\n      pageSizeBytes,\n      canUseRadixSort,\n      enableSort);\n  }\n\n\n  public void insertRow(UnsafeRow row) throws IOException {\n    final long prefix = prefixComputer.computePrefix(row);\n\n      sorter.insertRecord(\n      row.getBaseObject(),\n      row.getBaseOffset(),\n      row.getSizeInBytes(),\n      prefix);\n\n    numRowsInserted++;\n  }\n    /**\n     * Return total rows\n     */\n    public long getNumRowsInserted() {\n        return numRowsInserted;\n    }\n  /**\n   * Return the peak memory used so far, in bytes.\n   */\n  public long getPeakMemoryUsage() {\n    return sorter.getPeakMemoryUsedBytes();\n  }\n\n  /**\n   * @return the total amount of time spent sorting data (in-memory only).\n   */\n  public long getSortTimeNanos() {\n    return sorter.getSortTimeNanos();\n  }\n\n  public void cleanupResources() {\n      sorter.cleanupResources();\n  }\n\n  public Iterator<UnsafeRow> sort() throws IOException {\n    try {\n      final UnsafeSorterIterator sortedIterator = sorter.getSortedIterator();\n      if (!sortedIterator.hasNext()) {\n        cleanupResources();\n      }\n\n      return new AbstractScalaRowIterator<UnsafeRow>() {\n\n        private final int numFields = schema.length();\n        private UnsafeRow row = new UnsafeRow(numFields);\n\n        @Override\n        public boolean hasNext() {\n          return sortedIterator.hasNext();\n        }\n\n        @Override\n        public UnsafeRow next() {\n          try {\n            sortedIterator.loadNext();\n            row.pointTo(sortedIterator.getBaseObject(), sortedIterator.getBaseOffset(), sortedIterator.getRecordLength());\n            if (!hasNext()) {\n              UnsafeRow copy = row.copy(); // so that we don't have dangling pointers to freed page\n              row = null; // so that we don't keep references to the base object\n              cleanupResources();\n              return copy;\n            } else {\n              return row;\n            }\n          } catch (IOException e) {\n            cleanupResources();\n            // Scala iterators don't declare any checked exceptions, so we need to use this hack\n            // to re-throw the exception:\n            Platform.throwException(e);\n          }\n          throw new RuntimeException(\"Exception should have been re-thrown in next()\");\n        }\n\n        @Override\n        public void remove() {\n\n        }\n      };\n    } catch (IOException e) {\n      cleanupResources();\n      throw e;\n    }\n  }\n\n\n  public UnsafeSorterIterator getRowUnsafeSorterIterator() throws IOException{\n    return sorter.getSortedIterator();\n  }\n\n\n    public Iterator<UnsafeRow> mergerSort(List<UnsafeSorterIterator> list) throws IOException {\n\n        UnsafeRowsMerger unsafeRowsMerger = new UnsafeRowsMerger(recordComparator,prefixComparator,list.size());\n\n        for (int i = 0; i <list.size() ; i++) {\n            unsafeRowsMerger.addSpillIfNotEmpty(list.get(i));\n        }\n\n        try {\n            final UnsafeSorterIterator sortedIterator = unsafeRowsMerger.getSortedIterator();\n\n            if (!sortedIterator.hasNext()) {\n                cleanupResources();\n            }\n\n            return new AbstractScalaRowIterator<UnsafeRow>() {\n\n                private final int numFields = schema.length();\n                private UnsafeRow row = new UnsafeRow(numFields);\n\n                @Override\n                public boolean hasNext() {\n                    return sortedIterator.hasNext();\n                }\n\n                @Override\n                public UnsafeRow next() {\n                    try {\n                        sortedIterator.loadNext();\n                        row.pointTo(\n                                sortedIterator.getBaseObject(),\n                                sortedIterator.getBaseOffset(),\n                                sortedIterator.getRecordLength());\n                        if (!hasNext()) {\n                            UnsafeRow copy = row.copy(); // so that we don't have dangling pointers to freed page\n                            row = null; // so that we don't keep references to the base object\n                            cleanupResources();\n                            return copy;\n                        } else {\n                            return row;\n                        }\n                    } catch (IOException e) {\n                        cleanupResources();\n                        // Scala iterators don't declare any checked exceptions, so we need to use this hack\n                        // to re-throw the exception:\n                        Platform.throwException(e);\n                    }\n                    throw new RuntimeException(\"Exception should have been re-thrown in next()\");\n                }\n\n                @Override\n                public void remove() {\n\n                }\n            };\n        } catch (IOException e) {\n            cleanupResources();\n            throw e;\n        }\n    }\n\n\n  public Iterator<UnsafeRow> sort(Iterator<UnsafeRow> inputIterator) throws IOException {\n\n    while (inputIterator.hasNext()) {\n      insertRow(inputIterator.next());\n    }\n\n    return sort();\n  }\n\n\n\n  private static final class RowComparator extends RecordComparator {\n    private final int numFields;\n    private final UnsafeRow row1;\n    private final UnsafeRow row2;\n    private final StructType schema;\n\n    RowComparator(StructType schema) {\n\n      assert schema.length()>=0;\n\n      this.schema = schema;\n      this.numFields = schema.length();\n      this.row1 = new UnsafeRow(numFields);\n      this.row2 = new UnsafeRow(numFields);\n    }\n\n    @Override\n    public int compare(Object baseObj1, long baseOff1, Object baseObj2, long baseOff2) {\n      OrderCol[] orderCols =  schema.getOrderCols();\n\n      if(orderCols == null){\n          return 0;\n      }\n\n      /**取出一行数据*/\n      row1.pointTo(baseObj1, baseOff1, -1);\n      row2.pointTo(baseObj2, baseOff2, -1);\n      int cmp = 0;\n      int len = orderCols.length;\n\n      int type = OrderCol.COL_ORDER_TYPE_ASC; /**升序*/\n\n      for (int i = 0; i < len; i++) {\n        int colIndex = orderCols[i].colMeta.colIndex;\n        /**取出一行数据中的列值，进行大小比对*/\n        byte[] left = null;\n        byte[] right = null;\n\n\n\n          if(!row1.isNullAt(colIndex)) {\n              left = row1.getBinary(colIndex);\n          }else {\n              left = new byte[1];\n              left[0] = UnsafeRow.NULL_MARK;\n          }\n\n\n          if(!row2.isNullAt(colIndex)) {\n              right = row2.getBinary(colIndex);\n          }else {\n              right = new byte[1];\n              right[0] = UnsafeRow.NULL_MARK;\n          }\n\n        if (orderCols[i].orderType == type) {\n          cmp = RowDataPacketSorter.compareObject(left, right, orderCols[i]);\n        } else {\n          cmp = RowDataPacketSorter.compareObject(right, left, orderCols[i]);\n        }\n        if (cmp != 0)\n          return cmp;\n      }\n      return cmp;\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/UnsafeExternalSorter.java",
    "content": "\n\npackage io.mycat.memory.unsafe.utils.sort;\n\nimport com.google.common.annotations.VisibleForTesting;\n\nimport io.mycat.MycatServer;\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.array.LongArray;\nimport io.mycat.memory.unsafe.memory.MemoryBlock;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.MemoryConsumer;\nimport io.mycat.memory.unsafe.storage.DataNodeDiskManager;\nimport io.mycat.memory.unsafe.storage.SerializerManager;\nimport io.mycat.memory.unsafe.utils.JavaUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.annotation.Nullable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.LinkedList;\nimport java.util.Queue;\n\n/**\n * External sorter based on {@link UnsafeInMemorySorter}.\n */\npublic final class UnsafeExternalSorter extends MemoryConsumer {\n\n  private final Logger logger = LoggerFactory.getLogger(UnsafeExternalSorter.class);\n\n  @Nullable\n  private final PrefixComparator prefixComparator;\n  @Nullable\n  private final RecordComparator recordComparator;\n\n\n  private final DataNodeMemoryManager dataNodeMemoryManager;\n  private final DataNodeDiskManager blockManager;\n  private final SerializerManager serializerManager;\n\n\n  /** The buffer size to use when writing spills using DiskRowWriter */\n  private final int fileBufferSizeBytes;\n\n  /**\n   * Memory pages that hold the records being sorted. The pages in this list are freed when\n   * spilling, although in principle we could recycle these pages across spills (on the other hand,\n   * this might not be necessary if we maintained a pool of re-usable pages in the DataNodeMemoryManager\n   * itself).\n   */\n  private final LinkedList<MemoryBlock> allocatedPages = new LinkedList<MemoryBlock>();\n\n  private final LinkedList<UnsafeSorterSpillWriter> spillWriters = new LinkedList<UnsafeSorterSpillWriter>();\n\n  // These variables are reset after spilling:\n  @Nullable\n  private volatile UnsafeInMemorySorter inMemSorter;\n\n  private MemoryBlock currentPage = null;\n  private long pageCursor = -1;\n  private long peakMemoryUsedBytes = 0;\n  private long totalSpillBytes = 0L;\n  private long totalSortTimeNanos = 0L;\n  private volatile SpillableIterator readingIterator = null;\n\n  public static UnsafeExternalSorter createWithExistingInMemorySorter(\n      DataNodeMemoryManager dataNodeMemoryManager,\n      DataNodeDiskManager blockManager,\n      SerializerManager serializerManager,\n      RecordComparator recordComparator,\n      PrefixComparator prefixComparator,\n      int initialSize,\n      long pageSizeBytes,\n      UnsafeInMemorySorter inMemorySorter,boolean enableSort) throws IOException {\n\n    UnsafeExternalSorter sorter = new UnsafeExternalSorter(dataNodeMemoryManager, blockManager,\n      serializerManager,recordComparator, prefixComparator, initialSize,\n        pageSizeBytes, inMemorySorter, false /* ignored */,enableSort);\n\n    sorter.spill(Long.MAX_VALUE, sorter);\n    // The external sorter will be used to insert records, in-memory sorter is not needed.\n    sorter.inMemSorter = null;\n    return sorter;\n  }\n\n  public static UnsafeExternalSorter create(\n          DataNodeMemoryManager dataNodeMemoryManager,\n          DataNodeDiskManager blockManager,\n          SerializerManager serializerManager,\n          RecordComparator recordComparator,\n          PrefixComparator prefixComparator,\n          long initialSize,\n          long pageSizeBytes,\n          boolean canUseRadixSort,\n          boolean enableSort) {\n\n    return new UnsafeExternalSorter(dataNodeMemoryManager, blockManager, serializerManager, recordComparator, prefixComparator, initialSize, pageSizeBytes, null,\n      canUseRadixSort,enableSort);\n\n  }\n\n  private UnsafeExternalSorter(\n      DataNodeMemoryManager dataNodeMemoryManager,\n      DataNodeDiskManager blockManager,\n      SerializerManager serializerManager,\n      RecordComparator recordComparator,\n      PrefixComparator prefixComparator,\n      long initialSize,\n      long pageSizeBytes,\n      @Nullable UnsafeInMemorySorter existingInMemorySorter,\n      boolean canUseRadixSort,boolean enableSort) {\n\n    super(dataNodeMemoryManager, pageSizeBytes);\n\n    this.dataNodeMemoryManager = dataNodeMemoryManager;\n    this.blockManager = blockManager;\n    this.serializerManager = serializerManager;\n    this.recordComparator = recordComparator;\n    this.prefixComparator = prefixComparator;\n\n\n    if(MycatServer.getInstance().getMyCatMemory() != null){\n         this.fileBufferSizeBytes = (int) MycatServer.getInstance().\n              getMyCatMemory().getConf().getSizeAsBytes(\"mycat.merge.file.buffer\", \"32k\");\n    }else{\n      this.fileBufferSizeBytes = 32*1024;\n    }\n\n    if (existingInMemorySorter == null) {\n      this.inMemSorter = new UnsafeInMemorySorter(\n        this, dataNodeMemoryManager, recordComparator, prefixComparator, initialSize, canUseRadixSort,enableSort);\n    } else {\n      this.inMemSorter = existingInMemorySorter;\n    }\n\n    this.peakMemoryUsedBytes = getMemoryUsage();\n  }\n\n  /**\n   * Marks the current page as no-more-space-available, and as a result, either allocate a\n   * new page or spill when we see the next record.\n   */\n  @VisibleForTesting\n  public void closeCurrentPage() {\n    if (currentPage != null) {\n      pageCursor = currentPage.getBaseOffset() + currentPage.size();\n    }\n  }\n\n  /**\n   * Sort and spill the current records in response to memory pressure.\n   */\n  @Override\n  public long spill(long size, MemoryConsumer trigger) throws IOException {\n    if (trigger != this) {\n      if (readingIterator != null) {\n        return readingIterator.spill();\n      }\n      return 0L; // this should throw exception\n    }\n\n    if (inMemSorter == null || inMemSorter.numRecords() <= 0) {\n      return 0L;\n    }\n\n    logger.info(\"Thread\"  +   Thread.currentThread().getId() +\" spilling sort data of \"+  JavaUtils.bytesToString(getMemoryUsage())\n            +\" to disk (\"+ spillWriters.size()+\" times so far)\");\n\n    // We only write out contents of the inMemSorter if it is not empty.\n    if (inMemSorter.numRecords() > 0) {\n\n      /**\n       * 创建一个写外存的SpillWriter，当前内存数据已经排序了，直接写到磁盘中.\n       */\n      final UnsafeSorterSpillWriter spillWriter = new UnsafeSorterSpillWriter(blockManager, fileBufferSizeBytes,/**writeMetrics,*/ inMemSorter.numRecords());\n\n       /**\n       * 添加到SpillWriter列表中，标志了有多少 spillWriters.size()写到磁盘中了。\n       */\n      spillWriters.add(spillWriter);\n\n        /**\n         * 获取本次内存中排序的迭代器，这个函数执行In Memory Sort use time sorter 或者 radix sorter\n         */\n      final UnsafeSorterIterator sortedRecords = inMemSorter.getSortedIterator();\n\n       /**\n       * 一条一条记录写入磁盘\n       */\n      while (sortedRecords.hasNext()) {\n        /**\n         *\n         */\n        sortedRecords.loadNext();\n        /**\n         * 获取当前记录的起始对象实例，on-heap为obj，off-heap为null\n         */\n        final Object baseObject = sortedRecords.getBaseObject();\n\n        /**\n         * 获取当前记录的相对起始对象实例地址偏移量\n         */\n        final long baseOffset = sortedRecords.getBaseOffset();\n\n        /**\n         * 当前记录的长度\n         */\n        final int recordLength = sortedRecords.getRecordLength();\n        /**\n         * 把数据写入磁盘写入器中 Write a record to a spill file.\n         */\n        spillWriter.write(baseObject, baseOffset, recordLength, sortedRecords.getKeyPrefix());\n      }\n\n        /**\n         * 关闭spillWriter\n         */\n      spillWriter.close();\n    }\n\n    /**\n     * 释放当前sorter所占的内存数据\n     */\n    final long spillSize = freeMemory();\n    // Note that this is more-or-less going to be a multiple of the page size, so wasted space in\n    // pages will currently be counted as memory spilled even though that space isn't actually\n    // written to disk. This also counts the space needed to store the sorter's pointer array.\n    inMemSorter.reset();\n    // Reset the in-memory sorter's pointer array only after freeing up the memory pages holding the\n    // records. Otherwise, if the task is over allocated memory, then without freeing the memory\n    // pages, we might not be able to get memory for the pointer array.\n\n    totalSpillBytes += spillSize;\n    return spillSize;\n  }\n\n  /**\n   * Return the total memory usage of this sorter, including the data pages and the sorter's pointer\n   * array.\n   */\n  private long getMemoryUsage() {\n    long totalPageSize = 0;\n    for (MemoryBlock page : allocatedPages) {\n      totalPageSize += page.size();\n    }\n    return ((inMemSorter == null) ? 0 : inMemSorter.getMemoryUsage()) + totalPageSize;\n  }\n\n  private void updatePeakMemoryUsed() {\n    long mem = getMemoryUsage();\n    if (mem > peakMemoryUsedBytes) {\n      peakMemoryUsedBytes = mem;\n    }\n  }\n\n  /**\n   * Return the peak memory used so far, in bytes.\n   */\n  public long getPeakMemoryUsedBytes() {\n    updatePeakMemoryUsed();\n    return peakMemoryUsedBytes;\n  }\n\n  /**\n   * @return the total amount of time spent sorting data (in-memory only).\n   */\n  public long getSortTimeNanos() {\n    UnsafeInMemorySorter sorter = inMemSorter;\n    if (sorter != null) {\n      return sorter.getSortTimeNanos();\n    }\n    return totalSortTimeNanos;\n  }\n\n  /**\n   * Return the total number of bytes that has been spilled into disk so far.\n   */\n  public long getSpillSize() {\n    return totalSpillBytes;\n  }\n\n  @VisibleForTesting\n  public int getNumberOfAllocatedPages() {\n    return allocatedPages.size();\n  }\n\n  /**\n   * Free this sorter's data pages.\n   *\n   * @return the number of bytes freed.\n   */\n  private long freeMemory() {\n    updatePeakMemoryUsed();\n    long memoryFreed = 0;\n    for (MemoryBlock block : allocatedPages) {\n      memoryFreed += block.size();\n      freePage(block);\n    }\n    allocatedPages.clear();\n    currentPage = null;\n    pageCursor = 0;\n    return memoryFreed;\n  }\n\n  /**\n   * Deletes any spill files created by this sorter.\n   */\n  private void deleteSpillFiles() {\n    for (UnsafeSorterSpillWriter spill : spillWriters) {\n      File file = spill.getFile();\n      if(file == null)\n        continue;\n      try {\n        JavaUtils.deleteRecursively(file.getParentFile().getParentFile());\n      } catch (IOException e) {\n        logger.error(e.getMessage());\n      }\n\n      if (file.exists()) {\n        if (!file.delete()) {\n          logger.error(\"Was unable to delete spill file {}\", file.getAbsolutePath());\n        }\n      }\n    }\n  }\n\n  /**\n   * Frees this sorter's in-memory data structures and cleans up its spill files.\n   */\n  public void cleanupResources() {\n    synchronized (this) {\n      deleteSpillFiles();\n      freeMemory();\n      if (inMemSorter != null) {\n        inMemSorter.free();\n        inMemSorter = null;\n      }\n    }\n  }\n\n  /**\n   * Checks whether there is enough space to insert an additional record in to the sort pointer\n   * array and grows the array if additional space is required. If the required space cannot be\n   * obtained, then the in-memory data will be spilled to disk.\n   */\n  private void growPointerArrayIfNecessary() throws IOException {\n    assert(inMemSorter != null);\n    if (!inMemSorter.hasSpaceForAnotherRecord()) {\n      long used = inMemSorter.getMemoryUsage();\n      LongArray array;\n      try {\n        // could trigger spilling\n        array = allocateLongArray(used / 8 * 2);\n      } catch (OutOfMemoryError e) {\n        // should have trigger spilling\n        if (!inMemSorter.hasSpaceForAnotherRecord()) {\n          logger.error(\"Unable to grow the pointer array\");\n          throw e;\n        }\n        return;\n      }\n      // check if spilling is triggered or not\n      if (inMemSorter.hasSpaceForAnotherRecord()) {\n        freeLongArray(array);\n      } else {\n        inMemSorter.expandPointerArray(array);\n      }\n    }\n  }\n\n  /**\n   * Allocates more memory in order to insert an additional record. This will request additional\n   * memory from the memory manager and spill if the requested memory can not be obtained.\n   *\n   * @param required the required space in the data page, in bytes, including space for storing\n   *                      the record size. This must be less than or equal to the page size (records\n   *                      that exceed the page size are handled via a different code path which uses\n   *                      special overflow pages).\n   */\n  private void acquireNewPageIfNecessary(int required) {\n    if (currentPage == null ||\n      pageCursor + required > currentPage.getBaseOffset() + currentPage.size()) {\n      // TODO: try to find space on previous pages\n      currentPage = allocatePage(required);\n      pageCursor = currentPage.getBaseOffset();\n      allocatedPages.add(currentPage);\n    }\n  }\n\n  /**\n   * Write a record to the sorter.\n   */\n  public void insertRecord(Object recordBase, long recordOffset, int length, long prefix)\n    throws IOException {\n\n    growPointerArrayIfNecessary();\n    // Need 4 bytes to store the record length.\n    final int required = length + 4;\n    acquireNewPageIfNecessary(required);\n\n    final Object base = currentPage.getBaseObject();\n\n    final long recordAddress = dataNodeMemoryManager.encodePageNumberAndOffset(currentPage,pageCursor);\n    Platform.putInt(base, pageCursor, length);\n    pageCursor += 4;\n    Platform.copyMemory(recordBase,recordOffset,base,pageCursor,length);\n    pageCursor += length;\n    assert(inMemSorter != null);\n    inMemSorter.insertRecord(recordAddress,prefix);\n  }\n\n  /**\n   * Write a key-value record to the sorter. The key and value will be put together in-memory,\n   * using the following format:\n   *\n   * record length (4 bytes), key length (4 bytes), key data, value data\n   *\n   * record length = key length + value length + 4\n   */\n  public void insertKVRecord(Object keyBase, long keyOffset, int keyLen,\n      Object valueBase, long valueOffset, int valueLen, long prefix)\n    throws IOException {\n\n    growPointerArrayIfNecessary();\n    final int required = keyLen + valueLen + 4 + 4;\n    acquireNewPageIfNecessary(required);\n\n    /**\n     * 数据k-v插入currentPage(MemoryBlock)页内，当前插入位置pageCursor\n     */\n    final Object base = currentPage.getBaseObject();\n    /**\n     * 通过currentPage和pageCursor页内偏移量，codec一个地址处理，该条记录存数据的\n     * 存数据的起始位置\n     */\n    final long recordAddress = dataNodeMemoryManager.encodePageNumberAndOffset(currentPage,pageCursor);\n\n    /**\n     * 一条记录的总长度=keyLen + valueLen + record length (一般是int类型4个字节)\n     */\n    Platform.putInt(base,pageCursor, keyLen + valueLen + 4/**record length所占的长度*/);\n\n      /**\n       * 移动4个bytes\n       */\n    pageCursor += 4;\n    /**\n     * 存key len的size\n     */\n    Platform.putInt(base,pageCursor, keyLen);\n\n    /**\n     * 移动4个bytes\n     */\n    pageCursor += 4;\n\n    /**\n     * 存key的值\n     */\n    Platform.copyMemory(keyBase, keyOffset, base, pageCursor, keyLen);\n    /**\n     * 移动keyLen个bytes\n     */\n    pageCursor += keyLen;\n\n    /**\n     * 存value的值\n     */\n    Platform.copyMemory(valueBase, valueOffset, base, pageCursor, valueLen);\n\n    /**\n     * 移动valueLen个bytes\n     */\n    pageCursor += valueLen;\n\n    assert(inMemSorter != null);\n    /**\n     * 把对应的指针插入到longArray数组中，\n     * longArray存指向Page内一个指针的所存储的值\n     */\n    inMemSorter.insertRecord(recordAddress, prefix);\n  }\n\n  /**\n   * Merges another UnsafeExternalSorters into this one, the other one will be emptied.\n   *\n   * @throws IOException\n   */\n  public void merge(UnsafeExternalSorter other) throws IOException {\n    other.spill();\n    spillWriters.addAll(other.spillWriters);\n    // remove them from `spillWriters`, or the files will be deleted in `cleanupResources`.\n    other.spillWriters.clear();\n    other.cleanupResources();\n  }\n\n  /**\n   * SpillableIterator是一个支持内存+外存排序的迭代器\n   * Returns a sorted iterator. It is the caller's responsibility to call `cleanupResources()`\n   * after consuming this iterator.\n   */\n\n  public UnsafeSorterIterator getSortedIterator() throws IOException {\n    assert(recordComparator != null);\n    if (spillWriters.isEmpty()) {\n      assert(inMemSorter != null);\n      readingIterator = new SpillableIterator(inMemSorter.getSortedIterator());\n      return readingIterator;\n    } else {\n      /**\n       * 合并多个UnsafeSorterSpillWriter对应的文件，进行排序????\n       */\n      final UnsafeSorterSpillMerger spillMerger =\n        new UnsafeSorterSpillMerger(recordComparator, prefixComparator, spillWriters.size());\n\n      for (UnsafeSorterSpillWriter spillWriter : spillWriters) {\n        /**\n         * 通过UnsafeSorterSpillReader迭代器放入要合并的UnsafeSorterSpillMerger中\n         */\n        spillMerger.addSpillIfNotEmpty(spillWriter.getReader(serializerManager));\n      }\n      if (inMemSorter != null) {\n        readingIterator = new SpillableIterator(inMemSorter.getSortedIterator());\n        spillMerger.addSpillIfNotEmpty(readingIterator);\n      }\n      /**\n       * 最终调用排序器排序，重点分析函数\n       */\n      return spillMerger.getSortedIterator();\n    }\n  }\n\n  /**\n   * An UnsafeSorterIterator that support spilling.\n   */\n  public class SpillableIterator extends UnsafeSorterIterator {\n    private UnsafeSorterIterator upstream;\n    private UnsafeSorterIterator nextUpstream = null;\n    private MemoryBlock lastPage = null;\n    private boolean loaded = false;\n    private int numRecords = 0;\n\n    SpillableIterator(UnsafeInMemorySorter.SortedIterator inMemIterator) {\n      this.upstream = inMemIterator;\n      this.numRecords = inMemIterator.getNumRecords();\n    }\n\n    public int getNumRecords() {\n      return numRecords;\n    }\n\n    public long spill() throws IOException {\n      synchronized (this) {\n        if (!(upstream instanceof UnsafeInMemorySorter.SortedIterator && nextUpstream == null\n          && numRecords > 0)) {\n          return 0L;\n        }\n\n        UnsafeInMemorySorter.SortedIterator inMemIterator =\n          ((UnsafeInMemorySorter.SortedIterator) upstream).clone();\n\n        // Iterate over the records that have not been returned and spill them.\n        final UnsafeSorterSpillWriter spillWriter =\n          new UnsafeSorterSpillWriter(blockManager, fileBufferSizeBytes,/**writeMetrics,*/ numRecords);\n        while (inMemIterator.hasNext()) {\n          inMemIterator.loadNext();\n          final Object baseObject = inMemIterator.getBaseObject();\n          final long baseOffset = inMemIterator.getBaseOffset();\n          final int recordLength = inMemIterator.getRecordLength();\n          spillWriter.write(baseObject, baseOffset, recordLength, inMemIterator.getKeyPrefix());\n        }\n        spillWriter.close();\n        spillWriters.add(spillWriter);\n        nextUpstream = spillWriter.getReader(serializerManager);\n\n        long released = 0L;\n        synchronized (UnsafeExternalSorter.this) {\n          // release the pages except the one that is used. There can still be a caller that\n          // is accessing the current record. We free this page in that caller's next loadNext()\n          // call.\n          for (MemoryBlock page : allocatedPages) {\n            if (!loaded || page.getBaseObject() != upstream.getBaseObject()) {\n              released += page.size();\n              freePage(page);\n            } else {\n              lastPage = page;\n            }\n          }\n          allocatedPages.clear();\n        }\n\n        // in-memory sorter will not be used after spilling\n        assert(inMemSorter != null);\n        released += inMemSorter.getMemoryUsage();\n        totalSortTimeNanos += inMemSorter.getSortTimeNanos();\n        inMemSorter.free();\n        inMemSorter = null;\n        totalSpillBytes += released;\n        return released;\n      }\n    }\n\n    @Override\n    public boolean hasNext() {\n      return numRecords > 0;\n    }\n\n    @Override\n    public void loadNext() throws IOException {\n      synchronized (this) {\n        loaded = true;\n        if (nextUpstream != null) {\n          // Just consumed the last record from in memory iterator\n          if (lastPage != null) {\n            freePage(lastPage);\n            lastPage = null;\n          }\n          upstream = nextUpstream;\n          nextUpstream = null;\n        }\n        numRecords--;\n        upstream.loadNext();\n      }\n    }\n\n    @Override\n    public Object getBaseObject() {\n      return upstream.getBaseObject();\n    }\n\n    @Override\n    public long getBaseOffset() {\n      return upstream.getBaseOffset();\n    }\n\n    @Override\n    public int getRecordLength() {\n      return upstream.getRecordLength();\n    }\n\n    @Override\n    public long getKeyPrefix() {\n      return upstream.getKeyPrefix();\n    }\n  }\n\n  /**\n   * Returns a iterator, which will return the rows in the order as inserted.\n   *\n   * It is the caller's responsibility to call `cleanupResources()`\n   * after consuming this iterator.\n   *\n   * TODO: support forced spilling\n   */\n  public UnsafeSorterIterator getIterator() throws IOException {\n    /**\n     * 如果spillWriters为空说明，直接读取内存中即可\n     */\n    if (spillWriters.isEmpty()) {\n      assert(inMemSorter != null);\n      return inMemSorter.getSortedIterator();\n    } else {\n      /**\n       * 否则将spillWriters对应的file中的数据，通过getReader对应UnsafeSorterSpillReader的\n       * 读取器反序列化到UnsafeSorterIterator中，然后到添加到queue队列中\n       * UnsafeSorterSpillReader也是UnsafeSorterIterator的子类\n       */\n      LinkedList<UnsafeSorterIterator> queue = new LinkedList<UnsafeSorterIterator>();\n      for (UnsafeSorterSpillWriter spillWriter : spillWriters) {\n        queue.add(spillWriter.getReader(serializerManager));\n      }\n      if (inMemSorter != null) {\n        queue.add(inMemSorter.getSortedIterator());\n      }\n      /**\n       * ChainedIterator是一个UnsafeSorterIterator的子类\n       * 实现将将多个UnsafeSorterIterator合成一个UnsafeSorterIterator\n       * 提供给应用使用\n       */\n      return new ChainedIterator(queue);\n    }\n  }\n\n  /**\n   * Chain multiple UnsafeSorterIterator together as single one.\n   */\n  static class ChainedIterator extends UnsafeSorterIterator {\n\n    private final Queue<UnsafeSorterIterator> iterators;\n    private UnsafeSorterIterator current;\n    private int numRecords;\n\n    ChainedIterator(Queue<UnsafeSorterIterator> iterators) {\n      assert iterators.size() > 0;\n      this.numRecords = 0;\n      for (UnsafeSorterIterator iter: iterators) {\n        this.numRecords += iter.getNumRecords();\n      }\n      this.iterators = iterators;\n      this.current = iterators.remove();\n    }\n\n    @Override\n    public int getNumRecords() {\n      return numRecords;\n    }\n\n    @Override\n    public boolean hasNext() {\n      while (!current.hasNext() && !iterators.isEmpty()) {\n        current = iterators.remove(); /**从队列中移除一个已经遍历完的UnsafeSorterIterator*/\n      }\n      return current.hasNext();\n    }\n\n    @Override\n    public void loadNext() throws IOException {\n      while (!current.hasNext() && !iterators.isEmpty()) {\n        current = iterators.remove(); /**从队列中移除一个已经遍历完的UnsafeSorterIterator*/\n      }\n      current.loadNext();\n    }\n\n    @Override\n    public Object getBaseObject() { return current.getBaseObject(); }\n\n    @Override\n    public long getBaseOffset() { return current.getBaseOffset(); }\n\n    @Override\n    public int getRecordLength() { return current.getRecordLength(); }\n\n    @Override\n    public long getKeyPrefix() { return current.getKeyPrefix(); }\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/UnsafeInMemorySorter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\n\n\n\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.array.LongArray;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.MemoryConsumer;\n\nimport javax.annotation.Nullable;\nimport java.util.Comparator;\n\n/**\n * Sorts records using an AlphaSort-style key-prefix sort. This sort stores pointers to records\n * alongside a user-defined prefix of the record's sorting key. When the underlying sort algorithm\n * compares records, it will first compare the stored key prefixes; if the prefixes are not equal,\n * then we do not need to traverse the record pointers to compare the actual records. Avoiding these\n * random memory accesses improves cache hit rates.\n */\npublic final class UnsafeInMemorySorter {\n\n  private static final class SortComparator implements Comparator<RecordPointerAndKeyPrefix> {\n\n    private final RecordComparator recordComparator;\n    private final PrefixComparator prefixComparator;\n    private final DataNodeMemoryManager memoryManager;\n\n    SortComparator(\n        RecordComparator recordComparator,\n        PrefixComparator prefixComparator,\n        DataNodeMemoryManager memoryManager) {\n      this.recordComparator = recordComparator;\n      this.prefixComparator = prefixComparator;\n      this.memoryManager = memoryManager;\n    }\n\n    @Override\n    public int compare(RecordPointerAndKeyPrefix r1, RecordPointerAndKeyPrefix r2) {\n\n      final int prefixComparisonResult = prefixComparator.compare(r1.keyPrefix, r2.keyPrefix);\n\n      if (prefixComparisonResult == 0) {\n        final Object baseObject1 = memoryManager.getPage(r1.recordPointer);\n        final long baseOffset1 = memoryManager.getOffsetInPage(r1.recordPointer) + 4; // skip length\n        final Object baseObject2 = memoryManager.getPage(r2.recordPointer);\n        final long baseOffset2 = memoryManager.getOffsetInPage(r2.recordPointer) + 4; // skip length\n        return recordComparator.compare(baseObject1, baseOffset1, baseObject2, baseOffset2);\n      } else {\n        return prefixComparisonResult;\n      }\n    }\n  }\n\n  private final MemoryConsumer consumer;\n  private final DataNodeMemoryManager memoryManager;\n  @Nullable\n  private final Sorter<RecordPointerAndKeyPrefix,LongArray> sorter;\n  @Nullable\n  private final Comparator<RecordPointerAndKeyPrefix> sortComparator;\n\n  /**\n   * If non-null, specifies the radix sort parameters and that radix sort will be used.\n   */\n  @Nullable\n  private final PrefixComparators.RadixSortSupport radixSortSupport;\n\n  /**\n   * Set to 2x for radix sort to reserve extra memory for sorting, otherwise 1x.\n   */\n  private final int memoryAllocationFactor;\n\n  /**\n   * Within this buffer, position {@code 2 * i} holds a pointer pointer to the record at\n   * index {@code i}, while position {@code 2 * i + 1} in the array holds an 8-byte key prefix.\n   */\n  private LongArray array;\n\n  /**\n   * The position in the sort buffer where new records can be inserted.\n   */\n  private int pos = 0;\n\n  private long initialSize;\n\n  private long totalSortTimeNanos = 0L;\n  private  boolean enableSort = true;\n\n  public UnsafeInMemorySorter(\n    final MemoryConsumer consumer,\n    final DataNodeMemoryManager memoryManager,\n    final RecordComparator recordComparator,\n    final PrefixComparator prefixComparator,\n    long initialSize,\n    boolean canUseRadixSort,boolean enableSort) {\n    this(consumer, memoryManager, recordComparator, prefixComparator,\n      consumer.allocateLongArray(initialSize * 2), canUseRadixSort,enableSort);\n  }\n\n  public UnsafeInMemorySorter(\n      final MemoryConsumer consumer,\n      final DataNodeMemoryManager memoryManager,\n      final RecordComparator recordComparator,\n      final PrefixComparator prefixComparator,\n      LongArray array,\n      boolean canUseRadixSort,\n      boolean enableSort) {\n\n    this.consumer = consumer;\n\n    this.memoryManager = memoryManager;\n\n    this.initialSize = array.size();\n\n    if (recordComparator != null) {\n      this.sorter = new Sorter<RecordPointerAndKeyPrefix,LongArray>(UnsafeSortDataFormat.INSTANCE);\n\n      this.sortComparator = new SortComparator(recordComparator, prefixComparator, memoryManager);\n\n      if (canUseRadixSort && prefixComparator instanceof PrefixComparators.RadixSortSupport) {\n        this.radixSortSupport = (PrefixComparators.RadixSortSupport)prefixComparator;\n      } else {\n        this.radixSortSupport = null;\n      }\n    } else {\n      this.sorter = null;\n      this.sortComparator = null;\n      this.radixSortSupport = null;\n    }\n    this.enableSort = enableSort;\n    this.memoryAllocationFactor = this.radixSortSupport != null ? 2 : 1;\n    this.array = array;\n  }\n\n  /**\n   * Free the memory used by pointer array.\n   */\n  public void free() {\n    if (consumer != null) {\n      consumer.freeLongArray(array);\n      array = null;\n    }\n  }\n\n  public void reset() {\n    if (consumer != null) {\n      consumer.freeLongArray(array);\n      this.array = consumer.allocateLongArray(initialSize);\n    }\n    pos = 0;\n  }\n\n  /**\n   * @return the number of records that have been inserted into this sorter.\n   */\n  public int numRecords() {\n    return pos / 2;\n  }\n\n  /**\n   * @return the total amount of time spent sorting data (in-memory only).\n   */\n  public long getSortTimeNanos() {\n    return totalSortTimeNanos;\n  }\n\n  public long getMemoryUsage() {\n    return array.size() * 8;\n  }\n\n  public boolean hasSpaceForAnotherRecord() {\n    return pos + 1 < (array.size() / memoryAllocationFactor);\n  }\n\n  public void expandPointerArray(LongArray newArray) {\n    if (newArray.size() < array.size()) {\n      throw new OutOfMemoryError(\"Not enough memory to grow pointer array\");\n    }\n    Platform.copyMemory(\n      array.getBaseObject(),\n      array.getBaseOffset(),\n      newArray.getBaseObject(),\n      newArray.getBaseOffset(),\n      array.size() * (8 / memoryAllocationFactor));\n    consumer.freeLongArray(array);\n    array = newArray;\n  }\n\n  /**\n   * Inserts a record to be sorted. Assumes that the record pointer points to a record length\n   * stored as a 4-byte integer, followed by the record's bytes.\n   *\n   * @param recordPointer pointer to a record in a data page, encoded by {@link DataNodeMemoryManager}.\n   * @param keyPrefix a user-defined key prefix\n   */\n  public void insertRecord(long recordPointer, long keyPrefix) {\n    if (!hasSpaceForAnotherRecord()) {\n      throw new IllegalStateException(\"There is no space for new record\");\n    }\n      /**\n       * 先插入recordPointer，然后插入keyPrefix值\n       * */\n    array.set(pos, recordPointer);\n    pos++;\n    array.set(pos, keyPrefix);\n    pos++;\n  }\n\n  public final class SortedIterator extends UnsafeSorterIterator implements Cloneable {\n\n    private final int numRecords;\n    private int position;\n    private int offset;\n    private Object baseObject;\n    private long baseOffset;\n    private long keyPrefix;\n    private int recordLength;\n\n    private SortedIterator(int numRecords, int offset) {\n      this.numRecords = numRecords;\n      this.position = 0;\n      this.offset = offset;\n    }\n\n    public SortedIterator clone() {\n      SortedIterator iter = new SortedIterator(numRecords, offset);\n      iter.position = position;\n      iter.baseObject = baseObject;\n      iter.baseOffset = baseOffset;\n      iter.keyPrefix = keyPrefix;\n      iter.recordLength = recordLength;\n      return iter;\n    }\n\n    @Override\n    public int getNumRecords() {\n      return numRecords;\n    }\n\n    @Override\n    public boolean hasNext() {\n      return position / 2 < numRecords;\n    }\n\n    /**\n     * 更新迭代器相关指针信息，和当前记录内容的大小\n     */\n    @Override\n    public void loadNext() {\n      // This pointer points to a 4-byte record length, followed by the record's bytes\n      final long recordPointer = array.get(offset + position);\n      baseObject = memoryManager.getPage(recordPointer);\n      baseOffset = memoryManager.getOffsetInPage(recordPointer) + 4;  // Skip over record length\n      recordLength = Platform.getInt(baseObject, baseOffset - 4);\n      keyPrefix = array.get(offset + position + 1);\n      position += 2;\n    }\n\n    @Override\n    public Object getBaseObject() { return baseObject; }\n\n    @Override\n    public long getBaseOffset() { return baseOffset; }\n\n    @Override\n    public int getRecordLength() { return recordLength; }\n\n    @Override\n    public long getKeyPrefix() { return keyPrefix; }\n  }\n\n  /**\n   * Return an iterator over record pointers in sorted order. For efficiency, all calls to\n   * {@code next()} will return the same mutable object.\n   */\n  public SortedIterator getSortedIterator() {\n    int offset = 0;\n    long start = System.nanoTime();\n    if (sorter != null && enableSort) {\n      if (this.radixSortSupport != null) {\n        // TODO(ekl) we should handle NULL values before radix sort for efficiency, since they\n        // force a full-width sort (and we cannot radix-sort nullable long fields at all).\n        offset = RadixSort.sortKeyPrefixArray(array, pos / 2, 0, 7, radixSortSupport.sortDescending(),radixSortSupport.sortSigned());\n      } else {\n        sorter.sort(array,0,pos / 2,sortComparator);\n      }\n    }\n    totalSortTimeNanos += System.nanoTime() - start;\n    return new SortedIterator(pos / 2, offset);\n  }\n\n  /**\n   * Return an iterator over record pointers int not sorted order. For efficiency, all calls to\n   * {@code next()} will return the same mutable object.\n   */\n  public SortedIterator getIterator() {\n    return new SortedIterator(pos / 2,0);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/UnsafeKVExternalSorter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\nimport com.google.common.annotations.VisibleForTesting;\nimport com.google.common.collect.Ordering;\nimport io.mycat.memory.unsafe.KVIterator;\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.map.BytesToBytesMap;\nimport io.mycat.memory.unsafe.memory.MemoryBlock;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.row.StructType;\nimport io.mycat.memory.unsafe.row.UnsafeRow;\nimport io.mycat.memory.unsafe.storage.DataNodeDiskManager;\nimport io.mycat.memory.unsafe.storage.SerializerManager;\n\n\nimport javax.annotation.Nullable;\nimport java.io.IOException;\n\n/**\n * A class for performing external sorting on key-value records. Both key and value are UnsafeRows.\n *\n * Note that this class allows optionally passing in a {@link io.mycat.memory.unsafe.map.BytesToBytesMap} directly in order\n * to perform in-place sorting of records in the map.\n */\n\npublic final class UnsafeKVExternalSorter {\n\n  private final StructType keySchema;\n  private final StructType valueSchema;\n  private final UnsafeExternalRowSorter.PrefixComputer prefixComputer;\n  private final UnsafeExternalSorter sorter;\n\n  public UnsafeKVExternalSorter(\n      StructType keySchema,\n      StructType valueSchema,\n      DataNodeDiskManager blockManager,\n      SerializerManager serializerManager,\n      long pageSizeBytes) throws IOException {\n    this(keySchema, valueSchema, blockManager, serializerManager, pageSizeBytes, null);\n  }\n\n  public UnsafeKVExternalSorter(\n      StructType keySchema,\n      StructType valueSchema,\n      DataNodeDiskManager blockManager,\n      SerializerManager serializerManager,\n      long pageSizeBytes,\n      @Nullable BytesToBytesMap map) throws IOException {\n\n    this.keySchema = keySchema;\n    this.valueSchema = valueSchema;\n\n\n    /**\n     * 排序的key，根据前缀排序规则，进行比较\n     *\n     */\n    prefixComputer = SortPrefixUtils.createPrefixGenerator(keySchema);\n    /**\n     * 排序的key，根据前缀排序规则，进行比较\n     *\n     */\n    PrefixComparator prefixComparator = SortPrefixUtils.getPrefixComparator(keySchema);\n\n      /**\n       *\n       */\n    BaseOrdering ordering = new BaseOrdering(){\n      @Override\n      public int compare(@Nullable UnsafeRow unsafeRow, @Nullable UnsafeRow t1) {\n        return 1;\n      }\n    };\n\n    KVComparator recordComparator = new KVComparator(ordering, keySchema.length());\n\n    DataNodeMemoryManager dataNodeMemoryManager = null;\n    if (map == null) {\n      sorter = UnsafeExternalSorter.create(\n              dataNodeMemoryManager,\n        blockManager,\n        serializerManager,\n        recordComparator,\n        prefixComparator,\n         4096,\n        pageSizeBytes,\n        keySchema.length() == 1 && SortPrefixUtils.canSortFullyWithPrefix(keySchema.apply(0)),true);\n    } else {\n      // During spilling, the array in map will not be used, so we can borrow that and use it\n      // as the underline array for in-memory sorter (it's always large enough).\n      // Since we will not grow the array, it's fine to pass `null` as consumer.\n\n      /**\n       * map.getArray()获取longArray指针数组\n       */\n      final UnsafeInMemorySorter inMemSorter = new UnsafeInMemorySorter(\n        null, dataNodeMemoryManager, recordComparator, prefixComparator, map.getArray(),\n        false /* TODO(ekl) we can only radix sort if the BytesToBytes load factor is <= 0.5 */,true);\n\n      // We cannot use the destructive iterator here because we are reusing the existing memory\n      // pages in BytesToBytesMap to hold records during sorting.\n      // The only new memory we are allocating is the pointer/prefix array.\n      BytesToBytesMap.MapIterator iter = map.iterator();\n      final int numKeyFields = keySchema.length();\n      UnsafeRow row = new UnsafeRow(numKeyFields);\n      while (iter.hasNext()) {\n        final BytesToBytesMap.Location loc = iter.next();\n        final Object baseObject = loc.getKeyBase();\n        final long baseOffset = loc.getKeyOffset();\n\n        // Get encoded memory address\n        // baseObject + baseOffset point to the beginning of the key data in the map, but that\n        // the KV-pair's length data is stored in the word immediately before that address\n        MemoryBlock page = loc.getMemoryPage();\n        long address = dataNodeMemoryManager.encodePageNumberAndOffset(page, baseOffset - 8);\n\n        // Compute prefix\n        row.pointTo(baseObject, baseOffset, loc.getKeyLength());\n        final long prefix = prefixComputer.computePrefix(row);\n\n        inMemSorter.insertRecord(address, prefix);\n      }\n\n      sorter = UnsafeExternalSorter.createWithExistingInMemorySorter(\n              dataNodeMemoryManager,\n        blockManager,\n        serializerManager,\n        new KVComparator(ordering, keySchema.length()),\n        prefixComparator,\n        4096,\n        pageSizeBytes,\n        inMemSorter,true);\n\n      // reset the map, so we can re-use it to insert new records. the inMemSorter will not used\n      // anymore, so the underline array could be used by map again.\n      map.reset();\n    }\n  }\n\n  /**\n   * Inserts a key-value record into the sorter. If the sorter no longer has enough memory to hold\n   * the record, the sorter sorts the existing records in-memory, writes them out as partially\n   * sorted runs, and then reallocates memory to hold the new record.\n   */\n  public void insertKV(UnsafeRow key, UnsafeRow value) throws IOException {\n    final long prefix = prefixComputer.computePrefix(key);\n    sorter.insertKVRecord(\n      key.getBaseObject(), key.getBaseOffset(), key.getSizeInBytes(),\n      value.getBaseObject(), value.getBaseOffset(), value.getSizeInBytes(), prefix);\n  }\n\n  /**\n   * Merges another UnsafeKVExternalSorter into `this`, the other one will be emptied.\n   *\n   * @throws IOException\n   */\n  public void merge(UnsafeKVExternalSorter other) throws IOException {\n    sorter.merge(other.sorter);\n  }\n\n  /**\n   * Returns a sorted iterator. It is the caller's responsibility to call `cleanupResources()`\n   * after consuming this iterator.\n   */\n  public KVSorterIterator sortedIterator() throws IOException {\n    try {\n      final UnsafeSorterIterator underlying = sorter.getSortedIterator();\n      if (!underlying.hasNext()) {\n        // Since we won't ever call next() on an empty iterator, we need to clean up resources\n        // here in order to prevent memory leaks.\n        cleanupResources();\n      }\n      return new KVSorterIterator(underlying);\n    } catch (IOException e) {\n      cleanupResources();\n      throw e;\n    }\n  }\n\n  /**\n   * Return the total number of bytes that has been spilled into disk so far.\n   */\n  public long getSpillSize() {\n    return sorter.getSpillSize();\n  }\n\n  /**\n   * Return the peak memory used so far, in bytes.\n   */\n  public long getPeakMemoryUsedBytes() {\n    return sorter.getPeakMemoryUsedBytes();\n  }\n\n  /**\n   * Marks the current page as no-more-space-available, and as a result, either allocate a\n   * new page or spill when we see the next record.\n   */\n  @VisibleForTesting\n  void closeCurrentPage() {\n    sorter.closeCurrentPage();\n  }\n\n  /**\n   * Frees this sorter's in-memory data structures and cleans up its spill files.\n   */\n  public void cleanupResources() {\n    sorter.cleanupResources();\n  }\n\n  private static final class KVComparator extends RecordComparator {\n    private final BaseOrdering ordering;\n    private final UnsafeRow row1;\n    private final UnsafeRow row2;\n    private final int numKeyFields;\n\n    KVComparator(BaseOrdering ordering, int numKeyFields) {\n      this.numKeyFields = numKeyFields;\n      this.row1 = new UnsafeRow(numKeyFields);\n      this.row2 = new UnsafeRow(numKeyFields);\n      this.ordering = ordering;\n    }\n\n    @Override\n    public int compare(Object baseObj1, long baseOff1, Object baseObj2, long baseOff2) {\n      // Note that since ordering doesn't need the total length of the record, we just pass -1\n      // into the row.\n      row1.pointTo(baseObj1, baseOff1 + 4, -1);\n      row2.pointTo(baseObj2, baseOff2 + 4, -1);\n      return ordering.compare(row1, row2);\n    }\n  }\n\n  public class KVSorterIterator extends KVIterator<UnsafeRow, UnsafeRow> {\n    private UnsafeRow key = new UnsafeRow(keySchema.length());\n    private UnsafeRow value = new UnsafeRow(valueSchema.length());\n    private final UnsafeSorterIterator underlying;\n\n    private KVSorterIterator(UnsafeSorterIterator underlying) {\n      this.underlying = underlying;\n    }\n\n    @Override\n    public boolean next() throws IOException {\n      try {\n        if (underlying.hasNext()) {\n          underlying.loadNext();\n\n          Object baseObj = underlying.getBaseObject();\n          long recordOffset = underlying.getBaseOffset();\n          int recordLen = underlying.getRecordLength();\n\n          // Note that recordLen = keyLen + valueLen + 4 bytes (for the keyLen itself)\n          int keyLen = Platform.getInt(baseObj, recordOffset);\n          int valueLen = recordLen - keyLen - 4;\n          key.pointTo(baseObj, recordOffset + 4, keyLen);\n          value.pointTo(baseObj, recordOffset + 4 + keyLen, valueLen);\n\n          return true;\n        } else {\n          key = null;\n          value = null;\n          cleanupResources();\n          return false;\n        }\n      } catch (IOException e) {\n        cleanupResources();\n        throw e;\n      }\n    }\n\n    @Override\n    public UnsafeRow getKey() {\n      return key;\n    }\n\n    @Override\n    public UnsafeRow getValue() {\n      return value;\n    }\n\n    @Override\n    public void close() {\n      cleanupResources();\n    }\n  }\n\n  class BaseOrdering extends Ordering<UnsafeRow> {\n    @Override\n    public int compare(@Nullable UnsafeRow unsafeRow, @Nullable UnsafeRow t1) {\n       throw new UnsupportedOperationException();\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/UnsafeKeyValueSorter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\n\n\nimport io.mycat.memory.unsafe.KVIterator;\nimport io.mycat.memory.unsafe.row.UnsafeRow;\n\nimport java.io.IOException;\n\npublic abstract class UnsafeKeyValueSorter {\n\n  public abstract void insert(UnsafeRow key, UnsafeRow value);\n\n  public abstract KVIterator<UnsafeRow, UnsafeRow> sort() throws IOException;\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/UnsafeRowsMerger.java",
    "content": "package io.mycat.memory.unsafe.utils.sort;\n\nimport java.io.IOException;\nimport java.util.Comparator;\nimport java.util.PriorityQueue;\n\n/**\n * Created by zagnix on 2016/6/25.\n */\npublic final class UnsafeRowsMerger {\n    private int numRecords = 0;\n    private final PriorityQueue<UnsafeSorterIterator> priorityQueue;\n\n    UnsafeRowsMerger(\n            final RecordComparator recordComparator,\n            final PrefixComparator prefixComparator,\n            final int numSpills) {\n\n        final Comparator<UnsafeSorterIterator> comparator = new Comparator<UnsafeSorterIterator>() {\n            @Override\n            public int compare(UnsafeSorterIterator left, UnsafeSorterIterator right) {\n                final int prefixComparisonResult = prefixComparator.compare(left.getKeyPrefix(), right.getKeyPrefix());\n                if (prefixComparisonResult == 0) {\n                    return recordComparator.compare(\n                            left.getBaseObject(), left.getBaseOffset(),\n                            right.getBaseObject(), right.getBaseOffset());\n                } else {\n                    return prefixComparisonResult;\n                }\n            }\n        };\n\n        /**\n         * 使用优先级队列实现多个Spill File 合并排序,并且支持已经排序内存记录\n         * 重新写入一个排序文件中。\n         */\n        priorityQueue = new PriorityQueue<UnsafeSorterIterator>(numSpills,comparator);\n    }\n\n    /**\n     * Add an UnsafeSorterIterator to this merger\n     *\n     */\n    public void addSpillIfNotEmpty(UnsafeSorterIterator iterator) throws IOException {\n        /**\n         * 添加迭代器到priorityQueue中\n         */\n        if (iterator.hasNext()) {\n            iterator.loadNext();\n            priorityQueue.add(iterator);\n            numRecords += iterator.getNumRecords();\n        }\n    }\n\n    public int getNumRecords() {\n        return numRecords;\n    }\n\n    public UnsafeSorterIterator getSortedIterator() throws IOException {\n        return new UnsafeSorterIterator() {\n            private UnsafeSorterIterator spillReader;\n\n            @Override\n            public int getNumRecords() {\n                return numRecords;\n            }\n\n            @Override\n            public boolean hasNext() {\n                return !priorityQueue.isEmpty() || (spillReader != null && spillReader.hasNext());\n            }\n\n            @Override\n            public void loadNext() throws IOException {\n                if (spillReader != null) {\n                    if (spillReader.hasNext()) {\n                        spillReader.loadNext();\n                        priorityQueue.add(spillReader);\n                    }\n                }\n                spillReader = priorityQueue.remove();\n            }\n\n            @Override\n            public Object getBaseObject() { return spillReader.getBaseObject(); }\n\n            @Override\n            public long getBaseOffset() { return spillReader.getBaseOffset(); }\n\n            @Override\n            public int getRecordLength() { return spillReader.getRecordLength(); }\n\n            @Override\n            public long getKeyPrefix() { return spillReader.getKeyPrefix(); }\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/UnsafeSortDataFormat.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.array.LongArray;\nimport io.mycat.memory.unsafe.memory.MemoryBlock;\n\n/**\n * Supports sorting an array of (record pointer, key prefix) pairs.\n * Used in {@link UnsafeInMemorySorter}.\n * <p>\n * Within each long[] buffer, position {@code 2 * i} holds a pointer pointer to the record at\n * index {@code i}, while position {@code 2 * i + 1} in the array holds an 8-byte key prefix.\n */\npublic final class UnsafeSortDataFormat\n  extends SortDataFormat<RecordPointerAndKeyPrefix, LongArray> {\n\n  public static final UnsafeSortDataFormat INSTANCE = new UnsafeSortDataFormat();\n\n  private UnsafeSortDataFormat() { }\n\n  @Override\n  public RecordPointerAndKeyPrefix getKey(LongArray data, int pos) {\n    // Since we re-use keys, this method shouldn't be called.\n    throw new UnsupportedOperationException();\n  }\n\n  @Override\n  public RecordPointerAndKeyPrefix newKey() {\n    return new RecordPointerAndKeyPrefix();\n  }\n\n  @Override\n  public RecordPointerAndKeyPrefix getKey(LongArray data, int pos,\n                                          RecordPointerAndKeyPrefix reuse) {\n    reuse.recordPointer = data.get(pos * 2);\n    reuse.keyPrefix = data.get(pos * 2 + 1);\n    return reuse;\n  }\n\n  @Override\n  public void swap(LongArray data, int pos0, int pos1) {\n    long tempPointer = data.get(pos0 * 2);\n    long tempKeyPrefix = data.get(pos0 * 2 + 1);\n    data.set(pos0 * 2, data.get(pos1 * 2));\n    data.set(pos0 * 2 + 1, data.get(pos1 * 2 + 1));\n    data.set(pos1 * 2, tempPointer);\n    data.set(pos1 * 2 + 1, tempKeyPrefix);\n  }\n\n  @Override\n  public void copyElement(LongArray src, int srcPos, LongArray dst, int dstPos) {\n    dst.set(dstPos * 2, src.get(srcPos * 2));\n    dst.set(dstPos * 2 + 1, src.get(srcPos * 2 + 1));\n  }\n\n  @Override\n  public void copyRange(LongArray src, int srcPos, LongArray dst, int dstPos, int length) {\n    Platform.copyMemory(\n      src.getBaseObject(),\n      src.getBaseOffset() + srcPos * 16,\n      dst.getBaseObject(),\n      dst.getBaseOffset() + dstPos * 16,\n      length * 16);\n  }\n\n  @Override\n  public LongArray allocate(int length) {\n    assert (length < Integer.MAX_VALUE / 2) : \"Length \" + length + \" is too large\";\n    return new LongArray(MemoryBlock.fromLongArray(new long[length * 2]));\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/UnsafeSorterIterator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\nimport java.io.IOException;\n\npublic abstract class UnsafeSorterIterator {\n\n  public abstract boolean hasNext();\n\n  public abstract void loadNext() throws IOException;\n\n  public abstract Object getBaseObject();\n\n  public abstract long getBaseOffset();\n\n  public abstract int getRecordLength();\n\n  public abstract long getKeyPrefix();\n\n  public abstract int getNumRecords();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/UnsafeSorterSpillMerger.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\nimport java.io.IOException;\nimport java.util.Comparator;\nimport java.util.PriorityQueue;\n\nfinal class UnsafeSorterSpillMerger {\n\n  private int numRecords = 0;\n  private final PriorityQueue<UnsafeSorterIterator> priorityQueue;\n\n  UnsafeSorterSpillMerger(\n      final RecordComparator recordComparator,\n      final PrefixComparator prefixComparator,\n      final int numSpills) {\n\n    final Comparator<UnsafeSorterIterator> comparator = new Comparator<UnsafeSorterIterator>() {\n      @Override\n      public int compare(UnsafeSorterIterator left, UnsafeSorterIterator right) {\n        final int prefixComparisonResult = prefixComparator.compare(left.getKeyPrefix(), right.getKeyPrefix());\n        if (prefixComparisonResult == 0) {\n          return recordComparator.compare(\n            left.getBaseObject(), left.getBaseOffset(),\n            right.getBaseObject(), right.getBaseOffset());\n        } else {\n          return prefixComparisonResult;\n        }\n      }\n    };\n\n      /**\n       * 使用优先级队列实现多个Spill File 合并排序,并且支持已经排序内存记录\n       * 重新写入一个排序文件中。\n       */\n    priorityQueue = new PriorityQueue<UnsafeSorterIterator>(numSpills,comparator);\n  }\n\n  /**\n   * Add an UnsafeSorterIterator to this merger\n   *\n   */\n  public void addSpillIfNotEmpty(UnsafeSorterIterator spillReader) throws IOException {\n    /**\n     * 添加迭代器到priorityQueue中\n     */\n    if (spillReader.hasNext()) {\n      // We only add the spillReader to the priorityQueue if it is not empty. We do this to\n      // make sure the hasNext method of UnsafeSorterIterator returned by getSortedIterator\n      // does not return wrong result because hasNext will returns true\n      // at least priorityQueue.size() times. If we allow n spillReaders in the\n      // priorityQueue, we will have n extra empty records in the result of UnsafeSorterIterator.\n\n      spillReader.loadNext();\n      priorityQueue.add(spillReader);\n      numRecords += spillReader.getNumRecords();\n    }\n  }\n\n  /**\n   * 非常重要的一个排序迭代器\n   * @return\n   * @throws IOException\n     */\n  public UnsafeSorterIterator getSortedIterator() throws IOException {\n    return new UnsafeSorterIterator() {\n      /**\n       * 当前迭代器\n       */\n      private UnsafeSorterIterator spillReader;\n\n      @Override\n      public int getNumRecords() {\n        return numRecords;\n      }\n\n      @Override\n      public boolean hasNext() {\n        return !priorityQueue.isEmpty() || (spillReader != null && spillReader.hasNext());\n      }\n\n      @Override\n      public void loadNext() throws IOException {\n        if (spillReader != null) {\n          if (spillReader.hasNext()) {\n             spillReader.loadNext();\n             /**\n             *添加一个完整迭代器集合给优先级队列，\n             *优先级队列为根据比较器自动调整想要的数据大小\n             * 每次都将spillReader添加到队列中进行新的调整\n             * 最后得到最小的元素，为出优先级队列做准备\n             */\n            priorityQueue.add(spillReader);\n          }\n        }\n\n        /**\n         * 出队列，当前spillreader最小的元素出优先级队列\n         */\n        spillReader = priorityQueue.remove();\n      }\n\n      @Override\n      public Object getBaseObject() { return spillReader.getBaseObject(); }\n\n      @Override\n      public long getBaseOffset() { return spillReader.getBaseOffset(); }\n\n      @Override\n      public int getRecordLength() { return spillReader.getRecordLength(); }\n\n      @Override\n      public long getKeyPrefix() { return spillReader.getKeyPrefix(); }\n    };\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/UnsafeSorterSpillReader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\nimport com.google.common.io.ByteStreams;\nimport com.google.common.io.Closeables;\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.storage.ConnectionId;\nimport io.mycat.memory.unsafe.storage.SerializerManager;\n\n\nimport java.io.*;\n\n/**\n * Reads spill files written by {@link UnsafeSorterSpillWriter} (see that class for a description\n * of the file format).\n */\npublic final class UnsafeSorterSpillReader extends UnsafeSorterIterator implements Closeable {\n\n  private InputStream in;\n  private DataInputStream din;\n\n  // Variables that change with every record read:\n  private int recordLength;\n  private long keyPrefix;\n  private int numRecords;\n  private int numRecordsRemaining;\n\n  private byte[] arr = new byte[1024 * 1024];\n  private Object baseObject = arr;\n  private final long baseOffset = Platform.BYTE_ARRAY_OFFSET;\n\n  public UnsafeSorterSpillReader(\n      SerializerManager serializerManager,\n      File file,\n      ConnectionId blockId) throws IOException {\n    assert (file.length() > 0);\n    final BufferedInputStream bs = new BufferedInputStream(new FileInputStream(file));\n    try {\n      this.in = serializerManager.wrapForCompression(blockId,bs);\n      this.din = new DataInputStream(this.in);\n      numRecords = numRecordsRemaining = din.readInt();\n    } catch (IOException e) {\n      Closeables.close(bs, /* swallowIOException = */ true);\n      throw e;\n    }\n  }\n\n  @Override\n  public int getNumRecords() {\n    return numRecords;\n  }\n\n  @Override\n  public boolean hasNext() {\n    return (numRecordsRemaining > 0);\n  }\n\n  @Override\n  public void loadNext() throws IOException {\n    recordLength = din.readInt();\n    keyPrefix = din.readLong();\n    if (recordLength > arr.length) {\n      arr = new byte[recordLength];\n      baseObject = arr;\n    }\n    ByteStreams.readFully(in, arr, 0, recordLength);\n    numRecordsRemaining--;\n    if (numRecordsRemaining == 0) {\n      close();\n    }\n  }\n\n  @Override\n  public Object getBaseObject() {\n    return baseObject;\n  }\n\n  @Override\n  public long getBaseOffset() {\n    return baseOffset;\n  }\n\n  @Override\n  public int getRecordLength() {\n    return recordLength;\n  }\n\n  @Override\n  public long getKeyPrefix() {\n    return keyPrefix;\n  }\n\n  @Override\n  public void close() throws IOException {\n   if (in != null) {\n     try {\n       in.close();\n     } finally {\n       in = null;\n       din = null;\n     }\n   }\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/memory/unsafe/utils/sort/UnsafeSorterSpillWriter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.utils.sort;\n\n\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.storage.*;\n\nimport java.io.File;\nimport java.io.IOException;\n\n/**\n * Spills a list of sorted records to disk. Spill files have the following format:\n *\n *   [# of records (int)] [[len (int)][prefix (long)][data (bytes)]...]\n */\npublic final class UnsafeSorterSpillWriter {\n\n  static final int DISK_WRITE_BUFFER_SIZE = 1024 * 1024;\n\n  // Small writes to DiskRowWriter will be fairly inefficient. Since there doesn't seem to\n  // be an API to directly transfer bytes from managed memory to the disk writer, we buffer\n  // data through a byte array.\n  private byte[] writeBuffer = new byte[DISK_WRITE_BUFFER_SIZE];\n\n  private final File file;\n  private final ConnectionId conId;\n  private final int numRecordsToWrite;\n  private DiskRowWriter writer;\n  private DataNodeFileManager diskBlockManager;\n  private int numRecordsSpilled = 0;\n\n  public UnsafeSorterSpillWriter(\n      DataNodeDiskManager blockManager,\n      int fileBufferSize,\n      int numRecordsToWrite) throws IOException {\n\n    this.diskBlockManager =  blockManager.diskBlockManager();\n    this.conId =  diskBlockManager.createTempLocalBlock();\n    this.file = diskBlockManager.getFile(this.conId);\n\n    this.numRecordsToWrite = numRecordsToWrite;\n    // Unfortunately, we need a serializer instance in order to construct a DiskRowWriter.\n    // Our write path doesn't actually use this serializer (since we end up calling the `write()`\n    // OutputStream methods), but DiskRowWriter still calls some methods on it. To work\n    // around this, we pass a dummy no-op serializer.\n    writer = blockManager.getDiskWriter(conId, file, DummySerializerInstance.INSTANCE, fileBufferSize/**,writeMetrics*/);\n    // Write the number of records\n    writeIntToBuffer(numRecordsToWrite, 0);\n    writer.write(writeBuffer, 0, 4);\n  }\n\n  // Based on DataOutputStream.writeLong.\n  private void writeLongToBuffer(long v, int offset) throws IOException {\n    writeBuffer[offset + 0] = (byte)(v >>> 56);\n    writeBuffer[offset + 1] = (byte)(v >>> 48);\n    writeBuffer[offset + 2] = (byte)(v >>> 40);\n    writeBuffer[offset + 3] = (byte)(v >>> 32);\n    writeBuffer[offset + 4] = (byte)(v >>> 24);\n    writeBuffer[offset + 5] = (byte)(v >>> 16);\n    writeBuffer[offset + 6] = (byte)(v >>>  8);\n    writeBuffer[offset + 7] = (byte)(v >>>  0);\n  }\n\n  // Based on DataOutputStream.writeInt.\n  private void writeIntToBuffer(int v, int offset) throws IOException {\n    writeBuffer[offset + 0] = (byte)(v >>> 24);\n    writeBuffer[offset + 1] = (byte)(v >>> 16);\n    writeBuffer[offset + 2] = (byte)(v >>>  8);\n    writeBuffer[offset + 3] = (byte)(v >>>  0);\n  }\n\n  /**\n   * Write a record to a spill file.\n   *\n   * @param baseObject the base object / memory page containing the record\n   * @param baseOffset the base offset which points directly to the record data.\n   * @param recordLength the length of the record.\n   * @param keyPrefix a sort key prefix\n   */\n  public void write(\n      Object baseObject,\n      long baseOffset,\n      int recordLength,\n      long keyPrefix) throws IOException {\n    if (numRecordsSpilled == numRecordsToWrite) {\n      throw new IllegalStateException(\n        \"Number of records written exceeded numRecordsToWrite = \" + numRecordsToWrite);\n    } else {\n      numRecordsSpilled++;\n    }\n\n    /**\n     * [# of records (int)] [[len (int)][prefix (long)][data (bytes)]...]\n     * 一条记录在文件中格式\n     * */\n\n      /**\n       * recordLength记录长度 4个bytes\n       */\n    writeIntToBuffer(recordLength, 0);\n    /**\n     * 排序key，8个bytes\n     */\n    writeLongToBuffer(keyPrefix, 4);\n    /**\n     * dataRemaining要写的真实数据长度bytes\n     */\n    int dataRemaining = recordLength;\n    /**\n     * 写buffer剩余的空间\n     */\n    int freeSpaceInWriteBuffer = DISK_WRITE_BUFFER_SIZE - 4 - 8; // space used by prefix + len\n\n    /**\n     *记录在内存中的地址偏移量\n     */\n    long recordReadPosition = baseOffset;\n\n    while (dataRemaining > 0) {\n      /**\n       * 计算本次需要从内存中读取的实际数据，取freeSpaceInWriteBuffer和dataRemaining\n       * 中的最小值\n       */\n      final int toTransfer = Math.min(freeSpaceInWriteBuffer, dataRemaining);\n\n        /**\n         * 执行数据拷贝动作，将baseObject的数据拷贝到writeBuffer中\n         */\n      Platform.copyMemory(\n        baseObject,/**srd*/\n        recordReadPosition,/**offset*/\n        writeBuffer, /**write dst*/\n        Platform.BYTE_ARRAY_OFFSET + (DISK_WRITE_BUFFER_SIZE - freeSpaceInWriteBuffer),/**write offset*/\n        toTransfer);\n\n      /**\n       * 将writeBuffer中数据写到磁盘中\n       */\n      writer.write(writeBuffer, 0, (DISK_WRITE_BUFFER_SIZE - freeSpaceInWriteBuffer) + toTransfer);\n      /**\n       * 读指针移动toTransfer实际写的数据大小\n       */\n      recordReadPosition += toTransfer;\n      /**\n       * record还剩下多少数据要写入磁盘中\n       */\n      dataRemaining -= toTransfer;\n      /**\n       * 本次WriteBuffer初始化大小初始化为DISK_WRITE_BUFFER_SIZE\n       */\n      freeSpaceInWriteBuffer = DISK_WRITE_BUFFER_SIZE;\n    }\n\n      /**\n       * 写剩余数据到磁盘中\n       */\n    if (freeSpaceInWriteBuffer < DISK_WRITE_BUFFER_SIZE) {\n\n      writer.write(writeBuffer, 0, (DISK_WRITE_BUFFER_SIZE - freeSpaceInWriteBuffer));\n\n    }\n\n    /**\n     * writer类中数据统计\n     */\n    writer.recordWritten();\n  }\n\n  public void close() throws IOException {\n    writer.commitAndClose();\n    writer = null;\n    writeBuffer = null;\n  }\n\n  public File getFile() {\n    return file;\n  }\n\n  public UnsafeSorterSpillReader getReader(SerializerManager serializerManager) throws IOException {\n    return new UnsafeSorterSpillReader(serializerManager, file, conId);\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/BinlogIdleCheck.java",
    "content": "package io.mycat.migrate;\n\nimport com.alibaba.fastjson.JSON;\nimport io.mycat.util.ZKUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOException;\nimport java.sql.SQLException;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by magicdoom on 2016/12/14.\n */\npublic class BinlogIdleCheck implements Runnable {\n    private BinlogStream binlogStream;\n    private static final Logger LOGGER = LoggerFactory.getLogger(BinlogIdleCheck.class);\n    public BinlogIdleCheck(BinlogStream binlogStream) {\n        this.binlogStream = binlogStream;\n    }\n\n    @Override public void run() {\n        List<MigrateTask>migrateTaskList= binlogStream.getMigrateTaskList();\n        int sucessSwitchTask=0;\n        int fullSucessSwitchTask=0;\n        String taskPath=null;\n        String dataHost=null;\n        for (MigrateTask migrateTask : migrateTaskList) {\n            String zkPath=migrateTask.getZkpath();\n            if(taskPath==null){\n                taskPath=zkPath.substring(0,zkPath.lastIndexOf(\"/\")) ;\n                dataHost=zkPath.substring(zkPath.lastIndexOf(\"/\")+1);\n            }\n            if(migrateTask.isHaserror()||migrateTask.isHasExecute())\n            {\n                continue;\n            }\n                Date lastDate=       migrateTask.getLastBinlogDate();\n                long diff = (new Date().getTime() - lastDate.getTime())/1000;\n                if((!migrateTask.isHaserror())&&diff>30){\n                    //暂定30秒空闲 则代表增量任务结束，开始切换\n                   sucessSwitchTask=sucessSwitchTask+1;\n                    fullSucessSwitchTask=fullSucessSwitchTask+1;\n\n                }else if(!migrateTask.isHaserror()){\n                    String sql=MigrateUtils.makeCountSql(migrateTask);\n                    try {\n                        long oldCount=MigrateUtils.execulteCount(sql,migrateTask.getFrom());\n                        long newCount=MigrateUtils.execulteCount(sql,migrateTask.getTo());\n                        if(oldCount!=0) {\n                            double percent = newCount / oldCount;\n                            if(percent>=0.9) {\n                                sucessSwitchTask=sucessSwitchTask+1;\n                            }\n                        }\n                    } catch (SQLException e) {\n                        LOGGER.error(\"error:\",e);\n                    } catch (IOException e) {\n                        LOGGER.error(\"error:\",e);\n                    }\n\n                }\n\n        }\n\n\n        try {\n        \tbyte[] taskNodeStr = ZKUtils.getConnection().getData().forPath(taskPath);\n        \tSystem.out.println(new String(taskNodeStr));\n            TaskNode taskNode = JSON.parseObject(taskNodeStr,TaskNode.class);\n            if(taskNode.getStatus()>=3){\n                    binlogStream.disconnect();\n            }\n        } catch (Exception e) {\n            LOGGER.error(\"error:\",e);\n        }\n\n\n        if(sucessSwitchTask==migrateTaskList.size()){\n           String  childTaskPath=taskPath+\"/_prepare/\"+dataHost;\n            try {\n                if( ZKUtils.getConnection().checkExists().forPath(childTaskPath)==null) {\n                   ZKUtils.getConnection().create().creatingParentsIfNeeded().forPath(childTaskPath);\n                }\n\n            } catch (Exception e) {\n                LOGGER.error(\"error:\",e);\n            }\n\n        }\n\n\n          //全部空闲后，如果已经开始切换了，则修改每个子任务状态\n        if(fullSucessSwitchTask==migrateTaskList.size()){\n            try {\n                TaskNode taskNode=JSON.parseObject(new String( ZKUtils.getConnection().getData().forPath(taskPath),\"UTF-8\"),TaskNode.class);\n                 if(taskNode.getStatus()==2) {\n\n                     for (MigrateTask migrateTask : migrateTaskList) {\n                         String zkPath = migrateTask.getZkpath() + \"/\" + migrateTask.getFrom() + \"-\" + migrateTask.getTo();\n                         if (ZKUtils.getConnection().checkExists().forPath(zkPath) != null) {\n                             TaskStatus taskStatus = JSON.parseObject(\n                                     new String(ZKUtils.getConnection().getData().forPath(zkPath), \"UTF-8\"), TaskStatus.class);\n                             if (taskStatus.getStatus() == 1) {\n                                 taskStatus.setStatus(3);\n                                 ZKUtils.getConnection().setData().forPath(zkPath, JSON.toJSONBytes(taskStatus));\n                             }\n                         }\n                     }\n                 }\n            } catch (Exception e) {\n                LOGGER.error(\"error:\",e);\n            }\n\n        }\n    }\n\n\n\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/BinlogStream.java",
    "content": "package io.mycat.migrate;\n\nimport com.alibaba.druid.util.JdbcUtils;\nimport com.github.shyiko.mysql.binlog.BinaryLogClient;\nimport com.github.shyiko.mysql.binlog.event.*;\nimport com.google.common.base.Strings;\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.route.function.PartitionByCRC32PreSlot;\nimport io.mycat.server.util.SchemaUtil;\nimport io.mycat.sqlengine.OneRawSQLQueryResultHandler;\nimport io.mycat.sqlengine.SQLJob;\nimport io.mycat.util.DateUtil;\nimport org.joda.time.DateTime;\nimport org.joda.time.DateTimeZone;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.math.BigInteger;\nimport java.nio.charset.Charset;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.concurrent.*;\n\nimport static io.mycat.util.dataMigrator.DataMigratorUtil.executeQuery;\n\npublic class BinlogStream {\n    private static Logger logger = LoggerFactory.getLogger(BinlogStream.class);\n    private final String hostname;\n    private final int port;\n    private final String username;\n    private final String password;\n    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();\n    private BinaryLogClient binaryLogClient;\n    private Charset charset;\n\n    private long slaveID;\n    private String binglogFile;\n    private long binlogPos;\n\n    private Set<String> databaseSet = new HashSet<>();\n    private Map<String, Semaphore> semaphoreMap = new ConcurrentHashMap<>();\n\n\n    private List<MigrateTask> migrateTaskList;\n\n    public List<MigrateTask> getMigrateTaskList() {\n        return migrateTaskList;\n    }\n\n    public void setMigrateTaskList(List<MigrateTask> migrateTaskList) {\n        this.migrateTaskList = migrateTaskList;\n        for (MigrateTask migrateTask : migrateTaskList) {\n            databaseSet.add(MigrateUtils.getDatabaseFromDataNode(migrateTask.getFrom()));\n            String dataHostTo = MigrateUtils.getDataHostFromDataNode(migrateTask.getTo());\n            if (!semaphoreMap.containsKey(dataHostTo)) {\n                int count = Double.valueOf(MycatServer.getInstance().getConfig().getDataHosts().get(dataHostTo).getSource().getSize() * 0.8).intValue();\n                semaphoreMap.put(dataHostTo, new Semaphore(1));\n            }\n        }\n    }\n\n    public long getSlaveID() {\n        return slaveID;\n    }\n\n    public void setSlaveID(long slaveID) {\n        this.slaveID = slaveID;\n    }\n\n    public String getBinglogFile() {\n        return binglogFile;\n    }\n\n    public void setBinglogFile(String binglogFile) {\n        this.binglogFile = binglogFile;\n    }\n\n    public long getBinlogPos() {\n        return binlogPos;\n    }\n\n    public void setBinlogPos(long binlogPos) {\n        this.binlogPos = binlogPos;\n    }\n\n    private volatile boolean groupEventsByTX = true;\n\n\n    public BinlogStream(String hostname, int port, String username, String password, Charset charset) {\n        this.hostname = hostname;\n        this.port = port;\n        this.username = username;\n        this.password = password;\n        this.charset = charset;\n    }\n\n    public void setGroupEventsByTX(boolean groupEventsByTX) {\n        this.groupEventsByTX = groupEventsByTX;\n    }\n\n\n    public void connect() throws IOException {\n        initTaskDate();\n        scheduler.scheduleAtFixedRate(new BinlogIdleCheck(this), 5, 15, TimeUnit.SECONDS);\n        allocateBinaryLogClient().connect();\n\n    }\n\n    private void initTaskDate() {\n        Date curDate = new Date();\n        for (MigrateTask migrateTask : migrateTaskList) {\n            migrateTask.setLastBinlogDate(curDate);\n        }\n    }\n\n    public void connect(long timeoutInMilliseconds) throws IOException, TimeoutException {\n        initTaskDate();\n        scheduler.scheduleAtFixedRate(new BinlogIdleCheck(this), 5, 15, TimeUnit.SECONDS);\n        allocateBinaryLogClient().connect(timeoutInMilliseconds);\n\n    }\n\n    private synchronized BinaryLogClient allocateBinaryLogClient() {\n        if (isConnected()) {\n            throw new IllegalStateException(\"MySQL replication stream is already open\");\n        }\n        binaryLogClient = new BinaryLogClient(hostname, port, username, password);\n        binaryLogClient.setBinlogFilename(getBinglogFile());\n        binaryLogClient.setBinlogPosition(getBinlogPos());\n        binaryLogClient.setServerId(getSlaveID());\n        binaryLogClient.registerEventListener(new DelegatingEventListener());\n        return binaryLogClient;\n    }\n\n\n    public synchronized boolean isConnected() {\n        return binaryLogClient != null && binaryLogClient.isConnected();\n    }\n\n\n    public synchronized void disconnect() throws IOException {\n        if (binaryLogClient != null) {\n            binaryLogClient.disconnect();\n            binaryLogClient = null;\n        }\n        shutdownAndAwaitTermination(scheduler);\n    }\n\n\n    void shutdownAndAwaitTermination(ExecutorService pool) {\n        pool.shutdown(); // Disable new tasks from being submitted\n        try {\n            // Wait a while for existing tasks to terminate\n            if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {\n                pool.shutdownNow(); // Cancel currently executing tasks\n                // Wait a while for tasks to respond to being cancelled\n                if (!pool.awaitTermination(60, TimeUnit.SECONDS))\n                    logger.warn(\"Pool did not terminate\");\n            }\n        } catch (InterruptedException ie) {\n            // (Re-)Cancel if current thread also interrupted\n            pool.shutdownNow();\n            // Preserve interrupt status\n            Thread.currentThread().interrupt();\n        }\n    }\n\n\n    private final class DelegatingEventListener implements BinaryLogClient.EventListener {\n\n        private final Map<Long, TableMapEventData> tablesById = new HashMap<Long, TableMapEventData>();\n        private final Map<String, Map<Integer, Map<String, Object>>> tablesColumnMap = new HashMap<>();\n\n        private boolean transactionInProgress;\n        private String binlogFilename;\n\n\n        //当发现ddl语句时 需要更新重新取列名\n        private Map<Integer, Map<String, Object>> loadColumn(String database, String table) {\n            Map<Integer, Map<String, Object>> rtn = new HashMap<>();\n            List<Map<String, Object>> list = null;\n            Connection con = null;\n            try {\n                con = DriverManager.getConnection(\"jdbc:mysql://\" + hostname + \":\" + port, username, password);\n                list = executeQuery(con, \"select  COLUMN_NAME, ORDINAL_POSITION, DATA_TYPE, CHARACTER_SET_NAME from INFORMATION_SCHEMA.COLUMNS where table_name='\" + table + \"' and TABLE_SCHEMA='\" + database + \"'\");\n\n            } catch (SQLException e) {\n                throw new RuntimeException(e);\n            } finally {\n                JdbcUtils.close(con);\n            }\n            for (Map<String, Object> stringObjectMap : list) {\n                BigInteger pos = (BigInteger) stringObjectMap.get(\"ORDINAL_POSITION\");\n                rtn.put(pos.intValue(), stringObjectMap);\n            }\n            return rtn;\n        }\n\n        @Override\n        public void onEvent(Event event) {\n            logger.debug(\"----->migrate binlog event:\" + event.toString());\n            EventType eventType = event.getHeader().getEventType();\n            switch (eventType) {\n                case TABLE_MAP:\n                    TableMapEventData tableMapEventData = event.getData();\n                    tablesById.put(tableMapEventData.getTableId(), tableMapEventData);\n                    if (!tablesColumnMap.containsKey(tableMapEventData.getDatabase() + \".\" + tableMapEventData.getTable())) {\n                        tablesColumnMap.put(tableMapEventData.getDatabase() + \".\" + tableMapEventData.getTable(), loadColumn(tableMapEventData.getDatabase(), tableMapEventData.getTable()));\n                    }\n                    break;\n                case ROTATE:\n                    RotateEventData data = event.getData();\n                    binlogFilename = data.getBinlogFilename();\n                    break;\n                case PRE_GA_WRITE_ROWS:\n                case WRITE_ROWS:\n                case EXT_WRITE_ROWS:\n                    handleWriteRowsEvent(event);\n                    break;\n                case PRE_GA_UPDATE_ROWS:\n                case UPDATE_ROWS:\n                case EXT_UPDATE_ROWS:\n                    handleUpdateRowsEvent(event);\n                    break;\n                case PRE_GA_DELETE_ROWS:\n                case DELETE_ROWS:\n                case EXT_DELETE_ROWS:\n                    handleDeleteRowsEvent(event);\n                    break;\n                case QUERY:\n                    if (groupEventsByTX) {\n                        QueryEventData queryEventData = event.getData();\n                        String query = queryEventData.getSql();\n                        if (\"BEGIN\".equals(query)) {\n                            transactionInProgress = true;\n                        } else if (!query.startsWith(\"#\")) {\n                            handleOtherSqlEvent(event);\n                        }\n                    }\n                    break;\n                case XID:\n                    if (groupEventsByTX) {\n                        transactionInProgress = false;\n                    }\n\n                    break;\n                default:\n                    // ignore\n            }\n        }\n\n        private void exeSql(MigrateTask task, String sql) {\n            if (task.isHaserror())\n                return;\n            task.setHasExecute(true);\n            String dataHostTo = MigrateUtils.getDataHostFromDataNode(task.getTo());\n            Semaphore semaphore = semaphoreMap.get(dataHostTo);\n            try {\n                semaphore.acquire();\n            } catch (InterruptedException e) {\n                Thread.currentThread().interrupt();\n            }\n            SqlExecuteListener listener = new SqlExecuteListener(task, sql, BinlogStream.this,\n                    semaphore);\n            OneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler(new String[0],\n                    listener);\n            resultHandler.setMark(\"binlog execute\");\n            PhysicalDBNode dn = MycatServer.getInstance().getConfig().getDataNodes().get(task.getTo());\n            SQLJob sqlJob = new SQLJob(sql, dn.getDatabase(), resultHandler, dn.getDbPool().getSource());\n            listener.setSqlJob(sqlJob);\n            sqlJob.run();\n        }\n\n        private void handleOtherSqlEvent(Event event) {\n            QueryEventData queryEventData = event.getData();\n            logger.debug(\"receve sql:\", queryEventData.getSql());\n            SchemaUtil.SchemaInfo schemaInfo = SchemaUtil.parseSchema(queryEventData.getSql());\n            if (isShouldBeFilter(queryEventData.getDatabase(), schemaInfo.table))\n                return;\n            String query = queryEventData.getSql();\n            for (MigrateTask migrateTask : migrateTaskList) {\n                if (schemaInfo.table.equalsIgnoreCase(migrateTask.getTable())\n                        && queryEventData.getDatabase().equalsIgnoreCase(MigrateUtils.getDatabaseFromDataNode(migrateTask.getFrom()))) {\n                    exeSql(migrateTask, query);\n                }\n            }\n\n\n        }\n\n        private boolean isShouldBeFilter(String database, String table) {\n            if (Strings.isNullOrEmpty(database))\n                return true;\n            if (Strings.isNullOrEmpty(table))\n                return true;\n            if (!databaseSet.contains(database.toLowerCase())) {\n                return true;\n            }\n            for (MigrateTask migrateTask : migrateTaskList) {\n                if (database.equals(MigrateUtils.getDatabaseFromDataNode(migrateTask.getFrom())) && table.equalsIgnoreCase(migrateTask.getTable())) {\n                    return false;\n                }\n            }\n\n\n            return true;\n        }\n\n\n        private void handleWriteRowsEvent(Event event) {\n            WriteRowsEventData eventData = event.getData();\n            TableMapEventData tableMapEvent = tablesById.get(eventData.getTableId());\n            if (isShouldBeFilter(tableMapEvent.getDatabase(), tableMapEvent.getTable()))\n                return;\n            Map<Integer, Map<String, Object>> xxx = tablesColumnMap.get(tableMapEvent.getDatabase() + \".\" + tableMapEvent.getTable());\n            BitSet inculudeColumn = eventData.getIncludedColumns();\n            StringBuilder sb = new StringBuilder(\"insert into  \");\n            sb.append(tableMapEvent.getTable());\n            sb.append(\"(\");\n            int size = inculudeColumn.length();\n            List<Serializable[]> rows = eventData.getRows();\n\n            int slot = -1;\n            for (int i = 0; i < size; i++) {\n                int column = inculudeColumn.nextSetBit(i);\n                Map<String, Object> coumnMap = xxx.get(column + 1);\n                sb.append(coumnMap.get(\"COLUMN_NAME\"));\n                if (i != size - 1) {\n                    sb.append(\",\");\n                }\n            }\n            sb.append(\")  values  \");\n            for (int i = 0; i < rows.size(); i++) {\n                Serializable[] value = rows.get(i);\n                sb.append(\" (\");\n                for (int y = 0; y < size; y++) {\n                    int column = inculudeColumn.nextSetBit(y);\n                    Map<String, Object> coumnMap = xxx.get(column + 1);\n                    String dataType = (String) coumnMap.get(\"DATA_TYPE\");\n                    String columnName = (String) coumnMap.get(\"COLUMN_NAME\");\n                    if (\"_slot\".equalsIgnoreCase(columnName)) {\n                        slot = value[y] instanceof BigInteger ? ((BigInteger) value[y]).intValue() : ((Integer) value[y]);\n                    }\n                    sb.append(convertBinlogValue(value[y], dataType));\n\n                    if (y != size - 1) {\n                        sb.append(\",\");\n                    }\n                }\n                sb.append(\")\");\n                if (i != rows.size() - 1) {\n                    sb.append(\",\");\n                }\n            }\n\n            checkIfExeSql(tableMapEvent, sb, slot);\n\n        }\n\n        private void checkIfExeSql(TableMapEventData tableMapEvent, StringBuilder sb, int slot) {\n            for (MigrateTask migrateTask : migrateTaskList) {\n                if (tableMapEvent.getTable().equalsIgnoreCase(migrateTask.getTable())\n                        && tableMapEvent.getDatabase().equalsIgnoreCase(MigrateUtils.getDatabaseFromDataNode(migrateTask.getFrom()))) {\n                    for (PartitionByCRC32PreSlot.Range range : migrateTask.getSlots()) {\n                        if (range.end >= slot && range.start <= slot) {\n                            exeSql(migrateTask, sb.toString());\n                            return;\n                        }\n                    }\n\n                }\n            }\n        }\n\n        private Object convertBinlogValue(Serializable value, String dataType) {\n            if (value instanceof String) {\n                return \"'\" + ((String) value).replace(\"'\", \"\\\\'\") + \"'\";\n            } else if (value instanceof byte[]) {\n                //确认编码\n                return \"'\" + new String((byte[]) value, charset).replace(\"'\", \"\\\\'\") + \"'\";\n            } else if (value instanceof Date) {\n                return \"'\" + dateToString((Date) value, dataType) + \"'\";\n            } else if ((\"date\".equalsIgnoreCase(dataType)) && value instanceof Long) {\n                return \"'\" + dateToStringFromUTC((Long) value) + \"'\";\n                //mariadb   date\n\n            } else if (\"datetime\".equalsIgnoreCase(dataType) && value instanceof Long) {\n                return \"'\" + datetimeToStringFromUTC((Long) value) + \"'\";\n                //mariadb   date\n\n            } else if ((\"timestamp\".equalsIgnoreCase(dataType)) && value instanceof Long) {\n                return \"'\" + dateToString((Long) value) + \"'\";\n                //mariadb   date\n\n            } else {\n                return value;\n            }\n        }\n\n        private String dateToStringFromUTC(Long date) {\n            DateTime dt = new DateTime(date, DateTimeZone.UTC);\n            return dt.toString(DateUtil.DATE_PATTERN_ONLY_DATE);\n        }\n\n        private String datetimeToStringFromUTC(Long date) {\n            DateTime dt = new DateTime(date, DateTimeZone.UTC);\n            return dt.toString(DateUtil.DATE_PATTERN_FULL);\n        }\n\n        private String dateToString(Long date) {\n            DateTime dt = new DateTime(date);\n            return dt.toString(DateUtil.DATE_PATTERN_FULL);\n        }\n\n        private String dateToString(Date date, String dateType) {\n            if (\"timestamp\".equalsIgnoreCase(dateType)) {\n                DateTime dt = new DateTime(date);\n                return dt.toString(DateUtil.DATE_PATTERN_FULL);\n            } else if (\"datetime\".equalsIgnoreCase(dateType)) {\n                DateTime dt = new DateTime(date, DateTimeZone.UTC);\n                return dt.toString(DateUtil.DATE_PATTERN_FULL);\n            } else if (\"date\".equalsIgnoreCase(dateType)) {\n                DateTime dt = new DateTime(date, DateTimeZone.UTC);\n                return dt.toString(DateUtil.DATE_PATTERN_ONLY_DATE);\n            } else {\n                DateTime dt = new DateTime(date);\n                return dt.toString(DateUtil.DATE_PATTERN_FULL);\n            }\n\n        }\n\n        private void handleUpdateRowsEvent(Event event) {\n            UpdateRowsEventData eventData = event.getData();\n            TableMapEventData tableMapEvent = tablesById.get(eventData.getTableId());\n            if (isShouldBeFilter(tableMapEvent.getDatabase(), tableMapEvent.getTable()))\n                return;\n            Map<Integer, Map<String, Object>> xxx = tablesColumnMap.get(tableMapEvent.getDatabase() + \".\" + tableMapEvent.getTable());\n            BitSet inculudeColumn = eventData.getIncludedColumns();\n            StringBuilder sba = new StringBuilder(\"update \");\n            sba.append(tableMapEvent.getTable());\n            sba.append(\" set \");\n            int size = inculudeColumn.length();\n\n            List<Map.Entry<Serializable[], Serializable[]>> rows = eventData.getRows();\n            for (Map.Entry<Serializable[], Serializable[]> row : rows) {\n                StringBuilder sb = new StringBuilder(sba);\n                int slot = -1;\n                Map.Entry<Serializable[], Serializable[]> rowMap = row;\n                Serializable[] value = rowMap.getValue();\n                Serializable[] key = rowMap.getKey();\n                for (int i = 0; i < size; i++) {\n                    int column = inculudeColumn.nextSetBit(i);\n                    Map<String, Object> coumnMap = xxx.get(column + 1);\n                    sb.append(coumnMap.get(\"COLUMN_NAME\"));\n                    sb.append(\"=\");\n                    String dataType = (String) coumnMap.get(\"DATA_TYPE\");\n                    sb.append(convertBinlogValue(value[i], dataType));\n\n                    if (i != size - 1) {\n                        sb.append(\",\");\n                    }\n                }\n                sb.append(\" where \");\n\n                BitSet includedColumnsBeforeUpdate = eventData.getIncludedColumnsBeforeUpdate();\n                for (int i = 0; i < size; i++) {\n                    int column = includedColumnsBeforeUpdate.nextSetBit(i);\n                    Map<String, Object> coumnMap = xxx.get(column + 1);\n                    sb.append(coumnMap.get(\"COLUMN_NAME\"));\n                    Serializable value1 = key[i];\n                    if (value1 == null) {\n                        sb.append(\" is null\");\n                    } else {\n                        sb.append(\"=\");\n                        String dataType = (String) coumnMap.get(\"DATA_TYPE\");\n\n                        sb.append(convertBinlogValue(value1, dataType));\n                    }\n                    String columnName = (String) coumnMap.get(\"COLUMN_NAME\");\n                    if (\"_slot\".equalsIgnoreCase(columnName)) {\n                        slot = value1 instanceof BigInteger ? ((BigInteger) value1).intValue() : ((Integer) value1);\n                    }\n                    if (i != size - 1) {\n                        sb.append(\" and \");\n                    }\n                }\n\n                checkIfExeSql(tableMapEvent, sb, slot);\n            }\n\n\n        }\n\n        private void handleDeleteRowsEvent(Event event) {\n            DeleteRowsEventData eventData = event.getData();\n            TableMapEventData tableMapEvent = tablesById.get(eventData.getTableId());\n            if (isShouldBeFilter(tableMapEvent.getDatabase(), tableMapEvent.getTable()))\n                return;\n            Map<Integer, Map<String, Object>> xxx = tablesColumnMap.get(tableMapEvent.getDatabase() + \".\" + tableMapEvent.getTable());\n            BitSet inculudeColumn = eventData.getIncludedColumns();\n            StringBuilder sba = new StringBuilder(\"delete from \");\n            sba.append(tableMapEvent.getTable());\n            sba.append(\" where \");\n            int size = inculudeColumn.length();\n            List<Serializable[]> rows = eventData.getRows();\n            for (Serializable[] row : rows) {\n                StringBuilder sb = new StringBuilder(sba);\n                Serializable[] value = row;\n\n\n                int slot = -1;\n                for (int i = 0; i < size; i++) {\n                    int column = inculudeColumn.nextSetBit(i);\n                    Map<String, Object> coumnMap = xxx.get(column + 1);\n                    sb.append(coumnMap.get(\"COLUMN_NAME\"));\n                    Serializable value1 = value[i];\n                    if (value1 == null) {\n                        sb.append(\" is null\");\n                    } else {\n                        sb.append(\"=\");\n                        String dataType = (String) coumnMap.get(\"DATA_TYPE\");\n\n                        sb.append(convertBinlogValue(value1, dataType));\n                    }\n                    String columnName = (String) coumnMap.get(\"COLUMN_NAME\");\n                    if (\"_slot\".equalsIgnoreCase(columnName)) {\n                        slot = value1 instanceof BigInteger ? ((BigInteger) value1).intValue() : ((Integer) value1);\n                    }\n                    if (i != size - 1) {\n                        sb.append(\" and \");\n                    }\n                }\n                checkIfExeSql(tableMapEvent, sb, slot);\n\n            }\n\n\n        }\n\n\n    }\n\n    public static void main(String[] args) {\n        BinlogStream stream = new BinlogStream(\"localhost\", 3306, \"root\", \"123\", Charset.defaultCharset());\n        try {\n            stream.setSlaveID(23511);\n            stream.setBinglogFile(\"mysql-bin.000028\");\n            stream.setBinlogPos(1082);\n            stream.connect();\n\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n\n//        String sql=\"2'aa\\\"啊啊402\";\n//        System.out.println(sql.replace(\"'\",\"\\\\'\"));\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/BinlogStreamHoder.java",
    "content": "package io.mycat.migrate;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * Created by magicdoom on 2016/12/25.\n */\npublic class BinlogStreamHoder {\n    public static ConcurrentMap<String,BinlogStream> binlogStreamMap=new ConcurrentHashMap<>();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/MigrateDumpRunner.java",
    "content": "package io.mycat.migrate;\n\nimport com.alibaba.druid.util.JdbcUtils;\nimport com.alibaba.fastjson.JSON;\nimport com.google.common.base.Joiner;\nimport com.google.common.base.Strings;\nimport com.google.common.io.Files;\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.config.model.DBHostConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.memory.environment.OperatingSystem;\nimport io.mycat.route.function.PartitionByCRC32PreSlot.Range;\nimport io.mycat.util.ProcessUtil;\nimport io.mycat.util.StringUtil;\nimport io.mycat.util.ZKUtils;\nimport io.mycat.util.dataMigrator.DataMigratorUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport static io.mycat.util.dataMigrator.DataMigratorUtil.executeQuery;\n\n\n/**\n * Created by nange on 2016/12/1.\n */\npublic class MigrateDumpRunner implements Runnable {\n    private static final Logger LOGGER = LoggerFactory.getLogger(MigrateDumpRunner.class);\n    private MigrateTask task;\n    private CountDownLatch latch;\n    private AtomicInteger sucessTask;\n\n    public MigrateDumpRunner(MigrateTask task, CountDownLatch latch, AtomicInteger sucessTask) {\n        this.task = task;\n        this.latch = latch;\n        this.sucessTask = sucessTask;\n    }\n\n    @Override\n    public void run() {\n        try {\n            String mysqldump = \"?mysqldump -h? -P? -u? -p?  ? ? --single-transaction -q --default-character-set=utf8mb4 --hex-blob --where=\\\"?\\\" --master-data=1  -T  \\\"?\\\"  --fields-enclosed-by=\\\\\\\" --fields-terminated-by=, --lines-terminated-by=\\\\n  --fields-escaped-by=\\\\\\\\ \";\n            PhysicalDBPool dbPool = MycatServer.getInstance().getConfig().getDataNodes().get(task.getFrom()).getDbPool();\n            PhysicalDatasource datasource = dbPool.getSources()[dbPool.getActivedIndex()];\n            DBHostConfig config = datasource.getConfig();\n            File file = null;\n            String spath = querySecurePath(config);\n            if (Strings.isNullOrEmpty(spath) || \"NULL\".equalsIgnoreCase(spath) || \"empty\".equalsIgnoreCase(spath)) {\n                file = new File(SystemConfig.getHomePath() + File.separator + \"temp\", \"dump\" + File.separator + task.getFrom() + \"_\" + task.getTo());\n                //  task.getFrom() + \"_\" + task.getTo() + Thread.currentThread().getId() + System.currentTimeMillis() + \"\");\n            } else {\n                spath += Thread.currentThread().getId() + System.currentTimeMillis();\n                file = new File(spath);\n            }\n            file.mkdirs();\n\n            String encose = OperatingSystem.isWindows() ? \"\\\\\" : \"\";\n            String finalCmd = DataMigratorUtil\n                    .paramsAssignment(mysqldump, \"?\", \"\", config.getIp(), String.valueOf(config.getPort()), config.getUser(),\n                            config.getPassword(), MigrateUtils.getDatabaseFromDataNode(task.getFrom()), task.getTable(), makeWhere(task), file.getPath());\n            List<String> args = Arrays.asList(\"mysqldump\", \"-h\" + config.getIp(), \"-P\" + String.valueOf(config.getPort()), \"-u\" + config.getUser(),\n                    !StringUtil.isEmpty(config.getPassword()) ? \"-p\" + config.getPassword() : \"\", MigrateUtils.getDatabaseFromDataNode(task.getFrom()), task.getTable(), \"--single-transaction\", \"-q\", \"--default-character-set=utf8mb4\", \"--hex-blob\", \"--where=\" + makeWhere(task), \"--master-data=1\", \"-T\" + file.getPath()\n\n                    , \"--fields-enclosed-by=\" + encose + \"\\\"\", \"--fields-terminated-by=,\", \"--lines-terminated-by=\\\\n\", \"--fields-escaped-by=\\\\\\\\\");\n            LOGGER.info(\"migrate 中 mysqldump准备执行命令,如果超长时间没有响应则可能出错\");\n            LOGGER.info(args.toString());\n            String result = ProcessUtil.execReturnString(args);\n            int logIndex = result.indexOf(\"MASTER_LOG_FILE='\");\n            int logPosIndex = result.indexOf(\"MASTER_LOG_POS=\");\n            String logFile = result.substring(logIndex + 17, logIndex + 17 + result.substring(logIndex + 17).indexOf(\"'\"));\n            String logPos = result.substring(logPosIndex + 15, logPosIndex + 15 + result.substring(logPosIndex + 15).indexOf(\";\"));\n            task.setBinlogFile(logFile);\n            task.setPos(Integer.parseInt(logPos));\n            File dataFile = new File(file, task.getTable() + \".txt\");\n\n            File sqlFile = new File(file, task.getTable() + \".sql\");\n            if (!sqlFile.exists()) {\n                LOGGER.debug(sqlFile.getAbsolutePath() + \"not  exists\");\n            }\n            List<String> createTable = Files.readLines(sqlFile, Charset.forName(\"UTF-8\"));\n            LOGGER.info(\"migrate 中 准备自动创建新的table:\"+createTable);\n            exeCreateTableToDn(extractCreateSql(createTable), task.getTo(), task.getTable());\n            if (dataFile.length() > 0) {\n\n                loaddataToDn(dataFile, task.getTo(), task.getTable());\n            }\n            pushMsgToZK(task.getZkpath(), task.getFrom() + \"-\" + task.getTo(), 1, \"sucess\", logFile, logPos);\n\n            DataMigratorUtil.deleteDir(file);\n            sucessTask.getAndIncrement();\n        } catch (Exception e) {\n            try {\n                pushMsgToZK(task.getZkpath(), task.getFrom() + \"-\" + task.getTo(), 0, e.getLocalizedMessage(), \"\", \"\");\n            } catch (Exception e1) {\n            }\n            LOGGER.error(\"error:\", e);\n        } finally {\n            latch.countDown();\n        }\n\n\n    }\n\n    private String extractCreateSql(List<String> lines) {\n        StringBuilder sb = new StringBuilder();\n        boolean isAdd = false;\n        for (String line : lines) {\n            if (Strings.isNullOrEmpty(line) || line.startsWith(\"--\") || line.startsWith(\"/*\") || line.startsWith(\"DROP\")) {\n                isAdd = false;\n                continue;\n            }\n            if (line.startsWith(\"CREATE\")) {\n                isAdd = true;\n            }\n\n            if (isAdd) {\n                sb.append(line).append(\"\\n\");\n            }\n        }\n        String rtn = sb.toString();\n        if (rtn.endsWith(\";\\n\")) {\n            rtn = rtn.substring(0, rtn.length() - 2);\n        }\n        return rtn.replace(\"CREATE TABLE\", \"CREATE TABLE IF not EXISTS \");\n    }\n\n    private void exeCreateTableToDn(String sql, String toDn, String table) throws SQLException {\n        PhysicalDBNode dbNode = MycatServer.getInstance().getConfig().getDataNodes().get(toDn);\n        PhysicalDBPool dbPool = dbNode.getDbPool();\n        PhysicalDatasource datasource = dbPool.getSources()[dbPool.getActivedIndex()];\n        DBHostConfig config = datasource.getConfig();\n        Connection con = null;\n        try {\n            con = DriverManager.getConnection(\"jdbc:mysql://\" + config.getUrl() + \"/\" + dbNode.getDatabase(), config.getUser(), config.getPassword());\n            JdbcUtils.execute(con, sql, new ArrayList<>());\n        } finally {\n            JdbcUtils.close(con);\n        }\n    }\n\n\n    private void pushMsgToZK(String rootZkPath, String child, int status, String msg, String binlogFile, String pos) throws Exception {\n        LOGGER.error(msg);\n        String path = rootZkPath + \"/\" + child;\n        TaskStatus taskStatus = new TaskStatus();\n        taskStatus.setMsg(msg);\n        taskStatus.setStatus(status);\n        task.setStatus(status);\n        taskStatus.setBinlogFile(binlogFile);\n        taskStatus.setPos(Long.parseLong(pos));\n\n        if (ZKUtils.getConnection().checkExists().forPath(path) == null) {\n            ZKUtils.getConnection().create().forPath(path, JSON.toJSONBytes(taskStatus));\n        } else {\n            ZKUtils.getConnection().setData().forPath(path, JSON.toJSONBytes(taskStatus));\n        }\n    }\n\n    private void loaddataToDn(File loaddataFile, String toDn, String table) throws SQLException, IOException {\n        PhysicalDBNode dbNode = MycatServer.getInstance().getConfig().getDataNodes().get(toDn);\n        PhysicalDBPool dbPool = dbNode.getDbPool();\n        PhysicalDatasource datasource = dbPool.getSources()[dbPool.getActivedIndex()];\n        DBHostConfig config = datasource.getConfig();\n        Connection con = null;\n        try {\n            con = DriverManager.getConnection(\"jdbc:mysql://\" + config.getUrl() + \"/\" + dbNode.getDatabase(), config.getUser(), config.getPassword());\n            String sql = \"load data local infile '\" + loaddataFile.getPath().replace(\"\\\\\", \"//\") + \"' replace into table \" + table + \" character set 'utf8mb4'  fields terminated by ','  enclosed by '\\\"'  ESCAPED BY '\\\\\\\\'  lines terminated by '\\\\n'\";\n            JdbcUtils.execute(con, sql, new ArrayList<>());\n        }\n        catch (Exception e){\n            try {\n                pushMsgToZK(task.getZkpath(), task.getFrom() + \"-\" + task.getTo(), 0, e.getLocalizedMessage(), \"\", \"\");\n            } catch (Exception e1) {\n            }\n        }\n        finally {\n            JdbcUtils.close(con);\n        }\n    }\n\n    private String makeWhere(MigrateTask task) {\n        List<String> whereList = new ArrayList<>();\n        List<Range> slotRanges = task.getSlots();\n        for (Range slotRange : slotRanges) {\n            if (slotRange.start == slotRange.end) {\n                whereList.add(\"_slot =\" + slotRange.start);\n            } else {\n                whereList.add(\"(_slot >=\" + slotRange.start + \" and _slot <=\" + slotRange.end + \")\");\n            }\n        }\n\n        return Joiner.on(\" or  \").join(whereList);\n    }\n\n    private static String querySecurePath(DBHostConfig config) {\n        List<Map<String, Object>> list = null;\n        String path = null;\n        Connection con = null;\n        try {\n            con = DriverManager.getConnection(\"jdbc:mysql://\" + config.getUrl(), config.getUser(), config.getPassword());\n            list = executeQuery(con, \"show variables like 'secure_file_priv'\");\n            if (list != null && list.size() == 1)\n                path = (String) list.get(0).get(\"Value\");\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        } finally {\n            JdbcUtils.close(con);\n        }\n        return path;\n    }\n\n    public static void main(String[] args) {\n        String result = \"\\n\" + \"--\\n\" + \"-- Position to start replication or point-in-time recovery from\\n\" + \"--\\n\"\n                + \"\\n\" + \"CHANGE MASTER TO MASTER_LOG_FILE='NANGE-PC-bin.000021', MASTER_LOG_POS=154;\\n\";\n        int logIndex = result.indexOf(\"MASTER_LOG_FILE='\");\n        int logPosIndex = result.indexOf(\"MASTER_LOG_POS=\");\n        String logFile = result.substring(logIndex + 17, logIndex + 17 + result.substring(logIndex + 17).indexOf(\"'\"));\n        String logPos = result.substring(logPosIndex + 15, logPosIndex + 15 + result.substring(logPosIndex + 15).indexOf(\";\"));\n        System.out.println(logFile + logPos);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/MigrateMainRunner.java",
    "content": "package io.mycat.migrate;\n\nimport com.alibaba.fastjson.JSON;\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.config.model.DBHostConfig;\nimport io.mycat.util.ZKUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.util.List;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * Created by magicdoom on 2016/12/8.\n */\npublic class MigrateMainRunner implements Runnable {\n    private static final Logger LOGGER = LoggerFactory.getLogger(MigrateMainRunner.class);\n    private String dataHost;\n    private List<MigrateTask> migrateTaskList;\n    private int timeout;\n    private Charset charset;\n    private boolean forceBinlog;\n\n    public MigrateMainRunner(String dataHost, List<MigrateTask> migrateTaskList, int timeout, Charset charset,boolean forceBinlog) {\n        this.dataHost = dataHost;\n        this.migrateTaskList = migrateTaskList;\n        this.timeout = timeout;\n        this.charset = charset;\n        this.forceBinlog = forceBinlog;\n    }\n\n    @Override\n    public void run() {\n        try{\n        AtomicInteger sucessTask = new AtomicInteger(0);\n        if (!forceBinlog) {\n            LOGGER.info(\"migrate 中 进入 mysqldump阶段\");\n            CountDownLatch downLatch = new CountDownLatch(migrateTaskList.size());\n            for (MigrateTask migrateTask : migrateTaskList) {\n                MycatServer.getInstance().getBusinessExecutor().submit(new MigrateDumpRunner(migrateTask, downLatch, sucessTask));\n            }\n            try {\n                //modify by jian.xie 需要等到dumprunner执行结束 timeout需要改成用户指定的超时时间@cjw\n                downLatch.await(this.timeout, TimeUnit.MINUTES);\n            } catch (InterruptedException e) {\n                Thread.currentThread().interrupt();\n            }\n        }else {\n            LOGGER.info(\"migrate 中 不进入 mysqldump阶段,直接进入binlog stream\");\n        }\n        //同一个dataHost的任务合并执行，避免过多流量浪费\n        if (forceBinlog||(sucessTask.get() == migrateTaskList.size())) {\n            long binlogFileNum = -1;\n            String binlogFile = \"\";\n            long pos = -1;\n            for (MigrateTask migrateTask : migrateTaskList) {\n                if (binlogFileNum == -1) {\n                    binlogFileNum = Integer.parseInt(migrateTask.getBinlogFile().substring(migrateTask.getBinlogFile().lastIndexOf(\".\") + 1));\n                    binlogFile = migrateTask.getBinlogFile();\n                    pos = migrateTask.getPos();\n                } else {\n                    int tempBinlogFileNum = Integer.parseInt(migrateTask.getBinlogFile().substring(migrateTask.getBinlogFile().lastIndexOf(\".\") + 1));\n                    if (tempBinlogFileNum <= binlogFileNum && migrateTask.getPos() <= pos) {\n                        binlogFileNum = tempBinlogFileNum;\n                        binlogFile = migrateTask.getBinlogFile();\n                        pos = migrateTask.getPos();\n                    }\n                }\n            }\n            String taskPath = migrateTaskList.get(0).getZkpath();\n            taskPath = taskPath.substring(0, taskPath.lastIndexOf(\"/\"));\n            String taskID = taskPath.substring(taskPath.lastIndexOf('/') + 1, taskPath.length());\n\n            //开始增量数据迁移\n            PhysicalDBPool dbPool = MycatServer.getInstance().getConfig().getDataHosts().get(dataHost);\n            PhysicalDatasource datasource = dbPool.getSources()[dbPool.getActivedIndex()];\n            DBHostConfig config = datasource.getConfig();\n            BinlogStream stream = new BinlogStream(config.getUrl().substring(0, config.getUrl().indexOf(\":\")), config.getPort(), config.getUser(), config.getPassword(), charset);\n            try {\n                stream.setSlaveID(migrateTaskList.get(0).getSlaveId());\n                stream.setBinglogFile(binlogFile);\n                stream.setBinlogPos(pos);\n                stream.setMigrateTaskList(migrateTaskList);\n                BinlogStreamHoder.binlogStreamMap.put(taskID, stream);\n                stream.connect();\n\n            } catch (IOException e) {\n                LOGGER.error(\"migrate 中 binlog 连接 异常\");\n\n                try {\n                    TaskNode taskNode = JSON.parseObject(ZKUtils.getConnection().getData().forPath(taskPath), TaskNode.class);\n                    taskNode.addException(e.getLocalizedMessage());\n                    ZKUtils.getConnection().setData().forPath(taskPath, JSON.toJSONBytes(taskNode));\n                } catch (Exception e1) {\n\n                    LOGGER.error(\"error:\", e);\n                }\n                LOGGER.error(\"error:\", e);\n            }\n        }\n        }catch (Exception e){\n            LOGGER.error(\"migrate 中 binlog 连接 异常\");\n            e.printStackTrace();\n            LOGGER.error(e.getLocalizedMessage());\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/MigrateTask.java",
    "content": "package io.mycat.migrate;\n\nimport io.mycat.route.function.PartitionByCRC32PreSlot;\nimport io.mycat.route.function.PartitionByCRC32PreSlot.Range;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by magicdoom on 2016/9/15.\n */\npublic class MigrateTask implements Serializable {\n\n    private String from; //from dataNode\n    private String to;  // to dataNode\n    private String table; //\n    private List<Range> slots=new ArrayList<>(); // crc range\n\n    private String method;\n    private String fclass=PartitionByCRC32PreSlot.class.getName();\n\n    private String schema;\n\n\n    private int slaveId;\n\n    private transient String zkpath; //mycat/mycat-cluster-1/migrate/TESTDB/411c53ae7da84e418aed1a3909926933/localhost1 \n    \t\t\t\t\t\t\t\t//\n    private transient String binlogFile;\n    private transient int pos;\n    private transient volatile Date lastBinlogDate;\n    private transient volatile boolean haserror=false;\n    private transient volatile int status;\n\n    private transient volatile boolean hasExecute=false;\n\n    public int getStatus() {\n        return status;\n    }\n\n    public void setStatus(int status) {\n        this.status = status;\n    }\n\n    public boolean isHaserror() {\n        return haserror;\n    }\n\n    public void setHaserror(boolean haserror) {\n        this.haserror = haserror;\n    }\n\n    public List<Range> getSlots() {\n        return slots;\n    }\n\n    public void setSlots(List<Range> slots) {\n        this.slots = slots;\n    }\n\n    public int getSize()\n    {   int size=0;\n        for (Range slot : slots) {\n           size=size+slot.size;\n        }\n        return size;\n    }\n\n    public boolean isHasExecute() {\n        return hasExecute;\n    }\n\n    public void setHasExecute(boolean hasExecute) {\n        this.hasExecute = hasExecute;\n    }\n\n    public String getBinlogFile() {\n        return binlogFile;\n    }\n\n    public void setBinlogFile(String binlogFile) {\n        this.binlogFile = binlogFile;\n    }\n\n    public int getPos() {\n        return pos;\n    }\n\n    public void setPos(int pos) {\n        this.pos = pos;\n    }\n\n    public String getFrom() {\n        return from;\n    }\n\n    public Date getLastBinlogDate() {\n        return lastBinlogDate;\n    }\n\n    public void setLastBinlogDate(Date lastBinlogDate) {\n        this.lastBinlogDate = lastBinlogDate;\n    }\n\n    public void setFrom(String from) {\n        this.from = from;\n    }\n\n    public String getTo() {\n        return to;\n    }\n\n    public void setTo(String to) {\n        this.to = to;\n    }\n\n    public String getTable() {\n        return table;\n    }\n\n    public void setTable(String table) {\n        this.table = table;\n    }\n\n    public String getMethod() {\n        return method;\n    }\n\n    public void setMethod(String method) {\n        this.method = method;\n    }\n\n    public String getFclass() {\n        return fclass;\n    }\n\n    public void setFclass(String fclass) {\n        this.fclass = fclass;\n    }\n\n    public String getSchema() {\n        return schema;\n    }\n\n    public void setSchema(String schema) {\n        this.schema = schema;\n    }\n\n    public int getSlaveId() {\n        return slaveId;\n    }\n\n    public void setSlaveId(int slaveId) {\n        this.slaveId = slaveId;\n    }\n\n    public String getZkpath() {\n        return zkpath;\n    }\n\n    public void setZkpath(String zkpath) {\n        this.zkpath = zkpath;\n    }\n\n    public void addSlots(Range range)\n    {\n        slots.add(range);\n    }\n\n    public void addSlots(List<Range> ranges)\n    {\n        slots.addAll(ranges);\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/MigrateTaskWatch.java",
    "content": "package io.mycat.migrate;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONArray;\nimport com.google.common.base.Splitter;\nimport com.google.common.base.Strings;\nimport com.google.common.collect.Lists;\nimport io.mycat.MycatServer;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.util.ZKUtils;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;\nimport org.apache.curator.framework.recipes.locks.InterProcessMutex;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.nio.charset.Charset;\nimport java.util.*;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * ......./migrate/schemal/taskid/datahost   [任务数据]\n * Created by magicdoom on 2016/9/28.\n */\npublic class MigrateTaskWatch {\n    private static final Logger LOGGER = LoggerFactory.getLogger(MigrateTaskWatch.class);\n\n    public static void start() {\n        String migratePath = ZKUtils.getZKBasePath() + \"migrate\";\n        // modify by jian.xie,cjw,zwy 如果migrate 启动的时候不存在，无法监听，需要这里监听一次\n        // 如果第一次没有migrate节点这里应该无法使用集群 还需优化\n        try {\n            CuratorFramework client = ZKUtils.getConnection();\n            if (client.checkExists().forPath(migratePath) == null) {\n                client.create().creatingParentsIfNeeded().forPath(migratePath);\n            }\n        }catch (Exception e){\n            throw new RuntimeException(e);\n        }\n        ZKUtils.addChildPathCache(migratePath, new PathChildrenCacheListener() {\n            @Override\n            public void childEvent(CuratorFramework curatorFramework,\n                                   PathChildrenCacheEvent fevent) throws Exception {\n\n                switch (fevent.getType()) {\n                    case CHILD_ADDED:\n                        LOGGER.info(\"table CHILD_ADDED: \" + fevent.getData().getPath());\n                        ZKUtils.addChildPathCache(fevent.getData().getPath(), new TaskPathChildrenCacheListener());\n                        break;\n                    default:\n                        break;\n                }\n            }\n        });\n\n    }\n\n\n    private static class TaskPathChildrenCacheListener implements PathChildrenCacheListener {\n        @Override\n        public void childEvent(CuratorFramework curatorFramework,\n                               PathChildrenCacheEvent event) throws Exception {\n            switch (event.getType()) {\n                case CHILD_ADDED:\n                    if (isTaskErrorOrSucess(event)) break;\n                    addOrUpdate(event);\n                    String path = event.getData().getPath() + \"/_prepare\";\n                    if (curatorFramework.checkExists().forPath(path) == null) {\n                        curatorFramework.create().creatingParentsIfNeeded().forPath(path);\n                    }\n                    ZKUtils.addChildPathCache(path, new SwitchPrepareListener());\n\n                    String commitPath = event.getData().getPath() + \"/_commit\";\n                    if (curatorFramework.checkExists().forPath(commitPath) == null) {\n                        curatorFramework.create().creatingParentsIfNeeded().forPath(commitPath);\n                    }\n                    ZKUtils.addChildPathCache(commitPath, new SwitchCommitListener());\n\n\n                    String cleanPath = event.getData().getPath() + \"/_clean\";\n                    if (curatorFramework.checkExists().forPath(cleanPath) == null) {\n                        curatorFramework.create().creatingParentsIfNeeded().forPath(cleanPath);\n                    }\n                    ZKUtils.addChildPathCache(cleanPath, new SwitchCleanListener());\n                    LOGGER.info(\"table CHILD_ADDED: \" + event.getData().getPath());\n                    break;\n                case CHILD_UPDATED:\n                    if (isTaskErrorOrSucess(event)) break;\n                    addOrUpdate(event);\n                    LOGGER.info(\"CHILD_UPDATED: \" + event.getData().getPath());\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        private boolean isTaskErrorOrSucess(PathChildrenCacheEvent event) {\n            try {\n                TaskNode pTaskNode = JSON.parseObject(event.getData().getData(), TaskNode.class);\n                if (pTaskNode.getStatus() >= 4) {\n                    return true;\n                }\n            } catch (Exception e) {\n\n            }\n\n            return false;\n        }\n\n        private void addOrUpdate(PathChildrenCacheEvent event) throws Exception {\n\n            InterProcessMutex taskLock = null;\n            try {\n                String tpath = event.getData().getPath();\n                String taskID = tpath.substring(tpath.lastIndexOf(\"/\") + 1, tpath.length());\n                String lockPath = ZKUtils.getZKBasePath() + \"lock/\" + taskID + \".lock\";\n                taskLock = new InterProcessMutex(ZKUtils.getConnection(), lockPath);\n                taskLock.acquire(2000, TimeUnit.SECONDS);\n                String text = new String(ZKUtils.getConnection().getData().forPath(event.getData().getPath()), \"UTF-8\");\n                // /migrate/taskId/* 所有的数据\n                List<String> dataNodeList = ZKUtils.getConnection().getChildren().forPath(event.getData().getPath());\n                if (!dataNodeList.isEmpty()) {\n                    if ((!Strings.isNullOrEmpty(text)) && text.startsWith(\"{\")) {\n                        TaskNode taskNode = JSON.parseObject(text, TaskNode.class);\n                        if (taskNode.getStatus() == 0) {\n                            String boosterDataHosts = ZkConfig.getInstance().getValue(ZkParamCfg.MYCAT_BOOSTER_DATAHOSTS);\n                            Set<String> dataNodes = new HashSet<>(Splitter.on(\",\").trimResults().omitEmptyStrings().splitToList(boosterDataHosts));\n                            List<MigrateTask> finalMigrateList = new ArrayList<>();\n                            for (String s : dataNodeList) {\n                                if (\"_prepare\".equals(s))\n                                    continue;\n                                if (dataNodes.contains(s)) {\n                                    String zkpath = event.getData().getPath() + \"/\" + s;\n                                    String data = new String(ZKUtils.getConnection().getData().forPath(zkpath), \"UTF-8\");\n                                    List<MigrateTask> migrateTaskList = JSONArray.parseArray(data, MigrateTask.class);\n                                    for (MigrateTask migrateTask : migrateTaskList) {\n                                        migrateTask.setZkpath(zkpath);\n                                    }\n                                    finalMigrateList.addAll(migrateTaskList);\n                                }\n                            }\n\n                            Map<String, List<MigrateTask>> taskMap = mergerTaskForDataHost(finalMigrateList);\n                            for (Map.Entry<String, List<MigrateTask>> stringListEntry : taskMap.entrySet()) {\n                                String key = stringListEntry.getKey();\n                                List<MigrateTask> value = stringListEntry.getValue();\n                                MycatServer.getInstance().getBusinessExecutor().submit(new MigrateMainRunner(key, value,taskNode.getTimeout(), Charset.forName(taskNode.getCharset()),taskNode.isForceBinlog()));\n                            }\n\n                            //\n                            taskNode.setStatus(1);\n                            ZKUtils.getConnection().setData().forPath(event.getData().getPath(), JSON.toJSONBytes(taskNode));\n                        } else if (taskNode.getStatus() == 2) {\n                            //start switch\n\n                            ScheduledExecutorService scheduledExecutorService = MycatServer.getInstance().getScheduler();\n                            Set<String> allRunnerSet = SwitchPrepareCheckRunner.allSwitchRunnerSet;\n                            if (!allRunnerSet.contains(taskID)) {\n                                List<String> dataHosts = ZKUtils.getConnection().getChildren().forPath(tpath);\n                                List<MigrateTask> allTaskList = MigrateUtils.queryAllTask(tpath, removeStatusNode(dataHosts));\n                                allRunnerSet.add(taskID);\n                                scheduledExecutorService.schedule(new SwitchPrepareCheckRunner(taskID, tpath, taskNode,\n                                        MigrateUtils.convertAllTask(allTaskList)), 1, TimeUnit.SECONDS);\n\n                            }\n                        }\n                    }\n                }\n            } finally {\n                if (taskLock != null) {\n                    taskLock.release();\n                }\n            }\n        }\n\n        private List<String> removeStatusNode(List<String> dataHosts) {\n            List<String> resultList = new ArrayList<>();\n            for (String dataHost : dataHosts) {\n                if (\"_prepare\".equals(dataHost) || \"_commit\".equals(dataHost) || \"_clean\".equals(dataHost)) {\n                    continue;\n                }\n                resultList.add(dataHost);\n            }\n\n            return resultList;\n        }\n\n\n        private static String getDataHostNameFromNode(String dataNode) {\n            return MycatServer.getInstance().getConfig().getDataNodes().get(dataNode).getDbPool().getHostName();\n        }\n\n        //将所有有相同的来源的dataNode放置到一个任务当中。\n        private static Map<String, List<MigrateTask>> mergerTaskForDataHost(List<MigrateTask> migrateTaskList) {\n            Map<String, List<MigrateTask>> taskMap = new HashMap<>();\n            for (MigrateTask migrateTask : migrateTaskList) {\n                String dataHost = getDataHostNameFromNode(migrateTask.getFrom());\n                if (taskMap.containsKey(dataHost)) {\n                    taskMap.get(dataHost).add(migrateTask);\n                } else {\n                    taskMap.put(dataHost, Lists.newArrayList(migrateTask));\n                }\n            }\n\n            return taskMap;\n        }\n\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/MigrateUtils.java",
    "content": "package io.mycat.migrate;\n\nimport com.alibaba.druid.util.JdbcUtils;\nimport com.alibaba.fastjson.JSON;\nimport com.google.common.base.Joiner;\nimport com.google.common.base.Splitter;\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.config.model.DBHostConfig;\nimport io.mycat.util.ZKUtils;\n\nimport java.io.IOException;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static io.mycat.route.function.PartitionByCRC32PreSlot.Range;\n\n/**\n * Created by magicdoom on 2016/9/16.\n */\npublic class MigrateUtils {\n\n    /**\n     * 扩容计算，以每一个源节点到一个目标节点为一个任务\n     *\n     * @param table\n     * @param integerListMap 会进行修改，所以传入前请自己clone一份\n     * @param oldDataNodes\n     * @param newDataNodes\n     * @param slotsTotalNum\n     * @return\n     */\n    public static Map<String, List<MigrateTask>> balanceExpand(String table, Map<Integer, List<Range>> integerListMap, List<String> oldDataNodes,\n                                                               List<String> newDataNodes, int slotsTotalNum) {\n\n        int newNodeSize = oldDataNodes.size() + newDataNodes.size();\n        int newSlotPerNode = slotsTotalNum / newNodeSize;\n        Map<String, List<MigrateTask>> newNodeTask = new HashMap<>();\n        int gb = slotsTotalNum - newSlotPerNode * (newNodeSize);\n        for (int i = 0; i < integerListMap.size(); i++) {\n\n            List<Range> rangeList = integerListMap.get(i);\n            int needMoveNum = getCurTotalSize(rangeList) - newSlotPerNode;\n            List<Range> allMoveList = getPartAndRemove(rangeList, needMoveNum);\n            for (int i1 = 0; i1 < newDataNodes.size(); i1++) {\n                String newDataNode = newDataNodes.get(i1);\n                if (allMoveList.size() == 0)\n                    break;\n                List<MigrateTask> curRangeList = newNodeTask.get(newDataNode);\n                if (curRangeList == null)\n                    curRangeList = new ArrayList<>();\n                int hasSlots = getCurTotalSizeForTask(curRangeList);\n                int needMove = (i1 == 0) ? newSlotPerNode - hasSlots + gb : newSlotPerNode - hasSlots;\n                if (needMove > 0) {\n                    List<Range> moveList = getPartAndRemove(allMoveList, needMove);\n                    MigrateTask task = new MigrateTask();\n                    if (i >= oldDataNodes.size()) {\n                        throw new RuntimeException(String.format(\"crc32slot_%s.properties does not match schema table dataNode.\", table.toUpperCase()));\n                    }\n                    task.setFrom(oldDataNodes.get(i));\n                    task.setTo(newDataNode);\n                    task.setTable(table);\n                    task.setSlots(moveList);\n                    curRangeList.add(task);\n                    newNodeTask.put(newDataNode, curRangeList);\n                }\n            }\n\n            if (allMoveList.size() > 0) {\n                throw new RuntimeException(\"some slot has not moved to\");\n            }\n        }\n        return newNodeTask;\n    }\n\n\n    private static List<Range> getPartAndRemove(List<Range> rangeList, int size) {\n        List<Range> result = new ArrayList<>();\n\n        for (int i = 0; i < rangeList.size(); i++) {\n\n            Range range = rangeList.get(i);\n            if (range == null)\n                continue;\n            if (range.size == size) {\n                result.add(new Range(range.start, range.end));\n                rangeList.set(i, null);\n                break;\n            } else if (range.size < size) {\n                result.add(new Range(range.start, range.end));\n                size = size - range.size;\n                rangeList.set(i, null);\n            } else if (range.size > size) {\n                result.add(new Range(range.start, range.start + size - 1));\n                rangeList.set(i, new Range(range.start + size, range.end));\n                break;\n            }\n\n        }\n\n        for (int i = rangeList.size() - 1; i >= 0; i--) {\n            Range range = rangeList.get(i);\n            if (range == null)\n                rangeList.remove(i);\n        }\n        return result;\n    }\n\n    private static int getCurTotalSizeForTask(List<MigrateTask> rangeList) {\n        int size = 0;\n        for (MigrateTask task : rangeList) {\n            size = size + getCurTotalSize(task.getSlots());\n        }\n        return size;\n    }\n\n\n    public static List<Range> removeAndGetRemain(List<Range> oriRangeList, List<Range> rangeList) {\n        for (Range range : rangeList) {\n            oriRangeList = removeAndGetRemain(oriRangeList, range);\n        }\n        return oriRangeList;\n    }\n\n    private static List<Range> removeAndGetRemain(List<Range> oriRangeList, Range newRange) {\n        List<Range> result = new ArrayList<>();\n        for (Range range : oriRangeList) {\n            result.addAll(removeAndGetRemain(range, newRange));\n        }\n        return result;\n    }\n\n    private static List<Range> removeAndGetRemain(Range oriRange, Range newRange) {\n\n        List<Range> result = new ArrayList<>();\n        if (newRange.start > oriRange.end || newRange.end < oriRange.start) {\n            result.add(oriRange);\n        } else if (newRange.start <= oriRange.start && newRange.end >= oriRange.end) {\n            return result;\n        } else if (newRange.start > oriRange.start && newRange.end < oriRange.end) {\n            result.add(new Range(oriRange.start, newRange.start - 1));\n            result.add(new Range(newRange.end + 1, oriRange.end));\n        } else if (newRange.start <= oriRange.start && newRange.end < oriRange.end) {\n            result.add(new Range(newRange.end + 1, oriRange.end));\n        } else if (newRange.start > oriRange.start && newRange.end >= oriRange.end) {\n            result.add(new Range(oriRange.start, newRange.start - 1));\n        }\n\n\n        return result;\n    }\n\n    public static String convertRangeListToString(List<Range> rangeList) {\n        List<String> rangeStringList = new ArrayList<>();\n        for (Range range : rangeList) {\n            if (range.start == range.end) {\n                rangeStringList.add(String.valueOf(range.start));\n            } else {\n                rangeStringList.add(range.start + \"-\" + range.end);\n            }\n        }\n        return Joiner.on(',').join(rangeStringList);\n    }\n\n    public static List<Range> convertRangeStringToList(String rangeStr) {\n        List<String> ranges = Splitter.on(\",\").omitEmptyStrings().trimResults().splitToList(rangeStr);\n        List<Range> rangeList = new ArrayList<>();\n        for (String range : ranges) {\n            List<String> vv = Splitter.on(\"-\").omitEmptyStrings().trimResults().splitToList(range);\n            if (vv.size() == 2) {\n                Range ran = new Range(Integer.parseInt(vv.get(0)), Integer.parseInt(vv.get(1)));\n                rangeList.add(ran);\n\n            } else if (vv.size() == 1) {\n                Range ran = new Range(Integer.parseInt(vv.get(0)), Integer.parseInt(vv.get(0)));\n                rangeList.add(ran);\n\n            } else {\n                throw new RuntimeException(\"load crc32slot datafile error:dn=value=\" + range);\n            }\n        }\n        return rangeList;\n    }\n\n    public static int getCurTotalSize(List<Range> rangeList) {\n        int size = 0;\n        for (Range range : rangeList) {\n            size = size + range.size;\n        }\n        return size;\n    }\n\n    public static String getDatabaseFromDataNode(String dn) {\n        return MycatServer.getInstance().getConfig().getDataNodes().get(dn).getDatabase();\n    }\n\n    public static String getDataHostFromDataNode(String dn) {\n        return MycatServer.getInstance().getConfig().getDataNodes().get(dn).getDbPool().getHostName();\n    }\n\n    public static List<Range> convertAllTask(List<MigrateTask> allTasks) {\n        List<Range> resutlList = new ArrayList<>();\n        for (MigrateTask allTask : allTasks) {\n            resutlList.addAll(allTask.getSlots());\n        }\n        return resutlList;\n    }\n\n    public static List<MigrateTask> queryAllTask(String basePath, List<String> dataHost) throws Exception {\n        List<MigrateTask> resutlList = new ArrayList<>();\n        for (String dataHostName : dataHost) {\n            if (\"_prepare\".equals(dataHostName) || \"_commit\".equals(dataHostName) || \"_clean\".equals(dataHostName))\n                continue;\n            resutlList.addAll(JSON\n                    .parseArray(new String(ZKUtils.getConnection().getData().forPath(basePath + \"/\" + dataHostName), \"UTF-8\"), MigrateTask.class));\n        }\n        return resutlList;\n    }\n\n    public static String makeCountSql(MigrateTask task) {\n        StringBuilder sb = new StringBuilder();\n        sb.append(\"select count(*) as count from \");\n        sb.append(task.getTable()).append(\" where \");\n        List<Range> slots = task.getSlots();\n        for (int i = 0; i < slots.size(); i++) {\n            Range range = slots.get(i);\n            if (i != 0)\n                sb.append(\" or \");\n            if (range.start == range.end) {\n                sb.append(\" _slot=\").append(range.start);\n            } else {\n                sb.append(\" (_slot>=\").append(range.start);\n                sb.append(\" and _slot<=\").append(range.end).append(\")\");\n            }\n        }\n        return sb.toString();\n    }\n\n    public static void execulteSql(String sql, String toDn) throws SQLException, IOException {\n        PhysicalDBNode dbNode = MycatServer.getInstance().getConfig().getDataNodes().get(toDn);\n        PhysicalDBPool dbPool = dbNode.getDbPool();\n        PhysicalDatasource datasource = dbPool.getSources()[dbPool.getActivedIndex()];\n        DBHostConfig config = datasource.getConfig();\n        Connection con = null;\n        try {\n            con = DriverManager\n                    .getConnection(\"jdbc:mysql://\" + config.getUrl() + \"/\" + dbNode.getDatabase(), config.getUser(), config.getPassword());\n\n            JdbcUtils.execute(con, sql, new ArrayList<>());\n\n        } finally {\n            JdbcUtils.close(con);\n        }\n\n    }\n\n    public static long execulteCount(String sql, String toDn) throws SQLException, IOException {\n        PhysicalDBNode dbNode = MycatServer.getInstance().getConfig().getDataNodes().get(toDn);\n        PhysicalDBPool dbPool = dbNode.getDbPool();\n        PhysicalDatasource datasource = dbPool.getSources()[dbPool.getActivedIndex()];\n        DBHostConfig config = datasource.getConfig();\n        Connection con = null;\n        try {\n            con = DriverManager.getConnection(\"jdbc:mysql://\" + config.getUrl() + \"/\" + dbNode.getDatabase(), config.getUser(), config.getPassword());\n\n            List<Map<String, Object>> result = JdbcUtils.executeQuery(con, sql, new ArrayList<>());\n            if (result.size() == 1) {\n                return (long) result.get(0).get(\"count\");\n            }\n        } finally {\n            JdbcUtils.close(con);\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/SqlExecuteListener.java",
    "content": "package io.mycat.migrate;\n\nimport com.alibaba.fastjson.JSON;\nimport io.mycat.sqlengine.SQLJob;\nimport io.mycat.sqlengine.SQLQueryResult;\nimport io.mycat.sqlengine.SQLQueryResultListener;\nimport io.mycat.util.ZKUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Map;\nimport java.util.concurrent.Semaphore;\n\n/**\n * Created by nange on 2016/12/13.\n */\npublic class SqlExecuteListener implements SQLQueryResultListener<SQLQueryResult<Map<String, String>>> {\n    private static final Logger LOGGER = LoggerFactory.getLogger(SqlExecuteListener.class);\n    private MigrateTask task;\n    private String sql;\n    private BinlogStream binlogStream;\n    private Semaphore semaphore;\n    private volatile SQLJob sqlJob;\n\n    public SQLJob getSqlJob() {\n        return sqlJob;\n    }\n\n    public void setSqlJob(SQLJob sqlJob) {\n        this.sqlJob = sqlJob;\n    }\n\n    public SqlExecuteListener(MigrateTask task, String sql, BinlogStream binlogStream, Semaphore semaphore) {\n        this.task = task;\n        this.sql = sql;\n        this.binlogStream = binlogStream;\n        this.semaphore = semaphore;\n    }\n\n    @Override\n    public void onResult(SQLQueryResult<Map<String, String>> result) {\n        try {\n            if (!result.isSuccess()) {\n                try {\n                    task.setHaserror(true);\n                    pushMsgToZK(task.getZkpath(), task.getFrom() + \"-\" + task.getTo(), 2, \"sql:\" + sql + \";\" + result.getErrMsg());\n                    close(\"sucess\");\n                    binlogStream.disconnect();\n                } catch (Exception e) {\n                    LOGGER.error(\"error:\", e);\n                    close(e.getMessage());\n                }\n            } else {\n                close(\"sucess\");\n            }\n\n            task.setHasExecute(false);\n        } finally {\n            semaphore.release();\n        }\n    }\n\n\n    private void pushMsgToZK(String rootZkPath, String child, int status, String msg) throws Exception {\n        String path = rootZkPath + \"/\" + child;\n        TaskStatus taskStatus = new TaskStatus();\n        taskStatus.setMsg(msg);\n        taskStatus.setStatus(status);\n        task.setStatus(status);\n\n        if (ZKUtils.getConnection().checkExists().forPath(path) == null) {\n            ZKUtils.getConnection().create().forPath(path, JSON.toJSONBytes(taskStatus));\n        } else {\n            ZKUtils.getConnection().setData().forPath(path, JSON.toJSONBytes(taskStatus));\n        }\n    }\n\n    public void close(String msg) {\n        SQLJob curJob = sqlJob;\n        if (curJob != null) {\n            curJob.teminate(msg);\n            sqlJob = null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/SwitchCleanListener.java",
    "content": "package io.mycat.migrate;\n\nimport com.alibaba.fastjson.JSON;\nimport com.google.common.base.Splitter;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.loader.zkprocess.zookeeper.ClusterInfo;\nimport io.mycat.route.RouteCheckRule;\nimport io.mycat.util.ZKUtils;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;\nimport org.apache.curator.framework.recipes.locks.InterProcessMutex;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n/**      清理本地的阻止写的规则      slaveID relese      create table\n * Ceated by magicdoom on 2016/12/19.\n */\npublic class SwitchCleanListener implements PathChildrenCacheListener {\n    private static final Logger LOGGER = LoggerFactory.getLogger(SwitchCleanListener.class);\n    @Override public void childEvent(CuratorFramework curatorFramework,\n            PathChildrenCacheEvent event) throws Exception {\n        switch (event.getType()) {\n            case CHILD_ADDED:\n                 checkSwitch(event);\n                break;\n            default:\n                break;\n        }\n    }\n    private void checkSwitch(PathChildrenCacheEvent event)    {\n        InterProcessMutex taskLock =null;\n        try {\n            String path=event.getData().getPath();\n            String taskPath=path.substring(0,path.lastIndexOf(\"/_clean/\"))  ;\n            String taskID=taskPath.substring(taskPath.lastIndexOf('/')+1,taskPath.length());\n            String lockPath=     ZKUtils.getZKBasePath()+\"lock/\"+taskID+\".lock\";\n            List<String> sucessDataHost= ZKUtils.getConnection().getChildren().forPath(path.substring(0,path.lastIndexOf('/')));\n            TaskNode pTaskNode= JSON.parseObject(ZKUtils.getConnection().getData().forPath(taskPath),TaskNode.class);\n\n            String custerName = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTERID);\n            ClusterInfo clusterInfo= JSON.parseObject(ZKUtils.getConnection().getData().forPath(\"/mycat/\"+custerName) , ClusterInfo.class);\n            List<String> clusterNodeList= Splitter.on(',').omitEmptyStrings().splitToList(clusterInfo.getClusterNodes());\n            if(sucessDataHost.size()==clusterNodeList.size()) {\n\n                RouteCheckRule.migrateRuleMap.remove(pTaskNode.getSchema().toUpperCase());\n\n                List<String> needToCloseWatch=new ArrayList<>();\n                List<String> dataHosts=  ZKUtils.getConnection().getChildren().forPath(taskPath);\n                for (String dataHostName : dataHosts) {\n                    if (\"_prepare\".equals(dataHostName) || \"_commit\".equals(dataHostName) || \"_clean\".equals(dataHostName))\n                    {\n                       needToCloseWatch.add( taskPath+\"/\"+dataHostName );\n                    }\n                }\n                ZKUtils.closeWatch(needToCloseWatch);\n\n                taskLock=\t new InterProcessMutex(ZKUtils.getConnection(), lockPath);\n                taskLock.acquire(20, TimeUnit.SECONDS);\n                    TaskNode taskNode= JSON.parseObject(ZKUtils.getConnection().getData().forPath(taskPath),TaskNode.class);\n                    if(taskNode.getStatus()==3){\n                        taskNode.setStatus(5);  //clean sucess\n                        //释放slaveIDs\n                        for (String dataHostName : dataHosts) {\n                            if(\"_prepare\".equals(dataHostName)||\"_commit\".equals(dataHostName)||\"_clean\".equals(dataHostName))\n                                continue;\n                            List<MigrateTask> migrateTaskList= JSON\n                                    .parseArray(new String(ZKUtils.getConnection().getData().forPath(taskPath+\"/\"+dataHostName),\"UTF-8\") ,MigrateTask.class);\n                          int slaveId=  migrateTaskList.get(0).getSlaveId();\n                            String slavePath=ZKUtils.getZKBasePath()+\"slaveIDs/\"+dataHostName+\"/\"+slaveId;\n                            if( ZKUtils.getConnection().checkExists().forPath(slavePath)!=null) {\n                                ZKUtils.getConnection().delete().forPath(slavePath);\n                            }\n                        }\n\n                        ZKUtils.getConnection().setData().forPath(taskPath,JSON.toJSONBytes(taskNode))  ;\n                        LOGGER.info(\"task end\",new Date());\n                    }\n\n            }\n\n        } catch (Exception e) {\n            LOGGER.error(\"migrate 中 clean 阶段异常\");\n            LOGGER.error(\"error:\",e);\n        }\n        finally {\n            if(taskLock!=null){\n                try {\n                    taskLock.release();\n                } catch (Exception ignored) {\n\n                }\n            }\n        }\n    }\n\n\n\n\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/SwitchCommitListener.java",
    "content": "package io.mycat.migrate;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\nimport com.google.common.base.Joiner;\nimport com.google.common.base.Splitter;\nimport io.mycat.MycatServer;\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.loader.zkprocess.entity.Rules;\nimport io.mycat.config.loader.zkprocess.entity.Schemas;\nimport io.mycat.config.loader.zkprocess.entity.rule.function.Function;\nimport io.mycat.config.loader.zkprocess.entity.rule.tablerule.TableRule;\nimport io.mycat.config.loader.zkprocess.entity.schema.datahost.DataHost;\nimport io.mycat.config.loader.zkprocess.entity.schema.datanode.DataNode;\nimport io.mycat.config.loader.zkprocess.entity.schema.schema.Schema;\nimport io.mycat.config.loader.zkprocess.entity.schema.schema.Table;\nimport io.mycat.config.loader.zkprocess.parse.XmlProcessBase;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.rule.json.FunctionJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.rule.json.TableRuleJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.rule.xml.RuleParseXmlImpl;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.schema.json.DataHostJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.schema.json.DataNodeJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.schema.json.SchemaJsonParse;\nimport io.mycat.config.loader.zkprocess.parse.entryparse.schema.xml.SchemasParseXmlImpl;\nimport io.mycat.config.loader.zkprocess.zookeeper.ClusterInfo;\nimport io.mycat.config.loader.zkprocess.zookeeper.DataInf;\nimport io.mycat.config.loader.zkprocess.zookeeper.process.ZkDataImpl;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.config.model.rule.RuleConfig;\nimport io.mycat.manager.response.ReloadConfig;\nimport io.mycat.route.function.PartitionByCRC32PreSlot.Range;\nimport io.mycat.route.function.TableRuleAware;\nimport io.mycat.util.ZKUtils;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.api.transaction.CuratorTransactionFinal;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;\nimport org.apache.curator.framework.recipes.locks.InterProcessMutex;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.concurrent.TimeUnit;\n\n\n/**\n * Created by magicdoom on 2016/12/19.\n */\npublic class SwitchCommitListener implements PathChildrenCacheListener {\n    private static final Logger LOGGER = LoggerFactory.getLogger(SwitchCommitListener.class);\n\n    @Override\n    public void childEvent(CuratorFramework curatorFramework,\n                           PathChildrenCacheEvent event) throws Exception {\n        switch (event.getType()) {\n            case CHILD_ADDED:\n                checkCommit(event);\n                break;\n            default:\n                break;\n        }\n    }\n\n    private void checkCommit(PathChildrenCacheEvent event) {\n        InterProcessMutex taskLock = null;\n        try {\n\n            String path = event.getData().getPath();\n            String taskPath = path.substring(0, path.lastIndexOf(\"/_commit/\"));\n            String taskID = taskPath.substring(taskPath.lastIndexOf('/') + 1, taskPath.length());\n            String lockPath = ZKUtils.getZKBasePath() + \"lock/\" + taskID + \".lock\";\n            List<String> sucessDataHost = ZKUtils.getConnection().getChildren().forPath(path.substring(0, path.lastIndexOf('/')));\n            String custerName = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTERID);\n            ClusterInfo clusterInfo = JSON.parseObject(ZKUtils.getConnection().getData().forPath(\"/mycat/\" + custerName), ClusterInfo.class);\n            List<String> clusterNodeList = Splitter.on(',').omitEmptyStrings().splitToList(clusterInfo.getClusterNodes());\n            //等待所有的dataHost都导出数据完毕。 dataHost的数量== booster的数量\n            //判断条件需要进行修改  todo\n            if (sucessDataHost.size() == clusterNodeList.size()) {\n\n                List<String> taskDataHost = ZKUtils.getConnection().getChildren().forPath(taskPath);\n                List<MigrateTask> allTaskList = MigrateUtils.queryAllTask(taskPath, taskDataHost);\n                taskLock = new InterProcessMutex(ZKUtils.getConnection(), lockPath);\n                taskLock.acquire(120, TimeUnit.SECONDS);\n                TaskNode taskNode = JSON.parseObject(ZKUtils.getConnection().getData().forPath(taskPath), TaskNode.class);\n                if (taskNode.getStatus() == 2) {\n                    taskNode.setStatus(3);\n                    //开始切换 且个节点已经禁止写入并且无原有写入在执行\n                    try {\n                        CuratorTransactionFinal transactionFinal = null;\n                        check(taskID, allTaskList);\n                        SchemaConfig schemaConfig = MycatServer.getInstance().getConfig().getSchemas().get(taskNode.getSchema());\n                        TableConfig tableConfig = schemaConfig.getTables().get(taskNode.getTable().toUpperCase());\n                        List<String> newDataNodes = Splitter.on(\",\").omitEmptyStrings().trimResults().splitToList(taskNode.getAdd());\n                        List<String> allNewDataNodes = tableConfig.getDataNodes();\n                        allNewDataNodes.addAll(newDataNodes);\n                        //先修改rule config\n                        InterProcessMutex ruleLock = new InterProcessMutex(ZKUtils.getConnection(), ZKUtils.getZKBasePath() + \"lock/rules.lock\");\n                        ;\n                        try {\n                            ruleLock.acquire(30, TimeUnit.SECONDS);\n                            //transactionFinal = modifyZkRules(transactionFinal, tableConfig.getRule().getFunctionName(), newDataNodes);\n                            transactionFinal = modifyTableConfigRules(transactionFinal, taskNode.getSchema(), taskNode.getTable(), newDataNodes);\n                        } finally {\n                            ruleLock.release();\n                        }\n\n                        transactionFinal = modifyRuleData(transactionFinal, allTaskList, tableConfig, allNewDataNodes);\n                        transactionFinal.setData().forPath(taskPath, JSON.toJSONBytes(taskNode));\n\n                        clean(taskID, allTaskList);\n                        transactionFinal.commit();\n\n                        forceTableRuleToLocal(taskPath, taskNode);\n                        pushACKToClean(taskPath);\n                    } catch (Exception e) {\n                        taskNode.addException(e.getLocalizedMessage());\n                        //异常to  Zk\n                        ZKUtils.getConnection().setData().forPath(taskPath, JSON.toJSONBytes(taskNode));\n                        LOGGER.error(\"error:\", e);\n                        return;\n                    }\n                    //todo   清理规则     顺利拉下ruledata保证一定更新到本地\n                } else if (taskNode.getStatus() == 3) {\n                    forceTableRuleToLocal(taskPath, taskNode);\n                    pushACKToClean(taskPath);\n                }\n\n            }\n\n\n        } catch (Exception e) {\n            LOGGER.error(\"migrate 中 commit 阶段异常\");\n            LOGGER.error(\"error:\", e);\n        } finally {\n            if (taskLock != null) {\n                try {\n                    taskLock.release();\n                } catch (Exception ignored) {\n\n                }\n            }\n        }\n    }\n\n    private void forceTableRuleToLocal(String path, TaskNode taskNode) throws Exception {\n        Path localPath = Paths.get(this.getClass()\n                .getClassLoader()\n                .getResource(ZookeeperPath.ZK_LOCAL_WRITE_PATH.getKey()).toURI());\n        // 获得公共的xml转换器对象\n        XmlProcessBase xmlProcess = new XmlProcessBase();\n\n        try {\n            forceTableToLocal(localPath, xmlProcess);\n            forceRulesToLocal(localPath, xmlProcess);\n            //保证先有table再有rule\n            forceRuleDataToLocal(taskNode);\n            ReloadConfig.reload();\n            LOGGER.error(\"migrate 中 reload 配置成功\");\n        } catch (Exception e) {\n            taskNode.addException(e.getLocalizedMessage());\n            //异常to  Zk\n            ZKUtils.getConnection().setData().forPath(path, JSON.toJSONBytes(taskNode));\n            LOGGER.error(\"migrate 中 强制更新本地文件失败\");\n            LOGGER.error(\"error:\", e);\n        }\n    }\n\n    private void forceRuleDataToLocal(TaskNode taskNode) throws Exception {\n        SchemaConfig schemaConfig = MycatServer.getInstance().getConfig().getSchemas().get(taskNode.getSchema());\n        TableConfig tableConfig = schemaConfig.getTables().get(taskNode.getTable().toUpperCase());\n        RuleConfig ruleConfig = tableConfig.getRule();\n        String ruleName = ((TableRuleAware) ruleConfig.getRuleAlgorithm()).getRuleName() + \".properties\";\n        String rulePath = ZKUtils.getZKBasePath() + \"ruledata/\" + ruleName;\n        CuratorFramework zk = ZKUtils.getConnection();\n        byte[] ruleData = zk.getData().forPath(rulePath);\n        LOGGER.info(\"-----------------------------------从zookeeper中拉取的新的ruleData的信息--------------------------------------------------\\n\");\n        LOGGER.info(new String(ruleData));\n        Path file = Paths.get(SystemConfig.getHomePath(), \"conf\", \"ruledata\");\n        Files.write(file.resolve(ruleName), ruleData);\n    }\n\n    private static void forceTableToLocal(Path localPath, XmlProcessBase xmlProcess) throws Exception {\n\n        // 获得当前集群的名称\n        String schemaPath = ZKUtils.getZKBasePath();\n        schemaPath = schemaPath + ZookeeperPath.FOW_ZK_PATH_SCHEMA.getKey() + ZookeeperPath.ZK_SEPARATOR.getKey();\n        // 生成xml与类的转换信息\n        SchemasParseXmlImpl schemasParseXml = new SchemasParseXmlImpl(xmlProcess);\n        Schemas schema = new Schemas();\n        String str = \"\";\n        // 得到schema对象的目录信息\n        str = new String(ZKUtils.getConnection().getData().forPath(schemaPath + ZookeeperPath.FLOW_ZK_PATH_SCHEMA_SCHEMA.getKey()));\n        LOGGER.info(\"-----------------------------------从zookeeper中拉取的新的schema的信息--------------------------------------------------\");\n        LOGGER.info(str);\n        SchemaJsonParse schemaJsonParse = new SchemaJsonParse();\n        List<Schema> schemaList = schemaJsonParse.parseJsonToBean(str);\n        schema.setSchema(schemaList);\n        // 得到dataNode的信息\n        str = new String(ZKUtils.getConnection().getData().forPath(schemaPath + ZookeeperPath.FLOW_ZK_PATH_SCHEMA_DATANODE.getKey()));\n        LOGGER.info(\"-----------------------------------从zookeeper中拉取的新的dataNode的信息--------------------------------------------------\");\n        LOGGER.info(str);\n        DataNodeJsonParse dataNodeJsonParse = new DataNodeJsonParse();\n        List<DataNode> dataNodeList = dataNodeJsonParse.parseJsonToBean(str);\n        schema.setDataNode(dataNodeList);\n        // 得到dataHost的信息\n        str = new String(ZKUtils.getConnection().getData().forPath(schemaPath + ZookeeperPath.FLOW_ZK_PATH_SCHEMA_DATAHOST.getKey()));\n        LOGGER.info(\"-----------------------------------从zookeeper中拉取的新的dataHost的信息--------------------------------------------------\");\n        LOGGER.info(str);\n        DataHostJsonParse dataHostJsonParse = new DataHostJsonParse();\n        List<DataHost> dataHostList = dataHostJsonParse.parseJsonToBean(str);\n        schema.setDataHost(dataHostList);\n\n        xmlProcess.addParseClass(DataNode.class);\n        xmlProcess.addParseClass(DataHost.class);\n        xmlProcess.addParseClass(Schema.class);\n        xmlProcess.addParseClass(Schemas.class);\n        xmlProcess.initJaxbClass();\n\n        schemasParseXml.parseToXmlWrite(schema, localPath.resolve(\"schema.xml\").toString(), \"schema\");\n    }\n\n    private static void forceRulesToLocal(Path localPath, XmlProcessBase xmlProcess) throws Exception {\n        Rules rules = new Rules();\n        // tablerule信息\n        String value = new String(ZKUtils.getConnection().getData().forPath(ZKUtils.getZKBasePath() + \"rules/tableRule\"), \"UTF-8\");\n        LOGGER.info(\"-----------------------------------从zookeeper中拉取的新的tablerule的信息--------------------------------------------------\");\n        LOGGER.info(value);\n        DataInf RulesZkData = new ZkDataImpl(\"tableRule\", value);\n        TableRuleJsonParse tableRuleJsonParse = new TableRuleJsonParse();\n\n        List<TableRule> tableRuleData = tableRuleJsonParse.parseJsonToBean(RulesZkData.getDataValue());\n        rules.setTableRule(tableRuleData);\n        // 得到function信息\n        String fucValue = new String(ZKUtils.getConnection().getData().forPath(ZKUtils.getZKBasePath() + \"rules/function\"), \"UTF-8\");\n        LOGGER.info(\"-----------------------------------从zookeeper中拉取的新的function的信息--------------------------------------------------\");\n        LOGGER.info(fucValue);\n        DataInf functionZkData = new ZkDataImpl(\"function\", fucValue);\n        FunctionJsonParse functionJsonParse = new FunctionJsonParse();\n        List<Function> functionList = functionJsonParse.parseJsonToBean(functionZkData.getDataValue());\n        rules.setFunction(functionList);\n\n        xmlProcess.addParseClass(Table.class);\n        xmlProcess.addParseClass(Function.class);\n\n        RuleParseXmlImpl ruleParseXml = new RuleParseXmlImpl(xmlProcess);\n        xmlProcess.initJaxbClass();\n        ruleParseXml.parseToXmlWrite(rules, localPath.resolve(\"rule.xml\").toString(), \"rule\");\n    }\n\n    private void pushACKToClean(String taskPath) throws Exception {\n        String myID = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID);\n        String path = taskPath + \"/_clean/\" + myID;\n        if (ZKUtils.getConnection().checkExists().forPath(path) == null) {\n            ZKUtils.getConnection().create().creatingParentsIfNeeded().forPath(path);\n        }\n    }\n\n\n    private void clean(String taskID, List<MigrateTask> allTaskList) throws IOException, SQLException {\n\n        //clean\n        for (MigrateTask migrateTask : allTaskList) {\n            String sql = makeCleanSql(migrateTask);\n            MigrateUtils.execulteSql(sql, migrateTask.getFrom());\n        }\n    }\n\n    private void check(String taskID, List<MigrateTask> allTaskList) throws SQLException, IOException {\n        for (MigrateTask migrateTask : allTaskList) {\n            String sql = MigrateUtils.makeCountSql(migrateTask);\n            long oldCount = MigrateUtils.execulteCount(sql, migrateTask.getFrom());\n            long newCount = MigrateUtils.execulteCount(sql, migrateTask.getTo());\n            if (oldCount != newCount) {\n                throw new RuntimeException(\"migrate task (\" + taskID + \") check fail,because  fromNode:\"\n                        + migrateTask.getFrom() + \"(\" + oldCount + \")\" + \" and  toNode:\" + migrateTask.getTo() + \"(\" + newCount + \") and sql is \" + sql);\n            }\n        }\n    }\n\n    private String makeCleanSql(MigrateTask task) {\n        StringBuilder sb = new StringBuilder();\n        sb.append(\"delete  from \");\n        sb.append(task.getTable()).append(\" where \");\n        List<Range> slots = task.getSlots();\n        for (int i = 0; i < slots.size(); i++) {\n            Range range = slots.get(i);\n            if (i != 0)\n                sb.append(\" or \");\n            if (range.start == range.end) {\n                sb.append(\" _slot=\").append(range.start);\n            } else {\n                sb.append(\"( _slot>=\").append(range.start);\n                sb.append(\" and _slot<=\").append(range.end).append(\")\");\n            }\n        }\n        return sb.toString();\n    }\n\n\n    private CuratorTransactionFinal modifyRuleData(CuratorTransactionFinal transactionFinal, List<MigrateTask> allTaskList, TableConfig tableConfig, List<String> allNewDataNodes)\n            throws Exception {\n\n        InterProcessMutex ruleDataLock = null;\n        try {\n            String path = ZKUtils.getZKBasePath() + \"lock/ruledata.lock\";\n            ruleDataLock = new InterProcessMutex(ZKUtils.getConnection(), path);\n            ruleDataLock.acquire(30, TimeUnit.SECONDS);\n            RuleConfig ruleConfig = tableConfig.getRule();\n            String ruleName = ((TableRuleAware) ruleConfig.getRuleAlgorithm()).getRuleName() + \".properties\";\n            String rulePath = ZKUtils.getZKBasePath() + \"ruledata/\" + ruleName;\n            CuratorFramework zk = ZKUtils.getConnection();\n            byte[] ruleData = zk.getData().forPath(rulePath);\n            Properties prop = new Properties();\n            prop.load(new ByteArrayInputStream(ruleData));\n            for (MigrateTask migrateTask : allTaskList) {\n                modifyRuleData(prop, migrateTask, allNewDataNodes);\n            }\n            ByteArrayOutputStream out = new ByteArrayOutputStream();\n            prop.store(out, \"WARNING   !!!Please do not modify or delete this file!!!\");\n            if (transactionFinal == null) {\n                transactionFinal = ZKUtils.getConnection().inTransaction().setData().forPath(rulePath, out.toByteArray()).and();\n            } else {\n                transactionFinal.setData().forPath(rulePath, out.toByteArray());\n            }\n        } finally {\n            try {\n                if (ruleDataLock != null)\n                    ruleDataLock.release();\n            } catch (Exception e) {\n                throw new RuntimeException(e);\n            }\n        }\n        return transactionFinal;\n    }\n\n    private void modifyRuleData(Properties prop, MigrateTask task, List<String> allNewDataNodes) {\n        int fromIndex = -1;\n        int toIndex = -1;\n        List<String> dataNodes = allNewDataNodes;\n        for (int i = 0; i < dataNodes.size(); i++) {\n            String dataNode = dataNodes.get(i);\n            if (dataNode.equalsIgnoreCase(task.getFrom())) {\n                fromIndex = i;\n            } else if (dataNode.equalsIgnoreCase(task.getTo())) {\n                toIndex = i;\n            }\n        }\n        String from = prop.getProperty(String.valueOf(fromIndex));\n        String to = prop.getProperty(String.valueOf(toIndex));\n        String fromRemain = removeRangesRemain(from, task.getSlots());\n        String taskRanges = MigrateUtils.convertRangeListToString(task.getSlots());\n        String newTo = to == null ? taskRanges : to + \",\" + taskRanges;\n        prop.setProperty(String.valueOf(fromIndex), fromRemain);\n        prop.setProperty(String.valueOf(toIndex), newTo);\n    }\n\n    private String removeRangesRemain(String ori, List<Range> rangeList) {\n        List<Range> ranges = MigrateUtils.convertRangeStringToList(ori);\n        List<Range> ramain = MigrateUtils.removeAndGetRemain(ranges, rangeList);\n        return MigrateUtils.convertRangeListToString(ramain);\n    }\n\n\n    private static CuratorTransactionFinal modifyZkRules(CuratorTransactionFinal transactionFinal, String ruleName, List<String> newDataNodes)\n            throws Exception {\n        CuratorFramework client = ZKUtils.getConnection();\n        String rulePath = ZKUtils.getZKBasePath() + \"rules/function\";\n        JSONArray jsonArray = JSON.parseArray(new String(client.getData().forPath(rulePath), \"UTF-8\"));\n        for (Object obj : jsonArray) {\n            JSONObject func = (JSONObject) obj;\n            if (ruleName.equalsIgnoreCase(func.getString(\"name\"))) {\n                JSONArray property = func.getJSONArray(\"property\");\n                for (Object o : property) {\n                    JSONObject count = (JSONObject) o;\n                    if (\"count\".equals(count.getString(\"name\"))) {\n                        Integer xcount = Integer.parseInt(count.getString(\"value\"));\n                        count.put(\"value\", String.valueOf(xcount + newDataNodes.size()));\n\n                        if (transactionFinal == null) {\n                            transactionFinal = ZKUtils.getConnection().inTransaction().setData().forPath(rulePath, JSON.toJSONBytes(jsonArray)).and();\n                        } else {\n                            transactionFinal.setData().forPath(rulePath, JSON.toJSONBytes(jsonArray));\n                        }\n                    }\n                }\n            }\n\n        }\n        return transactionFinal;\n    }\n\n    private static CuratorTransactionFinal modifyTableConfigRules(CuratorTransactionFinal transactionFinal, String schemal, String table, List<String> newDataNodes)\n            throws Exception {\n        CuratorFramework client = ZKUtils.getConnection();\n        String rulePath = ZKUtils.getZKBasePath() + \"schema/schema\";\n        JSONArray jsonArray = JSON.parseArray(new String(client.getData().forPath(rulePath), \"UTF-8\"));\n        for (Object obj : jsonArray) {\n            JSONObject func = (JSONObject) obj;\n            if (schemal.equalsIgnoreCase(func.getString(\"name\"))) {\n\n                JSONArray property = func.getJSONArray(\"table\");\n                for (Object o : property) {\n                    JSONObject tt = (JSONObject) o;\n                    String tableName = tt.getString(\"name\");\n                    String dataNode = tt.getString(\"dataNode\");\n                    if (table.equalsIgnoreCase(tableName)) {\n                        List<String> allDataNodes = new ArrayList<>();\n                        allDataNodes.add(dataNode);\n                        allDataNodes.addAll(newDataNodes);\n                        tt.put(\"dataNode\", Joiner.on(\",\").join(allDataNodes));\n                        if (transactionFinal == null) {\n                            transactionFinal = ZKUtils.getConnection().inTransaction().setData().forPath(rulePath, JSON.toJSONBytes(jsonArray)).and();\n                        } else {\n                            transactionFinal.setData().forPath(rulePath, JSON.toJSONBytes(jsonArray));\n                        }\n                    }\n\n                }\n            }\n        }\n        return transactionFinal;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/SwitchPrepareCheckRunner.java",
    "content": "package io.mycat.migrate;\n\nimport com.alibaba.fastjson.JSON;\nimport com.google.common.collect.Sets;\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.route.RouteCheckRule;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.route.function.PartitionByCRC32PreSlot;\nimport io.mycat.util.ZKUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Created by nange on 2016/12/20.\n */\npublic class SwitchPrepareCheckRunner implements Runnable {\n    private static final Logger LOGGER = LoggerFactory.getLogger(SwitchPrepareListener.class);\n      public  static Set<String> allSwitchRunnerSet= Sets.newConcurrentHashSet();\n\n    private String taskID;\n    private String taskPath;\n    private TaskNode taskNode;\n    private List<PartitionByCRC32PreSlot.Range>     rangeList;\n\n    public SwitchPrepareCheckRunner( String taskID, String taskPath,\n            TaskNode taskNode,List<PartitionByCRC32PreSlot.Range>     rangeList) {\n        this.taskID = taskID;\n        this.taskPath = taskPath;\n        this.taskNode = taskNode;\n        this.rangeList=rangeList;\n    }\n\n    @Override public void run() {\n        if(!allSwitchRunnerSet.contains(taskID)){\n            return;\n        }\n        ScheduledExecutorService scheduledExecutorService= MycatServer.getInstance().getScheduler();\n        ConcurrentMap<String, ConcurrentMap<String, List<PartitionByCRC32PreSlot.Range>>> migrateRuleMap = RouteCheckRule.migrateRuleMap;\n        String schemal = taskNode.getSchema().toUpperCase();\n        if(!migrateRuleMap.containsKey(schemal)||!migrateRuleMap.get(\n                schemal).containsKey(taskNode.getTable().toUpperCase())){\n           scheduledExecutorService.schedule(this,3, TimeUnit.SECONDS);\n            return;\n        }\n       boolean isHasInTransation=false;\n        NIOProcessor[] processors=MycatServer.getInstance().getProcessors();\n        for (NIOProcessor processor : processors) {\n            Collection<BackendConnection> backendConnections= processor.getBackends().values();\n            for (BackendConnection backendConnection : backendConnections) {\n                isHasInTransation=  checkIsInTransation(backendConnection);\n                if(isHasInTransation){\n                    scheduledExecutorService.schedule(this,3, TimeUnit.SECONDS);\n                    return;\n                }\n            }\n        }\n\n        for (BackendConnection backendConnection : NIOProcessor.backends_old) {\n            isHasInTransation=  checkIsInTransation(backendConnection);\n            if(isHasInTransation){\n                scheduledExecutorService.schedule(this,3, TimeUnit.SECONDS);\n                return;\n            }\n        }\n\n       //增加判断binlog完成\n        if(!isHasInTransation){\n            try {\n\n                //先判断后端binlog都完成了才算本任务完成\n               boolean allIncrentmentSucess=true;\n                List<String> dataHosts=  ZKUtils.getConnection().getChildren().forPath(taskPath);\n                for (String dataHostName : dataHosts) {\n                    if(\"_prepare\".equals(dataHostName)||\"_commit\".equals(dataHostName)||\"_clean\".equals(dataHostName))\n                        continue;\n                    List<MigrateTask> migrateTaskList= JSON\n                            .parseArray(new String(ZKUtils.getConnection().getData().forPath(taskPath+\"/\"+dataHostName),\"UTF-8\") ,MigrateTask.class);\n                    for (MigrateTask migrateTask : migrateTaskList) {\n                        String zkPath =taskPath+\"/\"+dataHostName+ \"/\" + migrateTask.getFrom() + \"-\" + migrateTask.getTo();\n                        if (ZKUtils.getConnection().checkExists().forPath(zkPath) != null) {\n                            TaskStatus taskStatus = JSON.parseObject(\n                                    new String(ZKUtils.getConnection().getData().forPath(zkPath), \"UTF-8\"), TaskStatus.class);\n                            if (taskStatus.getStatus() != 3) {\n                                allIncrentmentSucess=false;\n                                break;\n                            }\n                        }else{\n                            allIncrentmentSucess=false;\n                            break;\n                        }\n                    }\n                }\n                if(allIncrentmentSucess) {\n                    //需要关闭binlog，不然后续的清楚老数据会删除数据\n                  BinlogStream stream=         BinlogStreamHoder.binlogStreamMap.get(taskID);\n                    if(stream!=null){\n                        BinlogStreamHoder.binlogStreamMap.remove(taskID);\n                        stream.disconnect();\n                    }\n\n                    String myID = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_MYID);\n                    String path = taskPath + \"/_commit/\" + myID;\n                    if (ZKUtils.getConnection().checkExists().forPath(path) == null) {\n                        ZKUtils.getConnection().create().creatingParentsIfNeeded().forPath(path);\n                    }\n                    allSwitchRunnerSet.remove(taskID);\n                }   else {\n                    scheduledExecutorService.schedule(this,3, TimeUnit.SECONDS);\n                }\n            } catch (Exception e) {\n                try {\n                    LOGGER.error(\"migrate 中 switch prepare 阶段异常\");\n\n                    taskNode.addException(e.getLocalizedMessage());\n                    //异常to  Zk\n                    ZKUtils.getConnection().setData().forPath(taskPath, JSON.toJSONBytes(taskNode));\n                }catch (Exception e1){\n                    LOGGER.error(\"error:\",e);\n                }\n                LOGGER.error(\"error:\",e);\n            }\n        }\n\n    }\n\n\n\n\n    private boolean  checkIsInTransation(BackendConnection backendConnection) {\n        if(!taskNode.getSchema().equalsIgnoreCase(backendConnection.getSchema()))\n            return false;\n\n        Object attach=   backendConnection.getAttachment();\n        if(attach instanceof RouteResultsetNode) {\n            RouteResultsetNode resultsetNode= (RouteResultsetNode) attach;\n            RouteResultset rrs= resultsetNode.getSource();\n            for (String table : rrs.getTables()) {\n                if(table.equalsIgnoreCase(taskNode.getTable())) {\n                    int slot = resultsetNode.getSlot();\n                    if(slot <0&&resultsetNode.isUpdateSql())\n                    {\n                       return true;\n\n                    }  else if(resultsetNode.isUpdateSql())  {\n                        for (PartitionByCRC32PreSlot.Range range : rangeList) {\n                            if(slot>=range.start&&slot<=range.end){\n                                return true;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/SwitchPrepareListener.java",
    "content": "package io.mycat.migrate;\n\nimport com.alibaba.fastjson.JSON;\nimport io.mycat.route.RouteCheckRule;\nimport io.mycat.route.function.PartitionByCRC32PreSlot;\nimport io.mycat.util.ZKUtils;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;\nimport org.apache.curator.framework.recipes.locks.InterProcessMutex;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Date;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Created by magicdoom on 2016/12/19.\n */\npublic class SwitchPrepareListener implements PathChildrenCacheListener {\n    private static final Logger LOGGER = LoggerFactory.getLogger(SwitchPrepareListener.class);\n\n    @Override\n    public void childEvent(CuratorFramework curatorFramework,\n                           PathChildrenCacheEvent event) throws Exception {\n        switch (event.getType()) {\n            case CHILD_ADDED:\n                checkSwitch(event);\n                break;\n            default:\n                break;\n        }\n    }\n\n    private void checkSwitch(PathChildrenCacheEvent event) {\n        InterProcessMutex taskLock = null;\n        try {\n\n            String path = event.getData().getPath();\n            String taskPath = path.substring(0, path.lastIndexOf(\"/_prepare/\"));\n            String taskID = taskPath.substring(taskPath.lastIndexOf('/') + 1, taskPath.length());\n            String lockPath = ZKUtils.getZKBasePath() + \"lock/\" + taskID + \".lock\";\n            List<String> sucessDataHost = ZKUtils.getConnection().getChildren().forPath(path.substring(0, path.lastIndexOf('/')));\n            List<MigrateTask> allTaskList = MigrateUtils.queryAllTask(taskPath, sucessDataHost);\n            TaskNode pTaskNode = JSON.parseObject(ZKUtils.getConnection().getData().forPath(taskPath), TaskNode.class);\n\n            ConcurrentMap<String, List<PartitionByCRC32PreSlot.Range>> tableRuleMap =\n                    RouteCheckRule.migrateRuleMap.containsKey(pTaskNode.getSchema().toUpperCase()) ?\n                            RouteCheckRule.migrateRuleMap.get(pTaskNode.getSchema().toUpperCase()) :\n                            new ConcurrentHashMap();\n            tableRuleMap.put(pTaskNode.getTable().toUpperCase(), MigrateUtils.convertAllTask(allTaskList));\n            RouteCheckRule.migrateRuleMap.put(pTaskNode.getSchema().toUpperCase(), tableRuleMap);\n\n\n            taskLock = new InterProcessMutex(ZKUtils.getConnection(), lockPath);\n            taskLock.acquire(20, TimeUnit.SECONDS);\n\n            List<String> dataHost = ZKUtils.getConnection().getChildren().forPath(taskPath);\n            if (getRealSize(dataHost) == sucessDataHost.size()) {\n                TaskNode taskNode = JSON.parseObject(ZKUtils.getConnection().getData().forPath(taskPath), TaskNode.class);\n                if (taskNode.getStatus() == 1) {\n                    taskNode.setStatus(2);  //prepare switch\n                    LOGGER.info(\"task switch:\", new Date());\n                    ZKUtils.getConnection().setData().forPath(taskPath, JSON.toJSONBytes(taskNode));\n                }\n            }\n        } catch (Exception e) {\n            LOGGER.error(\"error:\", e);\n        } finally {\n            if (taskLock != null) {\n                try {\n                    taskLock.release();\n                } catch (Exception ignored) {\n\n                }\n            }\n        }\n    }\n\n    private int getRealSize(List<String> dataHosts) {\n        int size = dataHosts.size();\n        Set<String> set = new HashSet(dataHosts);\n        if (set.contains(\"_prepare\")) {\n            size = size - 1;\n        }\n        if (set.contains(\"_commit\")) {\n            size = size - 1;\n        }\n        if (set.contains(\"_clean\")) {\n            size = size - 1;\n        }\n        return size;\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/TaskNode.java",
    "content": "package io.mycat.migrate;\n\nimport java.io.Serializable;\n\n/**\n * Created by magicdoom on 2016/9/28.\n */\npublic class TaskNode implements Serializable {\n    private String sql;\n    private int status;    //0=init    1=start    2=prepare switch    3=commit sucess   4=error     5=clean  sucess     6=error process end\n    private String schema;\n    private String table;\n    private String add;\n    private int timeout;\n    private String charset;\n    private boolean forceBinlog = false;\n    private String backupFile;\n    private String exception = \"\";\n\n    public String getSql() {\n        return sql;\n    }\n\n    public String getTable() {\n        return table;\n    }\n\n    public void setTable(String table) {\n        this.table = table;\n    }\n\n    public String getAdd() {\n        return add;\n    }\n\n    public void setAdd(String add) {\n        this.add = add;\n    }\n\n    public void setSql(String sql) {\n        this.sql = sql;\n    }\n\n    public int getStatus() {\n        return status;\n    }\n\n    public void setStatus(int status) {\n        this.status = status;\n    }\n\n    public String getSchema() {\n        return schema;\n    }\n\n    public void setSchema(String schema) {\n        this.schema = schema;\n    }\n\n    public int getTimeout() {\n        return timeout;\n    }\n\n    public void setTimeout(int timeout) {\n        this.timeout = timeout;\n    }\n\n    public String getCharset() {\n        return charset;\n    }\n\n    public void setCharset(String charset) {\n        this.charset = charset;\n    }\n\n    public boolean isForceBinlog() {\n        return forceBinlog;\n    }\n\n    public void setForceBinlog(boolean forceBinlog) {\n        this.forceBinlog = forceBinlog;\n    }\n\n    public String getBackupFile() {\n        return backupFile;\n    }\n\n    public void setBackupFile(String backupFile) {\n        this.backupFile = backupFile;\n    }\n\n    public String getException() {\n        return exception;\n    }\n\n    public void addException(String exception) {\n        this.exception = this.exception+\"\\n\"+exception;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/migrate/TaskStatus.java",
    "content": "package io.mycat.migrate;\n\nimport java.io.Serializable;\n\n/**\n * Created by nange on 2016/12/7.\n */\npublic class TaskStatus implements Serializable {\n    private int status;         //0= dump error     1=dump sucess     2=increnment error     3=increment sucess    4=other error\n    private String msg;\n    private String binlogFile;\n    private long pos;\n    private String lastDate;\n\n    public int getStatus() {\n        return status;\n    }\n\n    public void setStatus(int status) {\n        this.status = status;\n    }\n\n    public String getMsg() {\n        return msg;\n    }\n\n    public void setMsg(String msg) {\n        this.msg = msg;\n    }\n\n    public String getBinlogFile() {\n        return binlogFile;\n    }\n\n    public void setBinlogFile(String binlogFile) {\n        this.binlogFile = binlogFile;\n    }\n\n    public long getPos() {\n        return pos;\n    }\n\n    public void setPos(long pos) {\n        this.pos = pos;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/AIOAcceptor.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.StandardSocketOptions;\nimport java.nio.channels.AsynchronousChannelGroup;\nimport java.nio.channels.AsynchronousServerSocketChannel;\nimport java.nio.channels.AsynchronousSocketChannel;\nimport java.nio.channels.CompletionHandler;\nimport java.nio.channels.NetworkChannel;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.net.factory.FrontendConnectionFactory;\n\n/**\n * @author mycat\n */\npublic final class AIOAcceptor implements SocketAcceptor,\n\t\tCompletionHandler<AsynchronousSocketChannel, Long> {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(AIOAcceptor.class);\n\tprivate static final AcceptIdGenerator ID_GENERATOR = new AcceptIdGenerator();\n\n\tprivate final int port;\n\tprivate final AsynchronousServerSocketChannel serverChannel;\n\tprivate final FrontendConnectionFactory factory;\n\n\tprivate long acceptCount;\n\tprivate final String name;\n\n\tpublic AIOAcceptor(String name, String ip, int port, int backlog,\n\t\t\tFrontendConnectionFactory factory, AsynchronousChannelGroup group)\n\t\t\tthrows IOException {\n\t\tthis.name = name;\n\t\tthis.port = port;\n\t\tthis.factory = factory;\n\t\tserverChannel = AsynchronousServerSocketChannel.open(group);\n\t\t/** 设置TCP属性 */\n\t\tserverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);\n\t\tserverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 1024 * 16 * 2);\n\n\t\tserverChannel.bind(new InetSocketAddress(ip, port), backlog);\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void start() {\n\t\tthis.pendingAccept();\n\t}\n\n\tpublic int getPort() {\n\t\treturn port;\n\t}\n\n\tpublic long getAcceptCount() {\n\t\treturn acceptCount;\n\t}\n\n\tprivate void accept(NetworkChannel channel, Long id) {\n\t\ttry {\n\t\t\tFrontendConnection c = factory.make(channel);\n\t\t\tc.setAccepted(true);\n\t\t\tc.setId(id);\n\t\t\tNIOProcessor processor = MycatServer.getInstance().nextProcessor();\n\t\t\tc.setProcessor(processor);\n\t\t\tc.register();\n\t\t} catch (Exception e) {\n\t\t    LOGGER.error(\"AioAcceptorError\", e);\n\t\t\tcloseChannel(channel);\n\t\t}\n\t}\n\n\tprivate void pendingAccept() {\n\t\tif (serverChannel.isOpen()) {\n\t\t\tserverChannel.accept(ID_GENERATOR.getId(), this);\n\t\t} else {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"MyCAT Server Channel has been closed\");\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void completed(AsynchronousSocketChannel result, Long id) {\n\t\taccept(result, id);\n\t\t// next pending waiting\n\t\tpendingAccept();\n\n\t}\n\n\t@Override\n\tpublic void failed(Throwable exc, Long id) {\n\t\tLOGGER.info(\"acception connect failed:\" + exc);\n\t\t// next pending waiting\n\t\tpendingAccept();\n\n\t}\n\n\tprivate static void closeChannel(NetworkChannel channel) {\n\t\tif (channel == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tchannel.close();\n\t\t} catch (IOException e) {\n\t        LOGGER.error(\"AioAcceptorError\", e);\n\t\t}\n\t}\n\n\t/**\n\t * 前端连接ID生成器\n\t * \n\t * @author mycat\n\t */\n\tprivate static class AcceptIdGenerator {\n\n\t\tprivate static final long MAX_VALUE = 0xffffffffL;\n\n\t\tprivate AtomicLong acceptId = new AtomicLong();\n\t\tprivate final Object lock = new Object();\n\n\t\tprivate long getId() {\n\t\t\tlong newValue = acceptId.getAndIncrement();\n\t\t\tif (newValue >= MAX_VALUE) {\n\t\t\t\tsynchronized (lock) {\n\t\t\t\t\tnewValue = acceptId.getAndIncrement();\n\t\t\t\t\tif (newValue >= MAX_VALUE) {\n\t\t\t\t\t\tacceptId.set(0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn acceptId.getAndDecrement();\n\t\t\t} else {\n\t\t\t\treturn newValue;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/AIOConnector.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\nimport java.nio.channels.CompletionHandler;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * @author mycat\n */\npublic final class AIOConnector implements SocketConnector,\n\t\tCompletionHandler<Void, BackendAIOConnection> {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(AIOConnector.class);\n\tprivate static final ConnectIdGenerator ID_GENERATOR = new ConnectIdGenerator();\n\n\tpublic AIOConnector() {\n\n\t}\n\n\t@Override\n\tpublic void completed(Void result, BackendAIOConnection attachment) {\n\t\tfinishConnect(attachment);\n\t}\n\n\t@Override\n\tpublic void failed(Throwable exc, BackendAIOConnection conn) {\n\t\tconn.onConnectFailed(exc);\n\t}\n\n\tprivate void finishConnect(BackendAIOConnection c) {\n\t\ttry {\n\t\t\tif (c.finishConnect()) {\n\t\t\t\tc.setId(ID_GENERATOR.getId());\n\t\t\t\tNIOProcessor processor = MycatServer.getInstance()\n\t\t\t\t\t\t.nextProcessor();\n\t\t\t\tc.setProcessor(processor);\n\t\t\t\tc.register();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tc.onConnectFailed(e);\n\t\t\tLOGGER.info(\"connect err \" , e);\n\t\t\tc.close(e.toString());\n\t\t}\n\t}\n\n\t/**\n\t * 后端连接ID生成器\n\t * \n\t * @author mycat\n\t */\n\tprivate static class ConnectIdGenerator {\n\n\t\tprivate static final long MAX_VALUE = Long.MAX_VALUE;\n\n\t\tprivate AtomicLong connectId = new AtomicLong(0);\n\n\t\tprivate long getId() {\n\t\t\treturn connectId.incrementAndGet();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/AIOSocketWR.java",
    "content": "package io.mycat.net;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.AsynchronousSocketChannel;\nimport java.nio.channels.CompletionHandler;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport io.mycat.util.TimeUtil;\n\npublic class AIOSocketWR extends SocketWR\n{\n    private static final AIOReadHandler aioReadHandler = new AIOReadHandler();\n    private static final AIOWriteHandler aioWriteHandler = new AIOWriteHandler();\n    private final AsynchronousSocketChannel channel;\n    protected final AbstractConnection con;\n    protected final AtomicBoolean writing = new AtomicBoolean(false);\n\n\n    public AIOSocketWR(AbstractConnection conn)\n    {\n        channel = (AsynchronousSocketChannel) conn.getChannel();\n        this.con = conn;\n    }\n\n    @Override\n    public void asynRead()\n    {\n        ByteBuffer theBuffer = con.readBuffer;\n        if (theBuffer == null)\n        {\n            theBuffer = con.processor.getBufferPool().allocate(con.processor.getBufferPool().getChunkSize());\n            con.readBuffer = theBuffer;\n            channel.read(theBuffer, this, aioReadHandler);\n\n        } else if (theBuffer.hasRemaining())\n        {\n            channel.read(theBuffer, this, aioReadHandler);\n        } else\n        {\n            throw new java.lang.IllegalArgumentException(\"full buffer to read \");\n        }\n\n    }\n\n    private void asynWrite(final ByteBuffer buffer)\n    {\n\n            buffer.flip();\n            this.channel.write(buffer, this, aioWriteHandler);\n\n\n    }\n\n//    public  int flushChannel(final AsynchronousSocketChannel channel,\n//                             final ByteBuffer bb, final long writeTimeout)\n//    {\n//\n//        if (!bb.hasRemaining())\n//        {\n//            return 0;\n//        }\n//        int nWrite = bb.limit();\n//        try\n//        {\n//            while (bb.hasRemaining())\n//            {\n//                channel.write(bb).get(writeTimeout, TimeUnit.SECONDS);\n//            }\n//        } catch (Exception ie)\n//        {\n//            con.close(\"write failed \" + ie);\n//\n//        }\n//        return nWrite;\n//    }\n\n\n    /**\n     * return true ,means no more data\n     *\n     * @return\n     */\n    private boolean write0()\n    {\n        if (!writing.compareAndSet(false, true))\n        {\n            return false;\n        }\n        ByteBuffer theBuffer = con.writeBuffer;\n        if (theBuffer == null || !theBuffer.hasRemaining())\n        {// writeFinished,但要区分bufer是否NULL，不NULL，要回收\n            if (theBuffer != null)\n            {\n                con.recycle(theBuffer);\n                con.writeBuffer = null;\n\n            }\n            // poll again\n            ByteBuffer buffer = con.writeQueue.poll();\n            // more data\n            if (buffer != null)\n            {\n                if (buffer.limit() == 0)\n                {\n                    con.recycle(buffer);\n                    con.writeBuffer = null;\n                    con.close(\"quit cmd\");\n                    writing.set(false);\n                    return true;\n                } else\n                {\n                    con.writeBuffer = buffer;\n                    asynWrite(buffer);\n                    return false;\n                }\n            } else\n            {\n                // no buffer\n               writing.set(false);\n                return true;\n            }\n        } else\n        {\n              theBuffer.compact();\n            asynWrite(theBuffer);\n            return false;\n        }\n\n    }\n\n    protected void onWriteFinished(int result)\n    {\n\n        con.netOutBytes += result;\n        con.processor.addNetOutBytes(result);\n        con.lastWriteTime = TimeUtil.currentTimeMillis();\n        boolean noMoreData = this.write0();\n        if (noMoreData)\n        {\n            this.doNextWriteCheck();\n        }\n\n    }\n\n    public void doNextWriteCheck()\n    {\n\n        boolean noMoreData = false;\n        noMoreData = this.write0();\n        if (noMoreData\n                && !con.writeQueue.isEmpty())\n        {\n                this.write0();\n        }\n\n\n    }\n\n    @Override\n    public boolean checkAlive() {\n        return channel.isOpen();\n    }\n\n    @Override\n    public void disableRead() {\n        // TODO Auto-generated method stub\n\n    }\n\n    @Override\n    public void enableRead() {\n        // TODO Auto-generated method stub\n\n    }\n}\n\nclass AIOWriteHandler implements CompletionHandler<Integer, AIOSocketWR> {\n\n    @Override\n    public void completed(final Integer result, final AIOSocketWR wr) {\n        try {\n\n            wr.writing.set(false);\n\n            if (result >= 0) {\n                wr.onWriteFinished(result);\n            } else {\n                wr.con.close(\"write erro \" + result);\n            }\n        } catch (Exception e) {\n            AbstractConnection.LOGGER.warn(\"caught aio process err:\", e);\n        }\n\n    }\n\n    @Override\n    public void failed(Throwable exc, AIOSocketWR wr) {\n        wr.writing.set(false);\n        wr.con.close(\"write failed \" + exc);\n    }\n\n}\n\n\nclass AIOReadHandler implements CompletionHandler<Integer, AIOSocketWR>\n{\n    @Override\n    public void completed(final Integer i, final AIOSocketWR wr)\n    {\n        // con.getProcessor().getExecutor().execute(new Runnable() {\n        // public void run() {\n        if (i > 0)\n        {\n            try\n            {\n                wr.con.onReadData(i);\n                wr.con.asynRead();\n            } catch (IOException e)\n            {\n                wr.con.close(\"handle err:\" + e);\n            }\n        } else if (i == -1)\n        {\n            // System.out.println(\"read -1 xxxxxxxxx \"+con);\n            wr.con.close(\"client closed\");\n        }\n        // }\n        // });\n    }\n\n    @Override\n    public void failed(Throwable exc, AIOSocketWR wr)\n    {\n        wr.con.close(exc.toString());\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/AbstractConnection.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\nimport java.io.IOException;\nimport java.net.Socket;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.AsynchronousChannel;\nimport java.nio.channels.NetworkChannel;\nimport java.nio.channels.SocketChannel;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport com.google.common.base.Strings;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.CharsetUtil;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.util.CompressUtil;\nimport io.mycat.util.TimeUtil;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\n/**\n * @author mycat\n */\npublic abstract class AbstractConnection implements NIOConnection {\n\t\n\tprotected static final Logger LOGGER = LoggerFactory.getLogger(AbstractConnection.class);\n\t\n\tprotected String host;\n\tprotected int localPort;\n\tprotected int port;\n\tprotected long id;\n\tprotected volatile String charset;\n\tprotected volatile int charsetIndex;\n\n\tprotected final NetworkChannel channel;\n\tprotected NIOProcessor processor;\n\tprotected NIOHandler handler;\n\n\tprotected int packetHeaderSize;\n\tprotected int maxPacketSize;\n\tprotected volatile ByteBuffer readBuffer;\n\tprotected volatile ByteBuffer writeBuffer;\n\t\n\tprotected final ConcurrentLinkedQueue<ByteBuffer> writeQueue = new ConcurrentLinkedQueue<ByteBuffer>();\n\t\n\tprotected volatile int readBufferOffset;\n\tprotected long lastLargeMessageTime;\n\tprotected final AtomicBoolean isClosed;\n\tprotected boolean isSocketClosed;\n\tprotected long startupTime;\n\tprotected long lastReadTime;\n\tprotected long lastWriteTime;\n\tprotected long netInBytes;\n\tprotected long netOutBytes;\n\tprotected int writeAttempts;\n\t\n\tprotected volatile boolean isSupportCompress = false;\n    protected final ConcurrentLinkedQueue<byte[]> decompressUnfinishedDataQueue = new ConcurrentLinkedQueue<byte[]>();\n    protected final ConcurrentLinkedQueue<byte[]> compressUnfinishedDataQueue = new ConcurrentLinkedQueue<byte[]>();\n\n\tprivate long idleTimeout;\n\n\tprotected final SocketWR socketWR;\n\n    protected boolean enableFlowController;// writeQueue是否开启流控\n    protected final int writeQueueStopThreshold;// writeQueue停止阀值\n    protected final int writeQueueRecoverThreshold;// writeQueue恢复阀值\n\n\tpublic AbstractConnection(NetworkChannel channel) {\n\t\tthis.channel = channel;\n\t\tboolean isAIO = (channel instanceof AsynchronousChannel);\n\t\tif (isAIO) {\n\t\t\tsocketWR = new AIOSocketWR(this);\n\t\t} else {\n\t\t\tsocketWR = new NIOSocketWR(this);\n\t\t}\n\t\tthis.isClosed = new AtomicBoolean(false);\n\t\tthis.startupTime = TimeUtil.currentTimeMillis();\n\t\tthis.lastReadTime = startupTime;\n\t\tthis.lastWriteTime = startupTime;\n\n        SystemConfig config = MycatServer.getInstance().getConfig().getSystem();\n        this.enableFlowController = config.isEnableWriteQueueFlowControl();\n        this.writeQueueStopThreshold = config.getWriteQueueStopThreshold();\n        this.writeQueueRecoverThreshold = config.getWriteQueueRecoverThreshold();\n\t}\n\n\tpublic String getCharset() {\n\t\treturn charset;\n\t}\n\n\tpublic boolean setCharset(String charset) {\n\n\t\t// 修复PHP字符集设置错误, 如： set names 'utf8'\n\t\tif (charset != null) {\n\t\t\tcharset = charset.replace(\"'\", \"\");\n\t\t}\n\n\t\tint ci = CharsetUtil.getIndex(charset);\n\t\tif (ci > 0) {\n\t\t\tthis.charset = charset.equalsIgnoreCase(\"utf8mb4\") ? \"utf8\" : charset;\n\t\t\tthis.charsetIndex = ci;\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic boolean isSupportCompress() {\n\t\treturn isSupportCompress;\n\t}\n\n\tpublic void setSupportCompress(boolean isSupportCompress) {\n\t\tthis.isSupportCompress = isSupportCompress;\n\t}\n\n\tpublic int getCharsetIndex() {\n\t\treturn charsetIndex;\n\t}\n\n\tpublic long getIdleTimeout() {\n\t\treturn idleTimeout;\n\t}\n\n\tpublic SocketWR getSocketWR() {\n\t\treturn socketWR;\n\t}\n\n\tpublic void setIdleTimeout(long idleTimeout) {\n\t\tthis.idleTimeout = idleTimeout;\n\t}\n\n\tpublic int getLocalPort() {\n\t\treturn localPort;\n\t}\n\n\tpublic String getHost() {\n\t\treturn host;\n\t}\n\n\tpublic void setHost(String host) {\n\t\tthis.host = host;\n\t}\n\n\tpublic int getPort() {\n\t\treturn port;\n\t}\n\n\tpublic void setPort(int port) {\n\t\tthis.port = port;\n\t}\n\n\tpublic void setLocalPort(int localPort) {\n\t\tthis.localPort = localPort;\n\t}\n\n\tpublic long getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(long id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic boolean isIdleTimeout() {\n\t\treturn TimeUtil.currentTimeMillis() > Math.max(lastWriteTime, lastReadTime) + idleTimeout;\n\t}\n\n\tpublic NetworkChannel getChannel() {\n\t\treturn channel;\n\t}\n\n\tpublic int getPacketHeaderSize() {\n\t\treturn packetHeaderSize;\n\t}\n\n\tpublic void setPacketHeaderSize(int packetHeaderSize) {\n\t\tthis.packetHeaderSize = packetHeaderSize;\n\t}\n\n\tpublic int getMaxPacketSize() {\n\t\treturn maxPacketSize;\n\t}\n\n\tpublic void setMaxPacketSize(int maxPacketSize) {\n\t\tthis.maxPacketSize = maxPacketSize;\n\t}\n\n\tpublic long getStartupTime() {\n\t\treturn startupTime;\n\t}\n\n\tpublic long getLastReadTime() {\n\t\treturn lastReadTime;\n\t}\n\n\tpublic void setProcessor(NIOProcessor processor) {\n\t\tthis.processor = processor;\n\t\tint size = processor.getBufferPool().getChunkSize();\n\t\tthis.readBuffer = processor.getBufferPool().allocate(size);\n\t}\n\n\tpublic long getLastWriteTime() {\n\t\treturn lastWriteTime;\n\t}\n\t\n\tpublic void setLastWriteTime(long lasttime){\n\t\tthis.lastWriteTime = lasttime;\n\t}\n\n\tpublic long getNetInBytes() {\n\t\treturn netInBytes;\n\t}\n\n\tpublic long getNetOutBytes() {\n\t\treturn netOutBytes;\n\t}\n\n\tpublic int getWriteAttempts() {\n\t\treturn writeAttempts;\n\t}\n\n\tpublic NIOProcessor getProcessor() {\n\t\treturn processor;\n\t}\n\n\tpublic ByteBuffer getReadBuffer() {\n\t\treturn readBuffer;\n\t}\n\n\tpublic ByteBuffer allocate() {\n\t\tint size = this.processor.getBufferPool().getChunkSize();\n\t\tByteBuffer buffer = this.processor.getBufferPool().allocate(size);\n\t\treturn buffer;\n\t}\n\n\tpublic final void recycle(ByteBuffer buffer) {\n\t\tthis.processor.getBufferPool().recycle(buffer);\n\t}\n\n\tpublic void setHandler(NIOHandler handler) {\n\t\tthis.handler = handler;\n\t}\n\n\t@Override\n\tpublic void handle(byte[] data) {\n\t\tif (isSupportCompress()) {\n\t\t\tList<byte[]> packs = CompressUtil.decompressMysqlPacket(data, decompressUnfinishedDataQueue);\n\t\t\tfor (byte[] pack : packs) {\n\t\t\t\tif (pack.length != 0) {\n\t\t\t\t\thandler.handle(pack);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\thandler.handle(data);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void register() throws IOException {\n\n\t}\n\n\tpublic void asynRead() throws IOException {\n\t\tthis.socketWR.asynRead();\n\t}\n\n\tpublic void doNextWriteCheck() throws IOException {\n\t\tthis.socketWR.doNextWriteCheck();\n\t}\n\n\t/**\n\t * 读取可能的Socket字节流\n\t */\n\tpublic void onReadData(int got) throws IOException {\n\t\t\n\t\tif (isClosed.get()) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tlastReadTime = TimeUtil.currentTimeMillis();\n\t\tif (got < 0) {\n\t\t\tthis.close(\"stream closed\");\n            return;\n\t\t} else if (got == 0\n\t\t\t\t&& !this.channel.isOpen()) {\n\t\t\t\tthis.close(\"socket closed\");\n\t\t\t\treturn;\n\t\t}\n\t\tnetInBytes += got;\n\t\tprocessor.addNetInBytes(got);\n\n\t\t// 循环处理字节信息\n\t\tint offset = readBufferOffset, length = 0, position = readBuffer.position();\n\t\tfor (;;) {\n\t\t\tlength = getPacketLength(readBuffer, offset);\t\t\t\n\t\t\tif (length == -1) {\n\t\t\t\tif (offset != 0) {\n\t\t\t\t\tthis.readBuffer = compactReadBuffer(readBuffer, offset);\n\t\t\t\t} else if (readBuffer != null && !readBuffer.hasRemaining()) {\n\t\t\t\t\tthrow new RuntimeException( \"invalid readbuffer capacity ,too little buffer size \" \n\t\t\t\t\t\t\t+ readBuffer.capacity());\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (position >= offset + length && readBuffer != null) {\n\t\t\t\t\n\t\t\t\t// handle this package\n\t\t\t\treadBuffer.position(offset);\t\t\t\t\n\t\t\t\tbyte[] data = new byte[length];\n\t\t\t\treadBuffer.get(data, 0, length);\n\t\t\t\thandle(data);\n\t\t\t\t\n\t\t\t\t// maybe handle stmt_close\n\t\t\t\tif(isClosed()) {\n\t\t\t\t\treturn ;\n\t\t\t\t}\n\n\t\t\t\t// offset to next position\n\t\t\t\toffset += length;\n\t\t\t\t\n\t\t\t\t// reached end\n\t\t\t\tif (position == offset) {\n\t\t\t\t\t// if cur buffer is temper none direct byte buffer and not\n\t\t\t\t\t// received large message in recent 30 seconds\n\t\t\t\t\t// then change to direct buffer for performance\n\t\t\t\t\tif (readBuffer != null && !readBuffer.isDirect()\n\t\t\t\t\t\t\t&& lastLargeMessageTime < lastReadTime - 30 * 1000L) {  // used temp heap\n\t\t\t\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\t\t\t\tLOGGER.debug(\"change to direct con read buffer ,cur temp buf size :\" + readBuffer.capacity());\n\t\t\t\t\t\t}\n\t\t\t\t\t\trecycle(readBuffer);\n\t\t\t\t\t\treadBuffer = processor.getBufferPool().allocate(processor.getBufferPool().getConReadBuferChunk());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (readBuffer != null) {\n\t\t\t\t\t\t\treadBuffer.clear();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// no more data ,break\n\t\t\t\t\treadBufferOffset = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t} else {\n\t\t\t\t\t// try next package parse\n\t\t\t\t\treadBufferOffset = offset;\n\t\t\t\t\tif(readBuffer != null) {\n\t\t\t\t\t\treadBuffer.position(position);\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t} else {\t\t\t\t\n\t\t\t\t// not read whole message package ,so check if buffer enough and\n\t\t\t\t// compact readbuffer\n\t\t\t\tif (!readBuffer.hasRemaining()) {\n\t\t\t\t\treadBuffer = ensureFreeSpaceOfReadBuffer(readBuffer, offset, length);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate boolean isConReadBuffer(ByteBuffer buffer) {\n\t\treturn buffer.capacity() == processor.getBufferPool().getConReadBuferChunk() && buffer.isDirect();\n\t}\n\t\n\tprivate ByteBuffer ensureFreeSpaceOfReadBuffer(ByteBuffer buffer,\n\t\t\tint offset, final int pkgLength) {\n\t\t// need a large buffer to hold the package\n\t\tif (pkgLength > maxPacketSize) {\n\t\t\tthrow new IllegalArgumentException(\"Packet size over the limit.\");\n\t\t} else if (buffer.capacity() < pkgLength) {\n\t\t\n\t\t\tByteBuffer newBuffer = processor.getBufferPool().allocate(pkgLength);\n\t\t\tlastLargeMessageTime = TimeUtil.currentTimeMillis();\n\t\t\tbuffer.position(offset);\n\t\t\tnewBuffer.put(buffer);\n\t\t\treadBuffer = newBuffer;\n\n\t\t\trecycle(buffer);\n\t\t\treadBufferOffset = 0;\n\t\t\treturn newBuffer;\n\n\t\t} else {\n\t\t\tif (offset != 0) {\n\t\t\t\t// compact bytebuffer only\n\t\t\t\treturn compactReadBuffer(buffer, offset);\n\t\t\t} else {\n\t\t\t\tthrow new RuntimeException(\" not enough space\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate ByteBuffer compactReadBuffer(ByteBuffer buffer, int offset) {\n\t\tif(buffer == null) {\n\t\t\treturn null;\n\t\t}\n\t\tbuffer.limit(buffer.position());\n\t\tbuffer.position(offset);\n\t\tbuffer = buffer.compact();\n\t\treadBufferOffset = 0;\n\t\treturn buffer;\n\t}\n\n\tpublic void write(byte[] data) {\n\t\tByteBuffer buffer = allocate();\n\t\tbuffer = writeToBuffer(data, buffer);\n\t\twrite(buffer);\n\n\t}\n\n\tprivate final void writeNotSend(ByteBuffer buffer) {\n\t\tif (isSupportCompress()) {\n\t\t\tByteBuffer newBuffer = CompressUtil.compressMysqlPacket(buffer, this, compressUnfinishedDataQueue);\n\t\t\twriteQueue.offer(newBuffer);\n\t\t\t\n\t\t} else {\n\t\t\twriteQueue.offer(buffer);\n\t\t}\n\t\t\n\t\tif(isClosed()) {\n\t\t\tLOGGER.warn(\"write err:{}\", this);\n\t\t\tthis.close(\"found this connection has close , try to reClean the connection\");\n\t\t\tthrow new RuntimeException(\"writeNotSend but found connnection close err:\" + this);\n\t\t}\n\t}\n\n\n    @Override\n\tpublic final void write(ByteBuffer buffer) {\n    \t\n\t\tif (isSupportCompress()) {\n\t\t\tByteBuffer newBuffer = CompressUtil.compressMysqlPacket(buffer, this, compressUnfinishedDataQueue);\n\t\t\twriteQueue.offer(newBuffer);\n\t\t} else {\n\t\t\twriteQueue.offer(buffer);\n\t\t}\n\n\t\t// if ansyn write finishe event got lock before me ,then writing\n\t\t// flag is set false but not start a write request\n\t\t// so we check again\n\t\ttry {\n\t\t\tthis.socketWR.doNextWriteCheck();\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.warn(\"write err:\", e);\n\t\t\tthis.close(\"write err:\" + e);\n\t\t}\n\t}\n\n\t\n\tpublic ByteBuffer checkWriteBuffer(ByteBuffer buffer, int capacity, boolean writeSocketIfFull) {\n\t\tif (capacity > buffer.remaining()) {\n\t\t\tif (writeSocketIfFull) {\n\t\t\t\twriteNotSend(buffer);\n\t\t\t\treturn processor.getBufferPool().allocate(capacity);\n\t\t\t} else {// Relocate a larger buffer\n\t\t\t\tbuffer.flip();\n\t\t\t\tByteBuffer newBuf = processor.getBufferPool().allocate(capacity + buffer.limit() + 1);\n\t\t\t\tnewBuf.put(buffer);\n\t\t\t\tthis.recycle(buffer);\n\t\t\t\treturn newBuf;\n\t\t\t}\n\t\t} else {\n\t\t\treturn buffer;\n\t\t}\n\t}\n\n\tpublic ByteBuffer writeToBuffer(byte[] src, ByteBuffer buffer) {\n\t\tint offset = 0;\n\t\tint length = src.length;\n\t\tint remaining = buffer.remaining();\n\t\twhile (length > 0) {\n\t\t\tif (remaining >= length) {\n\t\t\t\tbuffer.put(src, offset, length);\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tbuffer.put(src, offset, remaining);\n\t\t\t\twriteNotSend(buffer);\n\t\t\t\tbuffer = allocate();\n\t\t\t\toffset += remaining;\n\t\t\t\tlength -= remaining;\n\t\t\t\tremaining = buffer.remaining();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\treturn buffer;\n\t}\n\n\t@Override\n\tpublic void close(String reason) {\n\t\tif (!isClosed.get()) {\n\t\t\tcloseSocket();\n\t\t\tisClosed.set(true);\n\t\t\tif (processor != null) {\n\t\t\t\tprocessor.removeConnection(this);\n\t\t\t}\n\t\t\tthis.cleanup();\n\t\t\tisSupportCompress = false;\n\n\t\t\t// ignore null information\n\t\t\tif (Strings.isNullOrEmpty(reason)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tLOGGER.info(\"close connection,reason:\" + reason + \" ,\" + this);\n\t\t\tif (reason.contains(\"connection,reason:java.net.ConnectException\")) {\n\t\t\t\tthrow new RuntimeException(\" errr\");\n\t\t\t}\n\t\t} else {\n\t\t    // make sure cleanup again\n\t\t    // Fix issue#1616\n\t\t    this.cleanup();\n\t\t}\n\t}\n\n\tpublic boolean isClosed() {\n\t\treturn isClosed.get();\n\t}\n\n\tpublic void idleCheck() {\n\t\tif (isIdleTimeout()) {\n\t\t\tLOGGER.info(toString() + \" idle timeout\");\n\t\t\tclose(\" idle \");\n\t\t}\n\t}\n\n\t/**\n\t * 清理资源\n\t */\n    synchronized protected void cleanup() {\n\t\t\n\t\t// 清理资源占用\n\t\tif (readBuffer != null) {\n\t\t\tthis.recycle(readBuffer);\n\t\t\tthis.readBuffer = null;\n\t\t\tthis.readBufferOffset = 0;\n\t\t}\n\t\t\n\t\tif (writeBuffer != null) {\n\t\t\trecycle(writeBuffer);\n\t\t\tthis.writeBuffer = null;\n\t\t}\n\t\t\n\t\tif (!decompressUnfinishedDataQueue.isEmpty()) {\n\t\t\tdecompressUnfinishedDataQueue.clear();\n\t\t}\n\t\t\n\t\tif (!compressUnfinishedDataQueue.isEmpty()) {\n\t\t\tcompressUnfinishedDataQueue.clear();\n\t\t}\n\t\t\n\t\tByteBuffer buffer = null;\n\t\twhile ((buffer = writeQueue.poll()) != null) {\n\t\t\trecycle(buffer);\n\t\t}\n\t}\n\t\n\tprotected int getPacketLength(ByteBuffer buffer, int offset) {\n\t\tint headerSize = getPacketHeaderSize();\n\t\tif ( isSupportCompress() ) {\n\t\t\theaderSize = 7;\n\t\t}\n\t\t\n\t\tif (buffer.position() < offset + headerSize) {\n\t\t\treturn -1;\n\t\t} else {\n\t\t\tint length = buffer.get(offset) & 0xff;\n\t\t\tlength |= (buffer.get(++offset) & 0xff) << 8;\n\t\t\tlength |= (buffer.get(++offset) & 0xff) << 16;\n\t\t\treturn length + headerSize;\n\t\t}\n\t}\n\n\tpublic ConcurrentLinkedQueue<ByteBuffer> getWriteQueue() {\n\t\treturn writeQueue;\n\t}\n\n\tprivate void closeSocket() {\n\t\tif (channel != null) {\n\t\t\tif (channel instanceof SocketChannel) {\n\t\t\t\tSocket socket = ((SocketChannel) channel).socket();\n\t\t\t\tif (socket != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsocket.close();\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t       LOGGER.error(\"closeChannelError\", e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\t\n\t\t\tboolean isSocketClosed = true;\n\t\t\ttry {\n\t\t\t\tchannel.close();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(\"AbstractConnectionCloseError\", e);\n\t\t\t}\n\t\t\t\n\t\t\tboolean closed = isSocketClosed && (!channel.isOpen());\n\t\t\tif (closed == false) {\n\t\t\t\tLOGGER.warn(\"close socket of connnection failed \" + this);\n\t\t\t}\n\t\t}\n\t}\n\tpublic void onConnectfinish() {\n\t\tLOGGER.debug(\"连接后台真正完成\");\n\t}\n\n\tpublic boolean checkAlive(){\n\treturn \tsocketWR.checkAlive();\n\t}\n\n    public boolean isEnableFlowController() {\n        return enableFlowController;\n    }\n\n    public int getWriteQueueStopThreshold() {\n        return writeQueueStopThreshold;\n    }\n\n    public int getWriteQueueRecoverThreshold() {\n        return writeQueueRecoverThreshold;\n    }\n\n    /**\n     * 检查写队列流量，必要时候进行流控\n     */\n    abstract public void checkQueueFlow();\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/BIOConnection.java",
    "content": "package io.mycat.net;\n\npublic interface BIOConnection extends ClosableConnection{\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/BackendAIOConnection.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.nio.channels.NetworkChannel;\n\nimport io.mycat.backend.BackendConnection;\n\n/**\n * @author mycat\n */\npublic abstract class BackendAIOConnection extends AbstractConnection implements\n\t\tBackendConnection {\n\n\t\n\t\n\tprotected boolean isFinishConnect;\n\n\tpublic BackendAIOConnection(NetworkChannel channel) {\n\t\tsuper(channel);\n\t}\n\n\tpublic void register() throws IOException {\n\t\tthis.asynRead();\n\t}\n\n\n\tpublic void setHost(String host) {\n\t\tthis.host = host;\n\t}\n\n\n\tpublic void setPort(int port) {\n\t\tthis.port = port;\n\t}\n\n\t\n\n\t\n\tpublic void discardClose(String reason){\n\t\t//跨节点处理,中断后端连接时关闭\n\t}\n\tpublic abstract void onConnectFailed(Throwable e);\n\n\tpublic boolean finishConnect() throws IOException {\n\t\tlocalPort = ((InetSocketAddress) channel.getLocalAddress()).getPort();\n\t\tisFinishConnect = true;\n\t\treturn true;\n\t}\n\n\tpublic void setProcessor(NIOProcessor processor) {\n\t\tsuper.setProcessor(processor);\n\t\tprocessor.addBackend(this);\n\t}\n\n    @Override\n    public void checkQueueFlow() {\n\n    }\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"BackendConnection [id=\" + id + \", host=\" + host + \", port=\"\n\t\t\t\t+ port + \", localPort=\" + localPort + \"]\";\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/ClosableConnection.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\npublic interface ClosableConnection {\n\tString getCharset();\n\t/**\n\t * 关闭连接\n\t */\n\tvoid close(String reason);\n\n\tboolean isClosed();\n\n\tpublic void idleCheck();\n\n\tlong getStartupTime();\n\n\tString getHost();\n\n\tint getPort();\n\n\tint getLocalPort();\n\n\tlong getNetInBytes();\n\n\tlong getNetOutBytes();\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/ConnectionException.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\npublic class ConnectionException extends RuntimeException {\n\t/**\n\t * \n\t */\n\tprivate static final long serialVersionUID = 1L;\n\tprivate final int code;\n\tprivate final String msg;\n\n\tpublic ConnectionException(int code, String msg) {\n\t\tsuper(msg);\n\t\tthis.code = code;\n\t\tthis.msg = msg;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ConnectionException [code=\" + code + \", msg=\" + msg + \"]\";\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/FrontendConnection.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.net.InetSocketAddress;\nimport java.nio.channels.AsynchronousSocketChannel;\nimport java.nio.channels.NetworkChannel;\nimport java.nio.channels.SocketChannel;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.mysql.CharsetUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.config.Capabilities;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.Versions;\nimport io.mycat.net.handler.FrontendAuthenticator;\nimport io.mycat.net.handler.FrontendPrepareHandler;\nimport io.mycat.net.handler.FrontendPrivileges;\nimport io.mycat.net.handler.FrontendQueryHandler;\nimport io.mycat.net.handler.LoadDataInfileHandler;\nimport io.mycat.net.mysql.CommandPacket;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.HandshakePacket;\nimport io.mycat.net.mysql.HandshakeV10Packet;\nimport io.mycat.net.mysql.MySQLPacket;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.util.CompressUtil;\nimport io.mycat.util.RandomUtil;\n\n/**\n * @author mycat\n */\npublic abstract class FrontendConnection extends AbstractConnection {\n\t\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(FrontendConnection.class);\n\n\tprotected long idleTimeout;\n\tprotected byte[] seed;\n\tprotected String user;\n\tprotected String schema;\n\tprotected String executeSql;\n\n\tprotected volatile long executeSqlId = 0;\n\tprotected AtomicLong  responseSqlId = new AtomicLong(0); //新增executeSqlId ,repsonseSqlId 用于避免对一个sql 写回了多个错误的结果.\n\t\n\tprotected FrontendPrivileges privileges;\n\tprotected FrontendQueryHandler queryHandler;\n\tprotected FrontendPrepareHandler prepareHandler;\n\tprotected LoadDataInfileHandler loadDataInfileHandler;\n\tprotected boolean isAccepted;\n\tprotected boolean isAuthenticated;\n    protected QueueFlowController flowController;\n\tprivate boolean allowMultiStatements = false;\n\n\tpublic FrontendConnection(NetworkChannel channel) throws IOException {\n\t\tsuper(channel);\n\t\tInetSocketAddress localAddr = (InetSocketAddress) channel.getLocalAddress();\n\t\tInetSocketAddress remoteAddr = null;\n\t\tif (channel instanceof SocketChannel) {\n\t\t\tremoteAddr = (InetSocketAddress) ((SocketChannel) channel).getRemoteAddress();\t\n\t\t\t\n\t\t} else if (channel instanceof AsynchronousSocketChannel) {\n\t\t\tremoteAddr = (InetSocketAddress) ((AsynchronousSocketChannel) channel).getRemoteAddress();\n\t\t}\n\t\t\n\t\tthis.host = remoteAddr.getHostString();\n\t\tthis.port = localAddr.getPort();\n\t\tthis.localPort = remoteAddr.getPort();\n\t\tthis.handler = new FrontendAuthenticator(this);\n\n        if (enableFlowController) {\n            this.flowController = new QueueFlowController(this);\n        }\n\t}\n\n\tpublic long getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(long id) {\n\t\tthis.id = id;\n\t\tif(LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(this + \" localPort:\" + this.localPort + \" port\"+this.port);\t\t\n\t\t}\n\t}\n\n\tpublic String getHost() {\n\t\treturn host;\n\t}\n\n\tpublic void setHost(String host) {\n\t\tthis.host = host;\n\t}\n\n\tpublic int getPort() {\n\t\treturn port;\n\t}\n\n\tpublic void setPort(int port) {\n\t\tthis.port = port;\n\t}\n\n\tpublic int getLocalPort() {\n\t\treturn localPort;\n\t}\n\n\tpublic void setLocalPort(int localPort) {\n\t\tthis.localPort = localPort;\n\t}\n\n\tpublic void setAccepted(boolean isAccepted) {\n\t\tthis.isAccepted = isAccepted;\n\t}\n\n\tpublic void setProcessor(NIOProcessor processor) {\n\t\tsuper.setProcessor(processor);\n\t\tprocessor.addFrontend(this);\n\t}\n\n\tpublic LoadDataInfileHandler getLoadDataInfileHandler() {\n\t\treturn loadDataInfileHandler;\n\t}\n\n\tpublic void setLoadDataInfileHandler(LoadDataInfileHandler loadDataInfileHandler) {\n\t\tthis.loadDataInfileHandler = loadDataInfileHandler;\n\t}\n\n\tpublic void setQueryHandler(FrontendQueryHandler queryHandler) {\n\t\tthis.queryHandler = queryHandler;\n\t}\n\n\tpublic void setPrepareHandler(FrontendPrepareHandler prepareHandler) {\n\t\tthis.prepareHandler = prepareHandler;\n\t}\n\n\tpublic void setAuthenticated(boolean isAuthenticated) {\n\t\tthis.isAuthenticated = isAuthenticated;\n\t}\n\n\tpublic FrontendPrivileges getPrivileges() {\n\t\treturn privileges;\n\t}\n\n\tpublic void setPrivileges(FrontendPrivileges privileges) {\n\t\tthis.privileges = privileges;\n\t}\n\n\tpublic String getUser() {\n\t\treturn user;\n\t}\n\n\tpublic void setUser(String user) {\n\t\tthis.user = user;\n\t}\n\n\tpublic String getSchema() {\n\t\treturn schema;\n\t}\n\n\tpublic void setSchema(String schema) {\n\t\tthis.schema = schema;\n\t}\n\n\tpublic String getExecuteSql() {\n\t\treturn executeSql;\n\t}\n\n\tpublic void setExecuteSql(String executeSql) {\n\t\tthis.executeSql = executeSql;\n\t}\n\n\tpublic byte[] getSeed() {\n\t\treturn seed;\n\t}\n\n\tpublic boolean setCharsetIndex(int ci) {\n\t\tString charset = CharsetUtil.getCharset(ci);\n\t\tif (charset != null) {\n\t\t\treturn setCharset(charset);\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic boolean isAllowMultiStatements() {\n\t\treturn allowMultiStatements;\n\t}\n\n\tpublic void setAllowMultiStatements(boolean allowMultiStatements) {\n\t\tthis.allowMultiStatements = allowMultiStatements;\n\t}\n\n\tpublic void writeErrMessage(int errno, String msg) {\n\t\tif(this.canResponse()){\n\t\t\t\tLOGGER.error(\"{}{} write errorMsg:{} error\",this, msg+ getStack());\n\t\t\twriteErrMessage((byte) 1, errno, msg);\n\t\t} else {\n\t\t\t\tLOGGER.error(\"{} write errorMsg:{} error\",this,msg);\n\t\t}\n\t}\n\n\t//前端sql已经返回结果集 则调用这个函数 避免向前端返回多次结果。\n\t//modify by zwy\n\tpublic void setResponseId() {\n\t\t//this.responseSqlId = this.executeSqlId;\n\t\tthis.canResponse();\n\t}\n\tpublic String getStack() {\n\t\tStackTraceElement stack[] = Thread.currentThread().getStackTrace();  \n\t\tStringBuilder sb = new StringBuilder();\n        for(int i=0;i<stack.length;i++){\n        \tsb.append(stack[i].getClassName()+\" .\"+stack[i].getMethodName()+stack[i].getLineNumber()+\"\\n\");\n        }\n        return sb.toString();\n\t}\n\t//modify by zwy 2018.07\n\tpublic boolean canResponse() {\n\t\t\n//\t\t\treturn true;\n\t\tlong resId = this.responseSqlId.get();\n\t\tif(this.executeSqlId > resId) {\n\t\t\tboolean t = this.responseSqlId.compareAndSet(resId, this.executeSqlId); \n\t\t\tif(false) {\n\t\t\t\tStackTraceElement stack[] = Thread.currentThread().getStackTrace();  \n\t\t\t\tStringBuilder sb = new StringBuilder();\n                for(int i=0;i<stack.length;i++){\n                \tsb.append(stack[i].getClassName()+\" .\"+stack[i].getMethodName()+stack[i].getLineNumber()+\"\\n\");\n                }\t\t\t\t\n\t\t\t\tLOGGER.debug(\"can Response \" + this.toString() + \"  \"+ getStack());\n\t\t\t}\n\t\t\treturn t;\n\t\t}else {\n\t\t\treturn false;\n\t\t}\n\t}\n\tpublic void writeErrMessage(byte id, int errno, String msg) {\n\t\tErrorPacket err = new ErrorPacket();\n\t\terr.packetId = id;\n\t\terr.errno = errno;\n\t\terr.message = encodeString(msg, charset);\n\t\terr.write(this);\n\t}\n\t\n\tpublic void initDB(byte[] data) {\n\t\t\n\t\tMySQLMessage mm = new MySQLMessage(data);\n\t\tmm.position(5);\n\t\tString db = mm.readString();\n\n\t\t// 检查schema的有效性\n\t\tif (db == null || !privileges.schemaExists(db)) {\n\t\t\twriteErrMessage(ErrorCode.ER_BAD_DB_ERROR, \"Unknown database '\" + db + \"'\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (!privileges.userExists(user, host)) {\n\t\t\twriteErrMessage(ErrorCode.ER_ACCESS_DENIED_ERROR, \"Access denied for user '\" + user + \"'\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tSet<String> schemas = privileges.getUserSchemas(user);\n\t\tif (schemas == null || schemas.size() == 0 || schemas.contains(db)) {\n\t\t\tthis.schema = db;\n\t\t\twrite(writeToBuffer(OkPacket.OK, allocate()));\n\t\t} else {\n\t\t\tString s = \"Access denied for user '\" + user + \"' to database '\" + db + \"'\";\n\t\t\twriteErrMessage(ErrorCode.ER_DBACCESS_DENIED_ERROR, s);\n\t\t}\n\t}\n\n\n\tpublic void loadDataInfileStart(String sql) {\n\t\tif (loadDataInfileHandler != null) {\n\t\t\ttry {\n\t\t\t\tloadDataInfileHandler.start(sql);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(\"load data error\", e);\n\t\t\t\twriteErrMessage(ErrorCode.ERR_HANDLE_DATA, e.getMessage());\n\t\t\t}\n\n\t\t} else {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"load data infile sql is not  unsupported!\");\n\t\t}\n\t}\n\n\tpublic void loadDataInfileData(byte[] data) {\n\t\tif (loadDataInfileHandler != null) {\n\t\t\ttry {\n\t\t\t\tloadDataInfileHandler.handle(data);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(\"load data error\", e);\n\t\t\t\twriteErrMessage(ErrorCode.ERR_HANDLE_DATA, e.getMessage());\n\t\t\t}\n\t\t} else {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"load data infile  data is not  unsupported!\");\n\t\t}\n\n\t}\n\n\tpublic void loadDataInfileEnd(byte packID) {\n\t\tif (loadDataInfileHandler != null) {\n\t\t\ttry {\n\t\t\t\tloadDataInfileHandler.end(packID);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(\"load data error\", e);\n\t\t\t\twriteErrMessage(ErrorCode.ERR_HANDLE_DATA, e.getMessage());\n\t\t\t}\n\t\t} else {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"load data infile end is not  unsupported!\");\n\t\t}\n\t}\n\t\n\t\n\tpublic void query(String sql) {\n\t\t\n\t\tif (sql == null || sql.length() == 0) {\n\t\t\twriteErrMessage(ErrorCode.ER_NOT_ALLOWED_COMMAND, \"Empty SQL\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(new StringBuilder().append(this).append(\" \").append(sql).toString());\n\t\t}\n\t\t\n\t\t// remove last ';'\n\t\tif (sql.endsWith(\";\")) {\n\t\t\tsql = sql.substring(0, sql.length() - 1);\n\t\t}\n\t\t// remove like '/* ApplicationName=DBeaver 6.0.1 - Main */' tool app hints\n\t\tif(sql.indexOf(\"/* ApplicationName\") >=0) {\n\t\t\tsql = sql.replaceFirst(\"\\\\/\\\\*.*\\\\*\\\\/\\\\s*\", \"\");\n\t\t}\n\t\t\n\t\t// 记录SQL\n\t\tthis.setExecuteSql(sql);\n\t\t\n\t\t// 防火墙策略( SQL 黑名单/ 注入攻击)\n\t\tif ( !privileges.checkFirewallSQLPolicy( user, sql ) ) {\n\t\t\twriteErrMessage(ErrorCode.ERR_WRONG_USED, \n\t\t\t\t\t\"The statement is unsafe SQL, reject for user '\" + user + \"'\");\n\t\t\treturn;\n\t\t}\t\t\n\t\t\n\t\t// DML 权限检查\n\t\ttry {\n\t\t\tboolean isPassed = privileges.checkDmlPrivilege(user, schema, sql);\n\t\t\tif ( !isPassed ) {\n\t\t\t\twriteErrMessage(ErrorCode.ERR_WRONG_USED, \n\t\t\t\t\t\t\"The statement DML privilege check is not passed, reject for user '\" + user + \"'\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t } catch( com.alibaba.druid.sql.parser.ParserException e1) {\n\t        \twriteErrMessage(ErrorCode.ERR_WRONG_USED,  e1.getMessage());\n\t        \tLOGGER.error(\"parse exception\", e1 );\n\t\t\t\treturn;\n\t     }\n\t\t\n\t\t// 执行查询\n\t\tif (queryHandler != null) {\t\t\t\n\t\t\tqueryHandler.setReadOnly(privileges.isReadOnly(user));\n\t\t\tqueryHandler.query(sql);\n\t\t\t\n\t\t} else {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"Query unsupported!\");\n\t\t}\t\t\n\t}\n\t\n\tpublic void query(byte[] data) {\n\t\t\n\t\t// 取得语句\n\t\tfinal String sql;\t\t\n\t\ttry {\n\t\t\tMySQLMessage mm = new MySQLMessage(data);\n\t\t\tmm.position(5);\n\t\t\tsql = mm.readString(charset);\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, \"Unknown charset '\" + charset + \"'\");\n\t\t\treturn;\n\t\t}\t\t\n\t\t\n\t\tMycatServer.getInstance().getBusinessExecutor().execute(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    FrontendConnection.this.query( sql );\n                  } catch (Throwable t) {\n                    FrontendConnection.this.writeErrMessage(ErrorCode.ERR_HANDLE_DATA, t.getMessage());\n                    FrontendConnection.LOGGER.error(\"uncaught exception executing sql:\"+FrontendConnection.this + \",[ \" + sql+ \"]\", t);\n                  }  \n                \n            }\n        });\n\t}\n\n\tpublic void stmtPrepare(byte[] data) {\n\t\tif (prepareHandler != null) {\n\t\t\t// 取得语句\n\t\t\tMySQLMessage mm = new MySQLMessage(data);\n\t\t\tmm.position(5);\n\t\t\tString sql = null;\n\t\t\ttry {\n\t\t\t\tsql = mm.readString(charset);\n\t\t\t} catch (UnsupportedEncodingException e) {\n\t\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET,\n\t\t\t\t\t\t\"Unknown charset '\" + charset + \"'\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (sql == null || sql.length() == 0) {\n\t\t\t\twriteErrMessage(ErrorCode.ER_NOT_ALLOWED_COMMAND, \"Empty SQL\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t// 记录SQL\n\t\t\tthis.setExecuteSql(sql);\n\t\t\t\n\t\t\t// 执行预处理\n\t\t\tprepareHandler.prepare(sql);\n\t\t} else {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"Prepare unsupported!\");\n\t\t}\n\t}\n\t\n\tpublic void stmtSendLongData(byte[] data) {\n\t\tif(prepareHandler != null) {\n\t\t\tprepareHandler.sendLongData(data);\n\t\t} else {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"Prepare unsupported!\");\n\t\t}\n\t}\n\t\n\tpublic void stmtReset(byte[] data) {\n\t\tif(prepareHandler != null) {\n\t\t\tprepareHandler.reset(data);\n\t\t} else {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"Prepare unsupported!\");\n\t\t}\n\t}\n\n\tpublic void stmtExecute(byte[] data) {\n\t\tif (prepareHandler != null) {\n\t\t\tprepareHandler.execute(data);\n\t\t} else {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"Prepare unsupported!\");\n\t\t}\n\t}\n\n\tpublic void stmtClose(byte[] data) {\n\t\tif (prepareHandler != null) {\n\t\t\tprepareHandler.close( data );\n\t\t} else {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"Prepare unsupported!\");\n\t\t}\n\t}\n\t/** \n\t * 用来模拟mysql协议命令中的com_field_list，方便通过发sql测试 \n\t * https://dev.mysql.com/doc/internals/en/com-field-list.html\n\t */\n\tpublic void fieldList(byte[] data) {\n\t\t// 取得语句\n\t\tString sql = null;\t\t\n\t\ttry {\n\t\t\tMySQLMessage mm = new MySQLMessage(data);\n\t\t\tmm.position(5);\n\t\t\tsql = mm.readString(charset);\n\t\t\tsql = ServerParse.COM_FIELD_LIST_FLAG + sql;\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, \"Unknown charset '\" + charset + \"'\");\n\t\t\treturn;\n\t\t}\t\t\n\t\tthis.query( sql );\n\t}\n\n\tpublic void ping() {\n\t\twrite(writeToBuffer(OkPacket.OK, allocate()));\n\t}\n\n\tpublic void heartbeat(byte[] data) {\n\t\twrite(writeToBuffer(OkPacket.OK, allocate()));\n\t}\n\n\tpublic void kill(byte[] data) {\n\t\tclose(\"kill command\");\n\t}\n\n\tpublic void unknown(byte[] data) {\n\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"Unknown command\");\n\t}\n\n\t@Override\n\tpublic void register() throws IOException {\n\t\tif (!isClosed.get()) {\n\n\t\t\t// 生成认证数据\n\t\t\tbyte[] rand1 = RandomUtil.randomBytes(8);\n\t\t\tbyte[] rand2 = RandomUtil.randomBytes(12);\n\n\t\t\t// 保存认证数据\n\t\t\tbyte[] seed = new byte[rand1.length + rand2.length];\n\t\t\tSystem.arraycopy(rand1, 0, seed, 0, rand1.length);\n\t\t\tSystem.arraycopy(rand2, 0, seed, rand1.length, rand2.length);\n\t\t\tthis.seed = seed;\n\n\t\t\t// 发送握手数据包\n\t\t\tboolean useHandshakeV10 = MycatServer.getInstance().getConfig().getSystem().getUseHandshakeV10() == 1;\n\t\t\tif(useHandshakeV10) {\n\t\t\t\tHandshakeV10Packet hs = new HandshakeV10Packet();\n\t\t\t\ths.packetId = 0;\n\t\t\t\ths.protocolVersion = Versions.PROTOCOL_VERSION;\n\t\t\t\ths.serverVersion = Versions.SERVER_VERSION;\n\t\t\t\ths.threadId = id;\n\t\t\t\ths.seed = rand1;\n\t\t\t\ths.serverCapabilities = getServerCapabilities();\n\t\t\t\ths.serverCharsetIndex = (byte) (charsetIndex & 0xff);\n\t\t\t\ths.serverStatus = 2;\n\t\t\t\ths.restOfScrambleBuff = rand2;\n\t\t\t\ths.write(this);\n\t\t\t} else {\n\t\t\t\tHandshakePacket hs = new HandshakePacket();\n\t\t\t\ths.packetId = 0;\n\t\t\t\ths.protocolVersion = Versions.PROTOCOL_VERSION;\n\t\t\t\ths.serverVersion = Versions.SERVER_VERSION;\n\t\t\t\ths.threadId = id;\n\t\t\t\ths.seed = rand1;\n\t\t\t\ths.serverCapabilities = getServerCapabilities();\n\t\t\t\ths.serverCharsetIndex = (byte) (charsetIndex & 0xff);\n\t\t\t\ths.serverStatus = 2;\n\t\t\t\ths.restOfScrambleBuff = rand2;\n\t\t\t\ths.write(this);\n\t\t\t}\n\n\t\t\t// asynread response\n\t\t\tthis.asynRead();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void handle(final byte[] data) {\n\t\tthis.executeSqlId ++;\n\t\tif (isSupportCompress()) {\t\t\t\n\t\t\tList<byte[]> packs = CompressUtil.decompressMysqlPacket(data, decompressUnfinishedDataQueue);\n\t\t\tfor (byte[] pack : packs) {\n\t\t\t\tif (pack.length != 0) {\n\t\t\t\t\trawHandle(pack);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t} else {\n\t\t\trawHandle(data);\n\t\t}\n\t}\n\n\tpublic void rawHandle(final byte[] data) {\n\n\t\t//load data infile  客户端会发空包 长度为4\n\t\tif (data.length == 4 && data[0] == 0 && data[1] == 0 && data[2] == 0) {\n\t\t\t// load in data空包\n\t\t\tloadDataInfileEnd(data[3]);\n\t\t\treturn;\n\t\t}\n\t\t//修改quit的判断,当load data infile 分隔符为\\001 时可能会出现误判断的bug.\n\t\tif (data.length>4 && data[0] == 1 && data[1] == 0 && data[2]== 0 && data[3] == 0 &&data[4] == MySQLPacket.COM_QUIT) {\n\t\t\tthis.getProcessor().getCommands().doQuit();\n\t\t\tthis.close(\"quit cmd\");\n\t\t\treturn;\n\t\t}\n\t\thandler.handle(data);\n\t}\n\n\tprotected int getServerCapabilities() {\n\t\tint flag = 0;\n\t\tflag |= Capabilities.CLIENT_LONG_PASSWORD;\n\t\tflag |= Capabilities.CLIENT_FOUND_ROWS;\n\t\tflag |= Capabilities.CLIENT_LONG_FLAG;\n\t\tflag |= Capabilities.CLIENT_CONNECT_WITH_DB;\n\t\t// flag |= Capabilities.CLIENT_NO_SCHEMA;\n\t\tboolean usingCompress= MycatServer.getInstance().getConfig().getSystem().getUseCompression()==1 ;\n\t\tif (usingCompress) {\n\t\t\tflag |= Capabilities.CLIENT_COMPRESS;\n\t\t}\n\t\t\n\t\tflag |= Capabilities.CLIENT_ODBC;\n\t\t flag |= Capabilities.CLIENT_LOCAL_FILES;\n\t\tflag |= Capabilities.CLIENT_IGNORE_SPACE;\n\t\tflag |= Capabilities.CLIENT_PROTOCOL_41;\n\t\tflag |= Capabilities.CLIENT_INTERACTIVE;\n\t\t// flag |= Capabilities.CLIENT_SSL;\n\t\tflag |= Capabilities.CLIENT_IGNORE_SIGPIPE;\n\t\tflag |= Capabilities.CLIENT_TRANSACTIONS;\n\t\t// flag |= ServerDefs.CLIENT_RESERVED;\n\t\tflag |= Capabilities.CLIENT_SECURE_CONNECTION;\n        // flag |= Capabilities.CLIENT_MULTI_STATEMENTS;\n        flag |= Capabilities.CLIENT_MULTI_RESULTS;\n        boolean useHandshakeV10 = MycatServer.getInstance().getConfig().getSystem().getUseHandshakeV10() == 1;\n        if(useHandshakeV10) {\n        \tflag |= Capabilities.CLIENT_PLUGIN_AUTH;\n        }\n\t\treturn flag;\n\t}\n\n\tprotected boolean isConnectionReset(Throwable t) {\n\t\tif (t instanceof IOException) {\n\t\t\tString msg = t.getMessage();\n\t\t\treturn (msg != null && msg.contains(\"Connection reset by peer\"));\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new StringBuilder().append(\"[thread=\")\n\t\t\t\t.append(Thread.currentThread().getName()).append(\",class=\")\n\t\t\t\t.append(getClass().getSimpleName()).append(\",id=\").append(id)\n\t\t\t\t.append(\",host=\").append(host).append(\",port=\").append(port)\n\t\t\t\t.append(\",schema=\").append(schema).append(']').toString();\n\t}\n\n\tprivate final static byte[] encodeString(String src, String charset) {\n\t\tif (src == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (charset == null) {\n\t\t\treturn src.getBytes();\n\t\t}\n\t\ttry {\n\t\t\treturn src.getBytes(charset);\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\treturn src.getBytes();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close(String reason) {\n\t\tsuper.close(isAuthenticated ? reason : \"\");\n\t}\n\n\t/**\n\t * 设置是否允许批量执行命令\n\t * @param data\n\t */\n\tpublic void setOption(byte[] data) {\n\t\tCommandPacket command = new CommandPacket();\n\t\tcommand.read(data);\n\t\tint option = 0;\n\t\ttry {\n\t\t\toption = command.arg[0];\n\t\t\tif (option == 0) {\n\t\t\t\tthis.allowMultiStatements = true;\n\t\t\t} else if (option == 1) {\n\t\t\t\tthis.allowMultiStatements = false;\n\t\t\t}\n\t\t\tEOFPacket eof = new EOFPacket();\n\t\t\teof.packetId = 1;\n\t\t\teof.warningCount = option;\n\t\t\twrite(eof.write(allocate(), this, true));\n\t\t} catch (Throwable e) {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"Com Set Option Error\");\n\t\t\treturn;\n\t\t}\n\t}\n  \n    /**\n     * https://dev.mysql.com/doc/dev/mysql-server/8.0.11/page_protocol_com_reset_connection.html\n     * https://dev.mysql.com/doc/refman/5.7/en/mysql-reset-connection.html\n     *\n                  与连接有关的状态受到以下影响：\n        -将回滚所有活动的事务，并重置自动提交模式。           \n        - 所有表锁均已释放。            \n        -会话系统变量将重新初始化为相应的全局系统变量的值，包括由诸如之类的语句隐式设置的系统变量SET NAMES。           \n        -用户变量设置丢失。             \n        -准备好的语句被释放。\n        ........\n         */\n    public void resetConnection() {\n        // 字符集恢复为UTF8\n        setCharset(\"utf8\");\n        write(writeToBuffer(OkPacket.OK, allocate()));\n    }\n\n    public boolean isEnableFlowController() {\n        return enableFlowController;\n    }\n\n    public void setEnableFlowController(boolean enableFlowController) {\n        this.enableFlowController = enableFlowController;\n    }\n\n    public QueueFlowController getFlowController() {\n        return flowController;\n    }\n\n    /**\n    * \n    * 队列流量控制器，防止队列过大内存OOM，功能：\n    * 1）超过最大阀值，关闭NIO读事件，停止从网络读取mysql数据\n    * 2）队列恢复到可继续写的阀值，重启NIO读事件，继续写队列\n    */\n    public class QueueFlowController {\n\n        private volatile boolean readIOStopped; // 读事件的IO是否已经停止\n        private Collection<BackendConnection> relationedBackendConns;// 关联的后端连接\n        private final FrontendConnection frontendConn;\n\n        public QueueFlowController(FrontendConnection c) {\n            this.readIOStopped = false;\n            this.relationedBackendConns = new ArrayList<BackendConnection>();\n            this.frontendConn = c;\n        }\n\n        /**\n         *恢复所有后端连接的读事件\n         */\n        private void recoverIORead() {\n            if (readIOStopped) {\n                synchronized (relationedBackendConns) {\n                    if (readIOStopped) {// 再次判断，防止并发多次执行\n                        readIOStopped = false;\n                        for (final BackendConnection conn : relationedBackendConns) {\n                            conn.enableRead();\n                        }\n                        relationedBackendConns.clear();\n                        LOGGER.info(\"The connection[{}] has removed flow control.\", frontendConn.toString());\n                    }\n                }\n            }\n        }\n\n        private void stopIORead(final Collection<BackendConnection> conns) {\n            if (null != conns && conns.size() > 0) {\n                synchronized (relationedBackendConns) {\n                    if (!readIOStopped) {// 再次判断，防止并发多次执行\n                        readIOStopped = true;\n                        for (BackendConnection conn : conns) {\n                            conn.disableRead();\n                            this.relationedBackendConns.add(conn);\n                        }\n                        LOGGER.info(\"Now the connection[{}]  is under flow control\", frontendConn.toString());\n                    }\n                }\n            }\n        }\n        /**\n         * 检查writeQueue的流量控制阈值\n         * \n         * @param connection\n         */\n        public void check(Map<RouteResultsetNode, BackendConnection> target) {\n            int size = writeQueue.size();\n            if (!readIOStopped && size > writeQueueStopThreshold) {\n                stopIORead(target.values());\n            } else {\n                if (readIOStopped && size <= writeQueueRecoverThreshold) {\n                    recoverIORead();\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/NIOAcceptor.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport java.net.StandardSocketOptions;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\nimport java.nio.channels.ServerSocketChannel;\nimport java.nio.channels.SocketChannel;\nimport java.util.Set;\n\nimport io.mycat.util.SelectorUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.net.factory.FrontendConnectionFactory;\n\n/**\n * @author mycat\n */\npublic final class NIOAcceptor extends Thread implements SocketAcceptor{\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(NIOAcceptor.class);\n\tprivate static final AcceptIdGenerator ID_GENERATOR = new AcceptIdGenerator();\n\n\tprivate final int port;\n\tprivate volatile Selector selector;\n\tprivate final ServerSocketChannel serverChannel;\n\tprivate final FrontendConnectionFactory factory;\n\tprivate long acceptCount;\n\tprivate final NIOReactorPool reactorPool;\n\n\tpublic NIOAcceptor(String name, String bindIp, int port, int backlog,\n\t\t\tFrontendConnectionFactory factory, NIOReactorPool reactorPool)\n\t\t\tthrows IOException {\n\t\tsuper.setName(name);\n\t\tthis.port = port;\n\t\tthis.selector = Selector.open();\n\t\tthis.serverChannel = ServerSocketChannel.open();\n\t\tthis.serverChannel.configureBlocking(false);\n\t\t/** 设置TCP属性 */\n\t\tserverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);\n\t\tserverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 1024 * 16 * 2);\n\n\t\tserverChannel.bind(new InetSocketAddress(bindIp, port), backlog);\n\t\tthis.serverChannel.register(selector, SelectionKey.OP_ACCEPT);\n\t\tthis.factory = factory;\n\t\tthis.reactorPool = reactorPool;\n\t}\n\n\tpublic int getPort() {\n\t\treturn port;\n\t}\n\n\tpublic long getAcceptCount() {\n\t\treturn acceptCount;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tint invalidSelectCount = 0;\n\t\tfor (;;) {\n\t\t\ttry {\n\t\t\t\tfinal Selector tSelector = this.selector;\n\t\t\t\t++acceptCount;\n\n\t\t\t\tlong start = System.nanoTime();\n\t\t\t    tSelector.select(1000L);\n\t\t\t\tlong end = System.nanoTime();\n\t\t\t\tSet<SelectionKey> keys = tSelector.selectedKeys();\n\t\t\t\tif (keys.size() == 0 && (end - start) < SelectorUtil.MIN_SELECT_TIME_IN_NANO_SECONDS )\n\t\t\t\t{\n\t\t\t\t\tinvalidSelectCount++;\n\t\t\t\t}\n\t\t\t\telse\n                {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfor (SelectionKey key : keys) {\n\t\t\t\t\t\t\tif (key.isValid() && key.isAcceptable()) {\n\t\t\t\t\t\t\t\taccept();\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tkey.cancel();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tkeys.clear();\n\t\t\t\t\t\tinvalidSelectCount = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (invalidSelectCount > SelectorUtil.REBUILD_COUNT_THRESHOLD)\n\t\t\t\t{\n\t\t\t\t\tfinal Selector rebuildSelector = SelectorUtil.rebuildSelector(this.selector);\n\t\t\t\t\tif (rebuildSelector != null)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.selector = rebuildSelector;\n\t\t\t\t\t}\n\t\t\t\t\tinvalidSelectCount = 0;\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.warn(getName(), e);\n\t\t\t} catch (final Throwable e) {\n\t\t\t\tLOGGER.warn(\"caught Throwable err: \", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void accept() {\n\t\tSocketChannel channel = null;\n\t\ttry {\n\t\t\tchannel = serverChannel.accept();\n\t\t\tchannel.configureBlocking(false);\n\t\t\tFrontendConnection c = factory.make(channel);\n\t\t\tc.setAccepted(true);\n\t\t\tc.setId(ID_GENERATOR.getId());\n\t\t\tNIOProcessor processor = (NIOProcessor) MycatServer.getInstance()\n\t\t\t\t\t.nextProcessor();\n\t\t\tc.setProcessor(processor);\n\t\t\t\n\t\t\tNIOReactor reactor = reactorPool.getNextReactor();\n\t\t\treactor.postRegister(c);\n\n\t\t} catch (Exception e) {\n\t        LOGGER.warn(getName(), e);\n\t\t\tcloseChannel(channel);\n\t\t}\n\t}\n\n\tprivate static void closeChannel(SocketChannel channel) {\n\t\tif (channel == null) {\n\t\t\treturn;\n\t\t}\n\t\tSocket socket = channel.socket();\n\t\tif (socket != null) {\n\t\t\ttry {\n\t\t\t\tsocket.close();\n\t\t\t} catch (IOException e) {\n\t\t       LOGGER.error(\"closeChannelError\", e);\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tchannel.close();\n\t\t} catch (IOException e) {\n            LOGGER.error(\"closeChannelError\", e);\n\t\t}\n\t}\n\n\t/**\n\t * 前端连接ID生成器\n\t * \n\t * @author mycat\n\t */\n\tprivate static class AcceptIdGenerator {\n\n\t\tprivate static final long MAX_VALUE = 0xffffffffL;\n\n\t\tprivate long acceptId = 0L;\n\t\tprivate final Object lock = new Object();\n\n\t\tprivate long getId() {\n\t\t\tsynchronized (lock) {\n\t\t\t\tif (acceptId >= MAX_VALUE) {\n\t\t\t\t\tacceptId = 0L;\n\t\t\t\t}\n\t\t\t\treturn ++acceptId;\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/NIOConnection.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\n\n/**\n * @author mycat\n */\npublic interface NIOConnection extends ClosableConnection{\n\n    /**\n     * connected \n     */\n    void register() throws IOException;\n\n    /**\n     * 处理数据\n     */\n    void handle(byte[] data);\n\n    /**\n     * 写出一块缓存数据\n     */\n    void write(ByteBuffer buffer);\n    \n     \n     \n}"
  },
  {
    "path": "src/main/java/io/mycat/net/NIOConnector.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\nimport java.nio.channels.SocketChannel;\nimport java.util.Set;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport io.mycat.util.SelectorUtil;\n\n/**\n * @author mycat\n */\npublic final class NIOConnector extends Thread implements SocketConnector {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(NIOConnector.class);\n\tpublic static final ConnectIdGenerator ID_GENERATOR = new ConnectIdGenerator();\n\n\tprivate final String name;\n\tprivate volatile Selector selector;\n\tprivate final BlockingQueue<AbstractConnection> connectQueue;\n\tprivate long connectCount;\n\tprivate final NIOReactorPool reactorPool;\n\n\tpublic NIOConnector(String name, NIOReactorPool reactorPool)\n\t\t\tthrows IOException {\n\t\tsuper.setName(name);\n\t\tthis.name = name;\n\t\tthis.selector = Selector.open();\n\t\tthis.reactorPool = reactorPool;\n\t\tthis.connectQueue = new LinkedBlockingQueue<AbstractConnection>();\n\t}\n\n\tpublic long getConnectCount() {\n\t\treturn connectCount;\n\t}\n\n\tpublic void postConnect(AbstractConnection c) {\n\t\tconnectQueue.offer(c);\n\t\tselector.wakeup();\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tint invalidSelectCount = 0;\n\t\tfor (;;) {\n\t\t\ttry {\n\t\t\t\tfinal Selector tSelector = this.selector;\n\t\t\t\t++connectCount;\n\n\t\t\t\tlong start = System.nanoTime();\n\t\t\t\ttSelector.select(1000L);\n\t\t\t\tlong end = System.nanoTime();\n\t\t\t\tconnect(tSelector);\n\t\t\t\tSet<SelectionKey> keys = tSelector.selectedKeys();\n\t\t\t\tif (keys.size() == 0 && (end - start) < SelectorUtil.MIN_SELECT_TIME_IN_NANO_SECONDS )\n\t\t\t\t{\n\t\t\t\t\tinvalidSelectCount++;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfor (SelectionKey key : keys)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tObject att = key.attachment();\n\t\t\t\t\t\t\tif (att != null && key.isValid() && key.isConnectable())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfinishConnect(key, att);\n\t\t\t\t\t\t\t} else\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tkey.cancel();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidSelectCount = 0;\n\t\t\t\t\t\tkeys.clear();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (invalidSelectCount > SelectorUtil.REBUILD_COUNT_THRESHOLD)\n\t\t\t\t{\n\t\t\t\t\tfinal Selector rebuildSelector = SelectorUtil.rebuildSelector(this.selector);\n\t\t\t\t\tif (rebuildSelector != null)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.selector = rebuildSelector;\n\t\t\t\t\t}\n\t\t\t\t\tinvalidSelectCount = 0;\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.warn(name, e);\n\t\t\t} catch (final Throwable e) {\n\t\t\t\tLOGGER.warn(\"caught Throwable err: \", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void connect(Selector selector) {\n\t\tAbstractConnection c = null;\n\t\twhile ((c = connectQueue.poll()) != null) {\n\t\t\ttry {\n\t\t\t\tSocketChannel channel = (SocketChannel) c.getChannel();\n\t\t\t\tchannel.register(selector, SelectionKey.OP_CONNECT, c);\n\t\t\t\tchannel.connect(new InetSocketAddress(c.host, c.port));\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(\"error:\",e);\n\t\t\t\tc.close(e.toString());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void finishConnect(SelectionKey key, Object att) {\n\t\tBackendAIOConnection c = (BackendAIOConnection) att;\n\t\ttry {\n\t\t\tif (finishConnect(c, (SocketChannel) c.channel)) {\n\t\t\t\tclearSelectionKey(key);\n\t\t\t\tc.setId(ID_GENERATOR.getId());\n\t\t\t\tNIOProcessor processor = MycatServer.getInstance()\n\t\t\t\t\t\t.nextProcessor();\n\t\t\t\tc.setProcessor(processor);\n\t\t\t\tNIOReactor reactor = reactorPool.getNextReactor();\n\t\t\t\treactor.postRegister(c);\n\t\t\t\tc.onConnectfinish();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tclearSelectionKey(key);\n\t\t\tLOGGER.error(\"error:\",e);\n\t\t\tc.close(e.toString());\n\t\t\tc.onConnectFailed(e);\n\n\t\t}\n\t}\n\n\tprivate boolean finishConnect(AbstractConnection c, SocketChannel channel)\n\t\t\tthrows IOException {\n\t\tif (channel.finishConnect()) {\n\t\t\tchannel.finishConnect();\n\n\t\t\tc.setLocalPort(channel.socket().getLocalPort());\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate void clearSelectionKey(SelectionKey key) {\n\t\tif (key.isValid()) {\n\t\t\tkey.attach(null);\n\t\t\tkey.cancel();\n\t\t}\n\t}\n\n\t/**\n\t * 后端连接ID生成器\n\t *\n\t * @author mycat\n\t */\n\tpublic static class ConnectIdGenerator {\n\n\t\tprivate static final long MAX_VALUE = Long.MAX_VALUE;\n\t\tprivate AtomicLong connectId = new AtomicLong(0);\n\n\t\tpublic long getId() {\n\t\t\treturn connectId.incrementAndGet();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/NIOHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\n/**\n * @author mycat\n */\npublic interface NIOHandler {\n\n    void handle(byte[] data);\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/NIOProcessor.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\nimport java.io.IOException;\nimport java.util.Iterator;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport io.mycat.buffer.BufferPool;\n\nimport org.slf4j.Logger; \nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.statistic.CommandCount;\nimport io.mycat.util.NameableExecutor;\nimport io.mycat.util.TimeUtil;\n\n/**\n * @author mycat\n */\npublic final class NIOProcessor {\n\t\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(\"NIOProcessor\");\n\t\n\tprivate final String name;\n\tprivate final BufferPool bufferPool;\n\tprivate final NameableExecutor executor;\n\tprivate final ConcurrentMap<Long, FrontendConnection> frontends;\n\tprivate final ConcurrentMap<Long, BackendConnection> backends;\n\tprivate final CommandCount commands;\n\tprivate long netInBytes;\n\tprivate long netOutBytes;\n\t\n\t// TODO: add by zhuam\n\t// reload @@config_all 后, 老的backends  全部移往 backends_old, 待检测任务进行销毁\n\tpublic final static ConcurrentLinkedQueue<BackendConnection> backends_old = new ConcurrentLinkedQueue<BackendConnection>();\n\n\t//前端已连接数\n\tprivate AtomicInteger frontendsLength = new AtomicInteger(0);\n\n\tpublic NIOProcessor(String name, BufferPool bufferPool,\n\t\t\tNameableExecutor executor) throws IOException {\n\t\tthis.name = name;\n\t\tthis.bufferPool = bufferPool;\n\t\tthis.executor = executor;\n\t\tthis.frontends = new ConcurrentHashMap<Long, FrontendConnection>();\n\t\tthis.backends = new ConcurrentHashMap<Long, BackendConnection>();\n\t\tthis.commands = new CommandCount();\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic BufferPool getBufferPool() {\n\t\treturn bufferPool;\n\t}\n\n\tpublic int getWriteQueueSize() {\n\t\tint total = 0;\n\t\tfor (FrontendConnection fron : frontends.values()) {\n\t\t\ttotal += fron.getWriteQueue().size();\n\t\t}\n\t\tfor (BackendConnection back : backends.values()) {\n\t\t\tif (back instanceof BackendAIOConnection) {\n\t\t\t\ttotal += ((BackendAIOConnection) back).getWriteQueue().size();\n\t\t\t}\n\t\t}\n\t\treturn total;\n\n\t}\n\n\tpublic NameableExecutor getExecutor() {\n\t\treturn this.executor;\n\t}\n\n\tpublic CommandCount getCommands() {\n\t\treturn this.commands;\n\t}\n\n\tpublic long getNetInBytes() {\n\t\treturn this.netInBytes;\n\t}\n\n\tpublic void addNetInBytes(long bytes) {\n\t\tthis.netInBytes += bytes;\n\t}\n\n\tpublic long getNetOutBytes() {\n\t\treturn this.netOutBytes;\n\t}\n\n\tpublic void addNetOutBytes(long bytes) {\n\t\tthis.netOutBytes += bytes;\n\t}\n\n\tpublic void addFrontend(FrontendConnection c) {\n\t\tthis.frontends.put(c.getId(), c);\n\t\tthis.frontendsLength.incrementAndGet();\n\t}\n\n\tpublic ConcurrentMap<Long, FrontendConnection> getFrontends() {\n\t\treturn this.frontends;\n\t}\n\t\n\tpublic int getForntedsLength(){\n\t\treturn this.frontendsLength.get();\n\t}\n\n\tpublic void addBackend(BackendConnection c) {\n\t\tthis.backends.put(c.getId(), c);\n\t}\n\n\tpublic ConcurrentMap<Long, BackendConnection> getBackends() {\n\t\treturn this.backends;\n\t}\n\n\t/**\n\t * 定时执行该方法，回收部分资源。\n\t */\n\tpublic void checkBackendCons() {\n\t\tbackendCheck();\n\t}\n\n\t/**\n\t * 定时执行该方法，回收部分资源。\n\t */\n\tpublic void checkFrontCons() {\n\t\tfrontendCheck();\n\t}\n\n\t// 前端连接检查\n\tprivate void frontendCheck() {\n\t\tIterator<Entry<Long, FrontendConnection>> it = frontends.entrySet()\n\t\t\t\t.iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tFrontendConnection c = it.next().getValue();\n\n\t\t\t// 删除空连接\n\t\t\tif (c == null) {\n\t\t\t\tit.remove();\n\t\t\t\tthis.frontendsLength.decrementAndGet();\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// 清理已关闭连接，否则空闲检查。\n\t\t\tif (c.isClosed()) {\n\t\t\t\t// 此处在高并发情况下会存在并发问题, fixed #1072  极有可能解决了 #700\n\t\t\t\t//c.cleanup();\n\t\t\t\tit.remove();\n\t\t\t\tthis.frontendsLength.decrementAndGet();\n\t\t\t} else {\n\t\t\t\t// very important ,for some data maybe not sent\n\t\t\t\tcheckConSendQueue(c);\n\t\t\t\tc.idleCheck();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void checkConSendQueue(AbstractConnection c) {\n\t\t// very important ,for some data maybe not sent\n\t\tif (!c.writeQueue.isEmpty()) {\n\t\t\tc.getSocketWR().doNextWriteCheck();\n\t\t}\n\n        if (c.isEnableFlowController()) {\n            c.checkQueueFlow();\n        }\n    }\n\n\t// 后端连接检查\n\tprivate void backendCheck() {\n\t\tlong sqlTimeout = MycatServer.getInstance().getConfig().getSystem().getSqlExecuteTimeout() * 1000L;\n\t\tIterator<Entry<Long, BackendConnection>> it = backends.entrySet().iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tBackendConnection c = it.next().getValue();\n\n\t\t\t// 删除空连接\n\t\t\tif (c == null) {\n\t\t\t\tit.remove();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// SQL执行超时的连接关闭\n\t\t\tif (c.isBorrowed() && c.getLastTime() < TimeUtil.currentTimeMillis() - sqlTimeout) {\n\t\t\t\tLOGGER.warn(\"found backend connection SQL timeout ,close it \" + c);\n\t\t\t\tc.close(\"sql timeout\");\n\t\t\t}\n\n\t\t\t// 清理已关闭连接，否则空闲检查。\n\t\t\tif (c.isClosed()) {\n\t\t\t\tit.remove();\n\n\t\t\t} else {\n\t\t\t\t// very important ,for some data maybe not sent\n\t\t\t\tif (c instanceof AbstractConnection) {\n\t\t\t\t\tcheckConSendQueue((AbstractConnection) c);\n\t\t\t\t}\n\t\t\t\tc.idleCheck();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void removeConnection(AbstractConnection con) {\n\t\tif (con instanceof BackendConnection) {\n\t\t\tthis.backends.remove(con.getId());\n\t\t} else {\n\t\t\tthis.frontends.remove(con.getId());\n\t\t\tthis.frontendsLength.decrementAndGet();\n\t\t}\n\n\t}\n\t//jdbc连接用这个释放\n\tpublic void removeConnection(BackendConnection con){\n\t    this.backends.remove(con.getId());\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/NIOReactor.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net;\n\nimport java.io.IOException;\nimport java.nio.channels.CancelledKeyException;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentLinkedQueue;\n\nimport io.mycat.util.SelectorUtil;\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\n/**\n * 网络事件反应器\n * \n * <p>\n * Catch exceptions such as OOM so that the reactor can keep running for response client!\n * </p>\n * @since 2016-03-30\n * \n * @author mycat, Uncle-pan\n * \n */\npublic final class NIOReactor {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(NIOReactor.class);\n\tprivate final String name;\n\tprivate final RW reactorR;\n\n\tpublic NIOReactor(String name) throws IOException {\n\t\tthis.name = name;\n\t\tthis.reactorR = new RW();\n\t}\n\n\tfinal void startup() {\n\t\tnew Thread(reactorR, name + \"-RW\").start();\n\t}\n\n\tfinal void postRegister(AbstractConnection c) {\n\t\treactorR.registerQueue.offer(c);\n\t\treactorR.selector.wakeup();\n\t}\n\n\tfinal Queue<AbstractConnection> getRegisterQueue() {\n\t\treturn reactorR.registerQueue;\n\t}\n\n\tfinal long getReactCount() {\n\t\treturn reactorR.reactCount;\n\t}\n\n\tprivate final class RW implements Runnable {\n\t\tprivate volatile Selector selector;\n\t\tprivate final ConcurrentLinkedQueue<AbstractConnection> registerQueue;\n\t\tprivate long reactCount;\n\n\t\tprivate RW() throws IOException {\n\t\t\tthis.selector = Selector.open();\n\t\t\tthis.registerQueue = new ConcurrentLinkedQueue<AbstractConnection>();\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tint invalidSelectCount = 0;\n\t\t\tSet<SelectionKey> keys = null;\n\t\t\tfor (;;) {\n\t\t\t\t++reactCount;\n\t\t\t\ttry {\n\t\t\t\t\tfinal Selector tSelector = this.selector;\n\t\t\t\t\tlong start = System.nanoTime();\n\t\t\t\t\ttSelector.select(500L);\n\t\t\t\t\tlong end = System.nanoTime();\n\t\t\t\t\tregister(tSelector);\n\t\t\t\t\tkeys = tSelector.selectedKeys();\n\t\t\t\t\tif (keys.size() == 0 && (end - start) < SelectorUtil.MIN_SELECT_TIME_IN_NANO_SECONDS )\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidSelectCount++;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tinvalidSelectCount = 0;\n\t\t\t\t\t\tfor (SelectionKey key : keys) {\n\t\t\t\t\t\t\tAbstractConnection con = null;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tObject att = key.attachment();\n\t\t\t\t\t\t\t\tif (att != null) {\n\t\t\t\t\t\t\t\t\tcon = (AbstractConnection) att;\n\t\t\t\t\t\t\t\t\tif (key.isValid() && key.isReadable()) {\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tcon.asynRead();\n\t\t\t\t\t\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t\t\t\t\t\tcon.close(\"program err:\" + e.toString());\n\t\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\t\t\tLOGGER.warn(\"caught err:\", e);\n\t\t\t\t\t\t\t\t\t\t\tcon.close(\"program err:\" + e.toString());\n\t\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (key.isValid() && key.isWritable()) {\n\t\t\t\t\t\t\t\t\t\tcon.doNextWriteCheck();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tkey.cancel();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch (CancelledKeyException e) {\n\t\t\t\t\t\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\t\t\t\t\t\tLOGGER.debug(con + \" socket key canceled\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tLOGGER.warn(con + \" \" + e);\n\t\t\t\t\t\t\t} catch (final Throwable e) {\n\t\t\t\t\t\t\t\t// Catch exceptions such as OOM and close connection if exists\n\t\t\t\t\t\t\t\t//so that the reactor can keep running!\n\t\t\t\t\t\t\t\t// @author Uncle-pan\n\t\t\t\t\t\t\t\t// @since 2016-03-30\n\t\t\t\t\t\t\t\tif (con != null) {\n\t\t\t\t\t\t\t\t\tcon.close(\"Bad: \" + e);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tLOGGER.error(\"caught err: \", e);\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (invalidSelectCount > SelectorUtil.REBUILD_COUNT_THRESHOLD)\n\t\t\t\t\t{\n\t\t\t\t\t\tfinal Selector rebuildSelector = SelectorUtil.rebuildSelector(this.selector);\n\t\t\t\t\t\tif (rebuildSelector != null)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthis.selector = rebuildSelector;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tinvalidSelectCount = 0;\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOGGER.warn(name, e);\n\t\t\t\t} catch (final Throwable e){\n\t\t\t\t\t// Catch exceptions such as OOM so that the reactor can keep running!\n                \t// @author Uncle-pan\n                \t// @since 2016-03-30\n\t\t\t\t\tLOGGER.error(\"caught err: \", e);\n\t\t\t\t} finally {\n\t\t\t\t\tif (keys != null) {\n\t\t\t\t\t\tkeys.clear();\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void register(Selector selector) {\n\t\t\tAbstractConnection c = null;\n\t\t\tif (registerQueue.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\twhile ((c = registerQueue.poll()) != null) {\n\t\t\t\ttry {\n\t\t\t\t\t((NIOSocketWR) c.getSocketWR()).register(selector);\n\t\t\t\t\tc.register();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tc.close(\"register err\" + e.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/NIOReactorPool.java",
    "content": "package io.mycat.net;\n\nimport java.io.IOException;\n\npublic class NIOReactorPool {\n\tprivate final NIOReactor[] reactors;\n\tprivate volatile int nextReactor;\n\n\tpublic NIOReactorPool(String name, int poolSize) throws IOException {\n\t\treactors = new NIOReactor[poolSize];\n\t\tfor (int i = 0; i < poolSize; i++) {\n\t\t\tNIOReactor reactor = new NIOReactor(name + \"-\" + i);\n\t\t\treactors[i] = reactor;\n\t\t\treactor.startup();\n\t\t}\n\t}\n\n\tpublic NIOReactor getNextReactor() {\n//\t\tif (++nextReactor == reactors.length) {\n//\t\t\tnextReactor = 0;\n//\t\t}\n//\t\treturn reactors[nextReactor];\n\n        int i = ++nextReactor;\n        if (i >= reactors.length) {\n            i=nextReactor = 0;\n        }\n        return reactors[i];\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/NIOSocketWR.java",
    "content": "package io.mycat.net;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\nimport java.nio.channels.SocketChannel;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport io.mycat.util.TimeUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class NIOSocketWR extends SocketWR {\n\tprivate SelectionKey processKey;\n\tprivate static final int OP_NOT_READ = ~SelectionKey.OP_READ;\n\tprivate static final int OP_NOT_WRITE = ~SelectionKey.OP_WRITE;\n\tprivate final AbstractConnection con;\n\tprivate final SocketChannel channel;\n\tprivate final AtomicBoolean writing = new AtomicBoolean(false);\n\tprotected static final Logger LOGGER = LoggerFactory.getLogger(NIOSocketWR.class);\n\tpublic static final ByteBuffer EMPTY_BYTEBUFFER  = ByteBuffer.allocate(1);\n\tpublic NIOSocketWR(AbstractConnection con) {\n\t\tthis.con = con;\n\t\tthis.channel = (SocketChannel) con.channel;\n\t}\n\n\tpublic void register(Selector selector) throws IOException {\n\t\ttry {\n\t\t\tprocessKey = channel.register(selector, SelectionKey.OP_READ, con);\n\t\t} finally {\n\t\t\tif (con.isClosed.get()) {\n\t\t\t\tclearSelectionKey();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void doNextWriteCheck() {\n\n\t\tif (!writing.compareAndSet(false, true)) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tif(!channel.isOpen()){\n\t\t\t\tAbstractConnection.LOGGER.debug(\"caught err: {}\", con);\n\t\t\t}\n\t\t\tboolean noMoreData = write0();\n\t\t\twriting.set(false);\n\t\t\tif (noMoreData && con.writeQueue.isEmpty()) {\n\t\t\t\tif ((processKey.isValid() && (processKey.interestOps() & SelectionKey.OP_WRITE) != 0)) {\n\t\t\t\t\tdisableWrite();\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tif ((processKey.isValid() && (processKey.interestOps() & SelectionKey.OP_WRITE) == 0)) {\n\t\t\t\t\tenableWrite(false);\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (IOException e) {\n\t\t\tif (AbstractConnection.LOGGER.isWarnEnabled()) {\n\t\t\t\tAbstractConnection.LOGGER.warn(\"caught err:\", e);\n\t\t\t}\n\t\t\tcon.close(\"err:\" + e);\n\t\t} finally {\n\t\t\twriting.set(false);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic boolean checkAlive() {\n\t\ttry {\n\t\t\treturn \tchannel.read(EMPTY_BYTEBUFFER) == 0;\n\t\t} catch (IOException e) {\n\t\t\tLOGGER.error(\"\",e);\n\t\t\treturn false;\n\t\t}finally {\n\t\t\tEMPTY_BYTEBUFFER.position(0);\n\t\t}\n\t}\n\n\tprivate boolean write0() throws IOException {\n\n\t\tint written = 0;\n\t\tByteBuffer buffer = con.writeBuffer;\n\t\tif (buffer != null) {\n\t\t\twhile (buffer.hasRemaining()) {\n\t\t\t\twritten = channel.write(buffer);\n\t\t\t\tif (written > 0) {\n\t\t\t\t\tcon.netOutBytes += written;\n\t\t\t\t\tcon.processor.addNetOutBytes(written);\n\t\t\t\t\tcon.lastWriteTime = TimeUtil.currentTimeMillis();\n\t\t\t\t} else {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (buffer.hasRemaining()) {\n\t\t\t\tcon.writeAttempts++;\n\t\t\t\treturn false;\n\t\t\t} else {\n\t\t\t\tcon.writeBuffer = null;\n\t\t\t\tcon.recycle(buffer);\n\t\t\t}\n\t\t}\n\t\twhile ((buffer = con.writeQueue.poll()) != null) {\n\t\t\tif (buffer.limit() == 0) {\n\t\t\t\tcon.recycle(buffer);\n\t\t\t\tcon.close(\"quit send\");\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tbuffer.flip();\n\t\t\ttry {\n\t\t\t\twhile (buffer.hasRemaining()) {\n\t\t\t\t\twritten = channel.write(buffer);// java.io.IOException:\n\t\t\t\t\t\t\t\t\t// Connection reset by peer\n\t\t\t\t\tif (written > 0) {\n\t\t\t\t\t\tcon.lastWriteTime = TimeUtil.currentTimeMillis();\n\t\t\t\t\t\tcon.netOutBytes += written;\n\t\t\t\t\t\tcon.processor.addNetOutBytes(written);\n\t\t\t\t\t\tcon.lastWriteTime = TimeUtil.currentTimeMillis();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\tcon.recycle(buffer);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t\tif (buffer.hasRemaining()) {\n\t\t\t\tcon.writeBuffer = buffer;\n\t\t\t\tcon.writeAttempts++;\n\t\t\t\treturn false;\n\t\t\t} else {\n\t\t\t\tcon.recycle(buffer);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void disableWrite() {\n\t\ttry {\n\t\t\tSelectionKey key = this.processKey;\n\t\t\tkey.interestOps(key.interestOps() & OP_NOT_WRITE);\n\t\t} catch (Exception e) {\n\t\t\tAbstractConnection.LOGGER.warn(\"can't disable write \" + e + \" con \"\n\t\t\t\t\t+ con);\n\t\t}\n\n\t}\n\n\tprivate void enableWrite(boolean wakeup) {\n\t\tboolean needWakeup = false;\n\t\ttry {\n\t\t\tSelectionKey key = this.processKey;\n\t\t\tkey.interestOps(key.interestOps() | SelectionKey.OP_WRITE);\n\t\t\tneedWakeup = true;\n\t\t} catch (Exception e) {\n\t\t\tAbstractConnection.LOGGER.warn(\"can't enable write \" + e);\n\n\t\t}\n\t\tif (needWakeup && wakeup) {\n\t\t\tprocessKey.selector().wakeup();\n\t\t}\n\t}\n\n\tpublic void disableRead() {\n\n\t\tSelectionKey key = this.processKey;\n\t\tkey.interestOps(key.interestOps() & OP_NOT_READ);\n\t}\n\n\tpublic void enableRead() {\n\n\t\tboolean needWakeup = false;\n\t\ttry {\n\t\t\tSelectionKey key = this.processKey;\n\t\t\tkey.interestOps(key.interestOps() | SelectionKey.OP_READ);\n\t\t\tneedWakeup = true;\n\t\t} catch (Exception e) {\n\t\t\tAbstractConnection.LOGGER.warn(\"enable read fail \" + e);\n\t\t}\n\t\tif (needWakeup) {\n\t\t\tprocessKey.selector().wakeup();\n\t\t}\n\t}\n\n\tprivate void clearSelectionKey() {\n\t\ttry {\n\t\t\tSelectionKey key = this.processKey;\n\t\t\tif (key != null && key.isValid()) {\n\t\t\t\tkey.attach(null);\n\t\t\t\tkey.cancel();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tAbstractConnection.LOGGER.warn(\"clear selector keys err:\" + e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void asynRead() throws IOException {\n\t\tByteBuffer theBuffer = con.readBuffer;\n\t\tif (theBuffer == null) {\n\n\t\t\ttheBuffer = con.processor.getBufferPool().allocate(con.processor.getBufferPool().getChunkSize());\n\n\t\t\tcon.readBuffer = theBuffer;\n\t\t}\n\n\t\tint got = channel.read(theBuffer);\n\n\t\tcon.onReadData(got);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/SocketAcceptor.java",
    "content": "package io.mycat.net;\n\npublic interface SocketAcceptor {\n\n\tvoid start();\n\n\tString getName();\n\n\tint getPort();\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/SocketConnector.java",
    "content": "package io.mycat.net;\n\npublic interface SocketConnector {\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/SocketWR.java",
    "content": "package io.mycat.net;\n\nimport java.io.IOException;\n\n\npublic abstract class SocketWR {\n\tpublic abstract void asynRead() throws IOException;\n\tpublic abstract void doNextWriteCheck() ;\n\tpublic abstract boolean checkAlive();\n    public abstract void disableRead();\n    public abstract void enableRead();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/WriteEventCheckRunner.java",
    "content": "package io.mycat.net;\n\npublic class WriteEventCheckRunner implements Runnable {\n\tprivate final SocketWR socketWR;\n\tprivate volatile boolean finshed = true;\n\n\tpublic WriteEventCheckRunner(SocketWR socketWR) {\n\t\tthis.socketWR = socketWR;\n\t}\n\n\tpublic boolean isFinished() {\n\t\treturn finshed;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tfinshed = false;\n\t\tsocketWR.doNextWriteCheck();\n\t\tfinshed = true;\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/factory/BackendConnectionFactory.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.factory;\n\nimport java.io.IOException;\nimport java.nio.channels.AsynchronousSocketChannel;\nimport java.nio.channels.NetworkChannel;\nimport java.nio.channels.SocketChannel;\n\nimport io.mycat.MycatServer;\n\n/**\n * @author mycat\n */\npublic abstract class BackendConnectionFactory {\n\n\tprotected NetworkChannel openSocketChannel(boolean isAIO)\n\t\t\tthrows IOException {\n\t\tif (isAIO) {\n\t\t\treturn AsynchronousSocketChannel\n                .open(MycatServer.getInstance().getNextAsyncChannelGroup());\n\t\t} else {\n\t\t\tSocketChannel channel = null;\n\t\t\tchannel = SocketChannel.open();\n\t\t\tchannel.configureBlocking(false);\n\t\t\treturn channel;\n\t\t}\n\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/factory/FrontendConnectionFactory.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.factory;\n\nimport java.io.IOException;\nimport java.net.StandardSocketOptions;\nimport java.nio.channels.NetworkChannel;\n\nimport io.mycat.MycatServer;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * @author mycat\n */\npublic abstract class FrontendConnectionFactory {\n\tprotected abstract FrontendConnection getConnection(NetworkChannel channel)\n\t\t\tthrows IOException;\n\n\tpublic FrontendConnection make(NetworkChannel channel) throws IOException {\n\t\tchannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);\n\t\tchannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);\n\n\t\tFrontendConnection c = getConnection(channel);\n\t\tMycatServer.getInstance().getConfig().setSocketParams(c, true);\n\t\treturn c;\n\t}\n\n\t\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/handler/BackendAsyncHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.handler;\n\nimport java.util.concurrent.Executor;\n\nimport io.mycat.net.NIOHandler;\n\n/**\n * @author mycat\n */\npublic abstract class BackendAsyncHandler implements NIOHandler {\n\n\tprotected void offerData(byte[] data, Executor executor) {\n\t\thandleData(data);\n\n\t\t// if (dataQueue.offer(data)) {\n\t\t// handleQueue(executor);\n\t\t// } else {\n\t\t// offerDataError();\n\t\t// }\n\t}\n\n\tprotected abstract void offerDataError();\n\n\tprotected abstract void handleData(byte[] data);\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/handler/FrontendAuthenticator.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.net.handler;\r\n\r\nimport java.nio.ByteBuffer;\r\nimport java.security.NoSuchAlgorithmException;\r\nimport java.util.Iterator;\r\nimport java.util.Map;\r\nimport java.util.Set;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport io.mycat.MycatServer;\r\nimport io.mycat.backend.mysql.SecurityUtil;\r\nimport io.mycat.config.Capabilities;\r\nimport io.mycat.config.ErrorCode;\r\nimport io.mycat.config.model.UserConfig;\r\nimport io.mycat.net.FrontendConnection;\r\nimport io.mycat.net.NIOHandler;\r\nimport io.mycat.net.NIOProcessor;\r\nimport io.mycat.net.mysql.AuthPacket;\r\nimport io.mycat.net.mysql.AuthSwitchPacket;\r\nimport io.mycat.net.mysql.MySQLPacket;\r\nimport io.mycat.net.mysql.QuitPacket;\r\n\r\n/**\r\n * 前端认证处理器\r\n * \r\n * @author mycat\r\n */\r\npublic class FrontendAuthenticator implements NIOHandler {\r\n\t\r\n    private static final Logger LOGGER = LoggerFactory.getLogger(FrontendAuthenticator.class);\r\n    private byte[] AUTH_OK = new byte[] { 7, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0 };\r\n    private static final String DEFAULT_AUTH_PLUGIN_NAME = \"mysql_native_password\";\r\n    \r\n    protected final FrontendConnection source;\r\n    \r\n    private AuthPacket auth;\r\n\r\n    public FrontendAuthenticator(FrontendConnection source) {\r\n        this.source = source;\r\n    }\r\n\r\n    @Override\r\n    public void handle(byte[] data) {\r\n        // check quit packet\r\n        if (data.length == QuitPacket.QUIT.length && data[4] == MySQLPacket.COM_QUIT) {\r\n            source.close(\"quit packet\");\r\n            return;\r\n        }\r\n\r\n\r\n        if(auth == null){\r\n            auth = new AuthPacket();\r\n            auth.read(data);\r\n\r\n            if (auth.clientAuthPlugin != null && !DEFAULT_AUTH_PLUGIN_NAME.equals(auth.clientAuthPlugin)) {\r\n                AuthSwitchPacket authSwitch = new AuthSwitchPacket(DEFAULT_AUTH_PLUGIN_NAME.getBytes(), source.getSeed());\r\n                authSwitch.packetId = 2;\r\n                authSwitch.write(this.source);\r\n                return;\r\n            }\r\n        }else{\r\n            AuthSwitchPacket authSwitch = new AuthSwitchPacket();\r\n            authSwitch.read(data);\r\n            auth.password = authSwitch.getAuthMethodData();\r\n            AUTH_OK = new byte[] { 7, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0 };\r\n        }\r\n\r\n        \r\n\r\n        //huangyiming add\r\n        int nopassWordLogin = MycatServer.getInstance().getConfig().getSystem().getNonePasswordLogin();\r\n        //如果无密码登陆则跳过密码验证这个步骤\r\n        boolean skipPassWord = false;\r\n        String defaultUser = \"\";\r\n        if(nopassWordLogin == 1){\r\n        \tskipPassWord = true;\r\n        \tMap<String, UserConfig> userMaps =  MycatServer.getInstance().getConfig().getUsers();\r\n        \tif(!userMaps.isEmpty()){\r\n        \t\tsetDefaultAccount(auth, userMaps);\r\n        \t}\r\n        }\r\n        // check user\r\n        if (!checkUser(auth.user, source.getHost())) {\r\n        \tfailure(ErrorCode.ER_ACCESS_DENIED_ERROR, \"Access denied for user '\" + auth.user + \"' with host '\" + source.getHost()+ \"'\");\r\n        \treturn;\r\n        }\r\n        // check password\r\n        if (!skipPassWord && !checkPassword(auth.password, auth.user)) {\r\n        \tfailure(ErrorCode.ER_ACCESS_DENIED_ERROR, \"Access denied for user '\" + auth.user + \"', because password is error \");\r\n        \treturn;\r\n        }\r\n        \r\n        // check degrade\r\n        if ( isDegrade( auth.user ) ) {\r\n        \t failure(ErrorCode.ER_ACCESS_DENIED_ERROR, \"Access denied for user '\" + auth.user + \"', because service be degraded \");\r\n             return;\r\n        }\r\n        \r\n        // check schema\r\n        switch (checkSchema(auth.database, auth.user)) {\r\n        case ErrorCode.ER_BAD_DB_ERROR:\r\n            failure(ErrorCode.ER_BAD_DB_ERROR, \"Unknown database '\" + auth.database + \"'\");\r\n            break;\r\n        case ErrorCode.ER_DBACCESS_DENIED_ERROR:\r\n            String s = \"Access denied for user '\" + auth.user + \"' to database '\" + auth.database + \"'\";\r\n            failure(ErrorCode.ER_DBACCESS_DENIED_ERROR, s);\r\n            break;\r\n        default:\r\n            success(auth);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * 设置了无密码登陆的情况下把客户端传过来的用户账号改变为默认账户\r\n     * @param auth\r\n     * @param userMaps\r\n     */\r\n\tprivate void setDefaultAccount(AuthPacket auth, Map<String, UserConfig> userMaps) {\r\n\t\tString defaultUser;\r\n\t\tIterator<UserConfig> items = userMaps.values().iterator();\r\n\t\twhile(items.hasNext()){\r\n\t\t\tUserConfig userConfig = items.next();\r\n\t\t\tif(userConfig.isDefaultAccount()){\r\n\t\t\t\tdefaultUser = userConfig.getName(); \r\n\t\t\t\tauth.user = defaultUser;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n    \r\n    //TODO: add by zhuam\r\n    //前端 connection 达到该用户设定的阀值后, 立马降级拒绝连接\r\n    protected boolean isDegrade(String user) {\r\n    \t\r\n    \tint benchmark = source.getPrivileges().getBenchmark(user);\r\n    \tif ( benchmark > 0 ) {\r\n    \t\r\n\t    \tint forntedsLength = 0;\r\n\t    \tNIOProcessor[] processors = MycatServer.getInstance().getProcessors();\r\n\t\t\tfor (NIOProcessor p : processors) {\r\n\t\t\t\tforntedsLength += p.getForntedsLength();\r\n\t\t\t}\r\n\t\t\r\n\t\t\tif ( forntedsLength >= benchmark ) {\t\t\t\t\t\t\t\r\n\t\t\t\treturn true;\r\n\t\t\t}\t\t\t\r\n    \t}\r\n\t\t\r\n\t\treturn false;\r\n    }\r\n    \r\n    protected boolean checkUser(String user, String host) {\r\n        return source.getPrivileges().userExists(user, host);\r\n    }\r\n\r\n    protected boolean checkPassword(byte[] password, String user) {\r\n        String pass = source.getPrivileges().getPassword(user);\r\n\r\n        // check null\r\n        if (pass == null || pass.length() == 0) {\r\n            if (password == null || password.length == 0) {\r\n                return true;\r\n            } else {\r\n                return false;\r\n            }\r\n        }\r\n        if (password == null || password.length == 0) {\r\n            return false;\r\n        }\r\n\r\n        // encrypt\r\n        byte[] encryptPass = null;\r\n        try {\r\n            encryptPass = SecurityUtil.scramble411(pass.getBytes(), source.getSeed());\r\n        } catch (NoSuchAlgorithmException e) {\r\n            LOGGER.warn(source.toString(), e);\r\n            return false;\r\n        }\r\n        if (encryptPass != null && (encryptPass.length == password.length)) {\r\n            int i = encryptPass.length;\r\n            while (i-- != 0) {\r\n                if (encryptPass[i] != password[i]) {\r\n                    return false;\r\n                }\r\n            }\r\n        } else {\r\n            return false;\r\n        }\r\n\r\n        return true;\r\n    }\r\n\r\n    protected int checkSchema(String schema, String user) {\r\n        if (schema == null) {\r\n            return 0;\r\n        }\r\n        FrontendPrivileges privileges = source.getPrivileges();\r\n        if (!privileges.schemaExists(schema)) {\r\n            return ErrorCode.ER_BAD_DB_ERROR;\r\n        }\r\n        Set<String> schemas = privileges.getUserSchemas(user);\r\n        if (schemas == null || schemas.size() == 0 || schemas.contains(schema)) {\r\n            return 0;\r\n        } else {\r\n            return ErrorCode.ER_DBACCESS_DENIED_ERROR;\r\n        }\r\n    }\r\n\r\n    protected void success(AuthPacket auth) {\r\n        source.setAuthenticated(true);\r\n        source.setUser(auth.user);\r\n        source.setSchema(auth.database);\r\n        source.setCharsetIndex(auth.charsetIndex);\r\n        if (auth.allowMultiStatements) {\r\n            /**\r\n             * #2589 url里面包含allowMultiQueries=true，allowMultiStatements这个参数会在握手协议时候返回\r\n             * mysql命令行客户端或navicat都会把这个参数设置为true，表示它会发送”multiple statements “\r\n             * 参考https://dev.mysql.com/doc/internals/en/capability-flags.html。\r\n             * 因为mycat目前不能支持这个特性，只能先日志记录下，便于以后排查问题\r\n             **/\r\n            String warnMsg = \"Mycat does not support the allowMultiQueries value to be true, maybe occur some error.Client connection is[{}]\";\r\n            if (LOGGER.isInfoEnabled()) {\r\n                LOGGER.info(warnMsg, this);\r\n            }\r\n            // failure(ErrorCode.ER_HANDSHAKE_ERROR, errMsg);\r\n            // return;\r\n        }\r\n\r\n        source.setHandler(new FrontendCommandHandler(source));\r\n\r\n        if (LOGGER.isInfoEnabled()) {\r\n            StringBuilder s = new StringBuilder();\r\n            s.append(source).append('\\'').append(auth.user).append(\"' login success\");\r\n            byte[] extra = auth.extra;\r\n            if (extra != null && extra.length > 0) {\r\n                s.append(\",extra:\").append(new String(extra));\r\n            }\r\n            LOGGER.info(s.toString());\r\n        }\r\n\r\n        ByteBuffer buffer = source.allocate();\r\n        source.write(source.writeToBuffer(AUTH_OK, buffer));\r\n        boolean clientCompress = Capabilities.CLIENT_COMPRESS==(Capabilities.CLIENT_COMPRESS & auth.clientFlags);\r\n        boolean usingCompress= MycatServer.getInstance().getConfig().getSystem().getUseCompression()==1 ;\r\n        if(clientCompress&&usingCompress)\r\n        {\r\n            source.setSupportCompress(true);\r\n        }\r\n    }\r\n\r\n    protected void failure(int errno, String info) {\r\n        LOGGER.error(source.toString() + info);\r\n        source.writeErrMessage((byte) 2, errno, info);\r\n    }\r\n\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/handler/FrontendCommandHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.handler;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.net.NIOHandler;\nimport io.mycat.net.mysql.MySQLPacket;\nimport io.mycat.statistic.CommandCount;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 前端命令处理器\n *\n * @author mycat\n */\npublic class FrontendCommandHandler implements NIOHandler\n{\n    private static final Logger LOGGER = LoggerFactory.getLogger(FrontendCommandHandler.class);\n\n    protected final FrontendConnection source;\n    protected final CommandCount commands;\n\n    public FrontendCommandHandler(FrontendConnection source)\n    {\n        this.source = source;\n        this.commands = source.getProcessor().getCommands();\n    }\n\n    @Override\n    public void handle(byte[] data)\n    {\n        if(source.getLoadDataInfileHandler()!=null&&source.getLoadDataInfileHandler().isStartLoadData())\n        {\n            MySQLMessage mm = new MySQLMessage(data);\n            int  packetLength = mm.readUB3();\n            if(packetLength+4==data.length)\n            {\n                source.loadDataInfileData(data);\n            }\n            return;\n        }\n        switch (data[4])\n        {\n            case MySQLPacket.COM_INIT_DB:\n                commands.doInitDB();\n                source.initDB(data);\n                break;\n            case MySQLPacket.COM_QUERY:\n                commands.doQuery();\n                source.query(data);\n                break;\n            case MySQLPacket.COM_PING:\n                commands.doPing();\n                source.ping();\n                break;\n            case MySQLPacket.COM_QUIT:\n                commands.doQuit();\n                source.close(\"quit cmd\");\n                break;\n            case MySQLPacket.COM_PROCESS_KILL:\n                commands.doKill();\n                source.kill(data);\n                break;\n            case MySQLPacket.COM_STMT_PREPARE:\n                commands.doStmtPrepare();\n                source.stmtPrepare(data);\n                break;\n            case MySQLPacket.COM_STMT_SEND_LONG_DATA:\n            \tcommands.doStmtSendLongData();\n            \tsource.stmtSendLongData(data);\n            \tbreak;\n            case MySQLPacket.COM_STMT_RESET:\n            \tcommands.doStmtReset();\n            \tsource.stmtReset(data);\n            \tbreak;\n            case MySQLPacket.COM_STMT_EXECUTE:\n                commands.doStmtExecute();\n                source.stmtExecute(data);\n                break;\n            case MySQLPacket.COM_STMT_CLOSE:\n                commands.doStmtClose();\n                source.stmtClose(data);\n                break;\n            case MySQLPacket.COM_HEARTBEAT:\n                commands.doHeartbeat();\n                source.heartbeat(data);\n                break;\n            case MySQLPacket.COM_FIELD_LIST:\n                source.fieldList(data);\n                break;\n            case MySQLPacket.COM_SET_OPTION:\n                commands.doSetOption();\n                source.setOption(data);\n                break;\n            case MySQLPacket.COM_RESET_CONNECTION:\n                source.resetConnection();\n                break;\n            default:\n                commands.doOther();\n                MycatConfig config = MycatServer.getInstance().getConfig();\n                if( config.getSystem().getIgnoreUnknownCommand()==1){\n                    LOGGER.warn(\"Unknown command:{}\",data[4]);\n                    source.ping();\n                }else {\n                    LOGGER.error(\"Unknown command:{}\",new String(data));\n                    source.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR,\n                            \"Unknown command\");\n                }\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/handler/FrontendPrepareHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.handler;\n\n/**\n * SQL预处理处理器\n * \n * @author mycat, CrazyPig\n */\npublic interface FrontendPrepareHandler {\n    \n    void prepare(String sql);\n    \n    void sendLongData(byte[] data);\n\n    void reset(byte[] data);\n    \n    void execute(byte[] data);\n\n    void close(byte[] data);\n\n    void clear();\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/handler/FrontendPrivileges.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.handler;\n\nimport java.util.Set;\n\n/**\n * 权限提供者\n * \n * @author mycat\n */\npublic interface FrontendPrivileges {\n\n    /**\n     * 检查schema是否存在\n     */\n    boolean schemaExists(String schema);\n\n    /**\n     * 检查用户是否存在，并且可以使用host实行隔离策略。\n     */\n    boolean userExists(String user, String host);\n\n    /**\n     * 提供用户的服务器端密码\n     */\n    String getPassword(String user);\n\n    /**\n     * 提供有效的用户schema集合\n     */\n    Set<String> getUserSchemas(String user);\n    \n    /**\n     * 检查用户是否为只读权限\n     * @param user\n     * @return\n     */\n    Boolean isReadOnly(String user);\n    \n    /**\n     * 获取设定的系统最大连接数的降级阀值\n     * @param user\n     * @return\n     */\n    int getBenchmark(String user);\n    \n    \n    /**\n     * 检查防火墙策略\n     * （白名单策略）\n     * @param user\n     * @param host\n     * @return\n     */\n    boolean checkFirewallWhiteHostPolicy(String user, String host);\n    \n    /**\n     * 检查防火墙策略\n     * (SQL黑名单及注入策略)\n     * @param sql\n     * @return\n     */\n    boolean checkFirewallSQLPolicy(String user, String sql);\n    \n    \n    /**\n     * 检查 SQL 语句的 DML 权限\n     * @return\n     */\n    boolean checkDmlPrivilege(String user, String schema, String sql);\n\n    /**\n     * 检查针对 DataNode 的 SQL 语句的 DML 权限\n     * @param user\n     * @param dataNode\n     * @param sql\n     * @return\n     */\n    boolean checkDataNodeDmlPrivilege(String user, String dataNode, String sql);\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/handler/FrontendQueryHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.handler;\n\n/**\n * 查询处理器\n * \n * @author mycat\n */\npublic interface FrontendQueryHandler {\n\n\tvoid query(String sql);\n\n\tvoid setReadOnly(Boolean readOnly);\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/handler/LoadDataInfileHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.handler;\n\n/**\n * load data infile\n * \n * @author magicdoom\n */\npublic interface LoadDataInfileHandler\n{\n\n    void start(String sql);\n\n    void handle(byte[] data);\n\n    void end(byte packID);\n\n    void clear();\n\n    byte getLastPackId();\n\n    boolean isStartLoadData();\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/AuthPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.backend.mysql.StreamUtil;\nimport io.mycat.config.Capabilities;\nimport io.mycat.net.BackendAIOConnection;\n\n/**\n * From client to server during initial handshake.\n * \n * <pre>\n * Bytes                        Name\n * -----                        ----\n * 4                            client_flags\n * 4                            max_packet_size\n * 1                            charset_number\n * 23                           (filler) always 0x00...\n * n (Null-Terminated String)   user\n * n (Length Coded Binary)      scramble_buff (1 + x bytes)\n * n (Null-Terminated String)   databasename (optional)\n * \n * @see http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Client_Authentication_Packet\n * </pre>\n * \n * @author mycat\n */\npublic class AuthPacket extends MySQLPacket {\n    private static final byte[] FILLER = new byte[23];\n\n    public long clientFlags;\n    public long maxPacketSize;\n    public int charsetIndex;\n    public byte[] extra;// from FILLER(23)\n    public String user;\n    public byte[] password;\n    public String database;\n    public boolean allowMultiStatements;\n    public String clientAuthPlugin;\n\n    public void read(byte[] data) {\n        MySQLMessage mm = new MySQLMessage(data);\n        packetLength = mm.readUB3();\n        packetId = mm.read();\n        clientFlags = mm.readUB4();\n        maxPacketSize = mm.readUB4();\n        charsetIndex = (mm.read() & 0xff);\n        // read extra\n        int current = mm.position();\n        int len = (int) mm.readLength();\n        if (len > 0 && len < FILLER.length) {\n            byte[] ab = new byte[len];\n            System.arraycopy(mm.bytes(), mm.position(), ab, 0, len);\n            this.extra = ab;\n        }\n        mm.position(current + FILLER.length);\n        user = mm.readStringWithNull();\n        password = mm.readBytesWithLength();\n        if (((clientFlags & Capabilities.CLIENT_CONNECT_WITH_DB) != 0) && mm.hasRemaining()) {\n            database = mm.readStringWithNull();\n        }\n\n        if ((clientFlags & Capabilities.CLIENT_MULTI_STATEMENTS) != 0) {\n            allowMultiStatements = true;\n        }\n\n        if (((clientFlags & Capabilities.CLIENT_PLUGIN_AUTH) != 0) && mm.hasRemaining()) {\n            clientAuthPlugin = mm.readStringWithNull();\n        }\n    }\n\n    public void write(OutputStream out) throws IOException {\n        StreamUtil.writeUB3(out, calcPacketSize());\n        StreamUtil.write(out, packetId);\n        StreamUtil.writeUB4(out, clientFlags);\n        StreamUtil.writeUB4(out, maxPacketSize);\n        StreamUtil.write(out, (byte) charsetIndex);\n        out.write(FILLER);\n        if (user == null) {\n            StreamUtil.write(out, (byte) 0);\n        } else {\n            StreamUtil.writeWithNull(out, user.getBytes());\n        }\n        if (password == null) {\n            StreamUtil.write(out, (byte) 0);\n        } else {\n            StreamUtil.writeWithLength(out, password);\n        }\n        if (database == null) {\n            StreamUtil.write(out, (byte) 0);\n        } else {\n            StreamUtil.writeWithNull(out, database.getBytes());\n        }\n    }\n\n    @Override\n    public void write(BackendAIOConnection c) {\n        ByteBuffer buffer = c.allocate();\n        BufferUtil.writeUB3(buffer, calcPacketSize());\n        buffer.put(packetId);\n        BufferUtil.writeUB4(buffer, clientFlags);\n        BufferUtil.writeUB4(buffer, maxPacketSize);\n        buffer.put((byte) charsetIndex);\n        buffer = c.writeToBuffer(FILLER, buffer);\n        if (user == null) {\n            buffer = c.checkWriteBuffer(buffer, 1,true);\n            buffer.put((byte) 0);\n        } else {\n            byte[] userData = user.getBytes();\n            buffer = c.checkWriteBuffer(buffer, userData.length + 1,true);\n            BufferUtil.writeWithNull(buffer, userData);\n        }\n        if (password == null) {\n            buffer = c.checkWriteBuffer(buffer, 1,true);\n            buffer.put((byte) 0);\n        } else {\n            buffer = c.checkWriteBuffer(buffer, BufferUtil.getLength(password),true);\n            BufferUtil.writeWithLength(buffer, password);\n        }\n        if (database == null) {\n            buffer = c.checkWriteBuffer(buffer, 1,true);\n            buffer.put((byte) 0);\n        } else {\n            byte[] databaseData = database.getBytes();\n            buffer = c.checkWriteBuffer(buffer, databaseData.length + 1,true);\n            BufferUtil.writeWithNull(buffer, databaseData);\n        }\n        c.write(buffer);\n    }\n\n    @Override\n    public int calcPacketSize() {\n        int size = 32;// 4+4+1+23;\n        size += (user == null) ? 1 : user.length() + 1;\n        size += (password == null) ? 1 : BufferUtil.getLength(password);\n        size += (database == null) ? 1 : database.length() + 1;\n        return size;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL Authentication Packet\";\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/AuthSwitchPacket.java",
    "content": "package io.mycat.net.mysql;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.config.Capabilities;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.net.FrontendConnection;\n\n\n/**\n * \n * 1              [fe]\n * string[NUL]    plugin name\n * string[EOF]    auth plugin data\n *  \n * @version 1.0<br>\n * @taskId <br>\n * @CreateDate Jun 18, 2021 <br>\n * @since V8.1<br>\n * @see io.mycat.net.mysql <br>\n */\npublic class AuthSwitchPacket extends MySQLPacket{\n    \n    private static final byte STATUS = (byte) 0XFE;\n    private byte[] authMethodName ;\n    private byte[] authMethodData;\n\n    public AuthSwitchPacket(byte[] authMethodName, byte[] authMethodData) {\n        super();\n        this.authMethodName = authMethodName;\n        this.authMethodData = authMethodData;\n    }\n    \n    public AuthSwitchPacket() {\n    }\n    \n    \n    public void read(byte[] data) {\n        MySQLMessage mm = new MySQLMessage(data);\n        packetLength = mm.readUB3();\n        packetId = mm.read();\n        authMethodData = mm.readBytes(packetLength);\n    }\n    \n    \n    @Override\n    public ByteBuffer write(ByteBuffer buffer, FrontendConnection c,\n            boolean writeSocketIfFull) {\n        int size = calcPacketSize();\n        buffer = c.checkWriteBuffer(buffer, c.getPacketHeaderSize() + size,\n                writeSocketIfFull);\n        BufferUtil.writeUB3(buffer, size);\n        buffer.put(packetId);\n        buffer.put(STATUS);\n        BufferUtil.writeWithNull(buffer, authMethodName);\n        BufferUtil.writeWithNull(buffer, authMethodData);\n        return buffer;\n    }\n\n\n\n    public void write(FrontendConnection c) {\n        ByteBuffer buffer = c.allocate();\n        buffer = this.write(buffer, c, true);\n        c.write(buffer);\n    }\n\n    @Override\n    public int calcPacketSize() {\n        int size = 3; //status\n        size += authMethodName.length;\n        size += authMethodData.length;\n        return size;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL Auth Switch Packet\";\n    }\n\n    public byte[] getAuthMethodName() {\n        return authMethodName;\n    }\n\n    public void setAuthMethodName(byte[] authMethodName) {\n        this.authMethodName = authMethodName;\n    }\n\n    public byte[] getAuthMethodData() {\n        return authMethodData;\n    }\n\n    public void setAuthMethodData(byte[] authMethodData) {\n        this.authMethodData = authMethodData;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/BinaryPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.StreamUtil;\nimport io.mycat.net.BackendAIOConnection;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * @author mycat\n */\npublic class BinaryPacket extends MySQLPacket {\n    public static final byte OK = 1;\n    public static final byte ERROR = 2;\n    public static final byte HEADER = 3;\n    public static final byte FIELD = 4;\n    public static final byte FIELD_EOF = 5;\n    public static final byte ROW = 6;\n    public static final byte PACKET_EOF = 7;\n\n    public byte[] data;\n\n    public void read(InputStream in) throws IOException {\n        packetLength = StreamUtil.readUB3(in);\n        packetId = StreamUtil.read(in);\n        byte[] ab = new byte[packetLength];\n        StreamUtil.read(in, ab, 0, ab.length);\n        data = ab;\n    }\n\n    @Override\n    public ByteBuffer write(ByteBuffer buffer, FrontendConnection c,boolean writeSocketIfFull) {\n        buffer = c.checkWriteBuffer(buffer, c.getPacketHeaderSize(),writeSocketIfFull);\n        BufferUtil.writeUB3(buffer, calcPacketSize());\n        buffer.put(packetId);\n        buffer = c.writeToBuffer(data, buffer);\n        return buffer;\n    }\n    @Override\n    public void write(BackendAIOConnection c) {\n        ByteBuffer buffer = c.allocate();\n        buffer=  c.checkWriteBuffer(buffer,c.getPacketHeaderSize()+calcPacketSize(),false);\n        BufferUtil.writeUB3(buffer, calcPacketSize());\n        buffer.put(packetId);\n        buffer.put(data);\n        c.write(buffer);\n    }\n\n    @Override\n    public int calcPacketSize() {\n        return data == null ? 0 : data.length;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL Binary Packet\";\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/BinaryRowDataPacket.java",
    "content": "package io.mycat.net.mysql;\n\n\nimport java.nio.ByteBuffer;\nimport java.text.ParseException;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.memory.unsafe.row.UnsafeRow;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.util.ByteUtil;\nimport io.mycat.util.DateUtil;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * ProtocolBinary::ResultsetRow:\n * row of a binary resultset (COM_STMT_EXECUTE)\n\n * Payload\n * 1              packet header [00]\n * string[$len]   NULL-bitmap, length: (column_count + 7 + 2) / 8\n * string[$len]   values\n * \n * A Binary Protocol Resultset Row is made up of the NULL bitmap \n * containing as many bits as we have columns in the resultset + 2 \n * and the values for columns that are not NULL in the Binary Protocol Value format.\n * \n * @see @http://dev.mysql.com/doc/internals/en/binary-protocol-resultset-row.html#packet-ProtocolBinary::ResultsetRow\n * @see @http://dev.mysql.com/doc/internals/en/binary-protocol-value.html\n * @author CrazyPig\n * \n */\npublic class BinaryRowDataPacket extends MySQLPacket {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(BinaryRowDataPacket.class);\n\tpublic int fieldCount;\n\tpublic List<byte[]> fieldValues;\n\tpublic byte packetHeader = (byte) 0;\n\tpublic byte[] nullBitMap;\n\t\n\tpublic List<FieldPacket> fieldPackets;\n\t\n\tpublic BinaryRowDataPacket() {}\n\t\n\t/**\n\t * 从UnsafeRow转换成BinaryRowDataPacket\n\t * \n\t * 说明: 当开启<b>isOffHeapuseOffHeapForMerge</b>参数时,会使用UnsafeRow封装数据,\n\t * 因此需要从这个对象里面将数据封装成BinaryRowDataPacket\n\t * \n\t * @param fieldPackets\n\t * @param unsafeRow\n\t */\n\tpublic void read(List<FieldPacket> fieldPackets, UnsafeRow unsafeRow) {\n\t\tthis.fieldPackets = fieldPackets;\n\t\tthis.fieldCount = unsafeRow.numFields();\n\t\tthis.fieldValues = new ArrayList<byte[]>(fieldCount);\n\t\tthis.packetId = unsafeRow.packetId;\n\t\tthis.nullBitMap = new byte[(fieldCount + 7 + 2) / 8];\n\t\t\n\t\tfor(int i = 0; i < this.fieldCount; i++) {\n\t\t\tbyte[] fv = unsafeRow.getBinary(i);\n\t\t\tFieldPacket fieldPk = fieldPackets.get(i);\n\t\t\tif(fv == null) {\n\t\t\t\tstoreNullBitMap(i);\n\t\t\t\tthis.fieldValues.add(fv);\n\t\t\t} else {\n\t\t\t\tconvert(fv, fieldPk);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * 从RowDataPacket转换成BinaryRowDataPacket\n\t * @param fieldPackets 字段包集合\n\t * @param rowDataPk 文本协议行数据包\n\t */\n\tpublic void read(List<FieldPacket> fieldPackets, RowDataPacket rowDataPk) {\n\t\tthis.fieldPackets = fieldPackets;\n\t\tthis.fieldCount = rowDataPk.fieldCount;\n\t\tthis.fieldValues = new ArrayList<byte[]>(fieldCount);\n\t\tthis.packetId = rowDataPk.packetId;\n\t\tthis.nullBitMap = new byte[(fieldCount + 7 + 2) / 8];\n\t\t\n\t\tList<byte[]> _fieldValues = rowDataPk.fieldValues;\n\t\tfor (int i = 0; i < fieldCount; i++) {\n\t\t\tbyte[] fv = _fieldValues.get(i);\n\t\t\tFieldPacket fieldPk = fieldPackets.get(i);\n\t\t\tif (fv == null) { // 字段值为null,根据协议规定存储nullBitMap\n\t\t\t\tstoreNullBitMap(i);\n\t\t\t\tthis.fieldValues.add(fv);\n\t\t\t} else {\n\t\t\t\tconvert(fv, fieldPk);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate void storeNullBitMap(int i) {\n\t\tint bitMapPos = (i + 2) / 8;\n\t\tint bitPos = (i + 2) % 8;\n\t\tthis.nullBitMap[bitMapPos] |= (byte) (1 << bitPos);\n\t}\n\t\n\t/**\n\t * 从RowDataPacket的fieldValue的数据转化成BinaryRowDataPacket的fieldValue数据\n\t * @param fv\n\t * @param fieldPk\n\t */\n\tprivate void convert(byte[] fv, FieldPacket fieldPk) {\n\t\t\n\t\tint fieldType = fieldPk.type;\n\t\tswitch (fieldType) {\n\t\tcase Fields.FIELD_TYPE_STRING:\n\t\tcase Fields.FIELD_TYPE_VARCHAR:\n\t\tcase Fields.FIELD_TYPE_VAR_STRING:\n\t\tcase Fields.FIELD_TYPE_ENUM:\n\t\tcase Fields.FIELD_TYPE_SET:\n\t\tcase Fields.FIELD_TYPE_LONG_BLOB:\n\t\tcase Fields.FIELD_TYPE_MEDIUM_BLOB:\n\t\tcase Fields.FIELD_TYPE_BLOB:\n\t\tcase Fields.FIELD_TYPE_TINY_BLOB:\n\t\tcase Fields.FIELD_TYPE_GEOMETRY:\n\t\tcase Fields.FIELD_TYPE_BIT:\n\t\tcase Fields.FIELD_TYPE_DECIMAL:\n\t\tcase Fields.FIELD_TYPE_NEW_DECIMAL:\n\t\t\t// Fields\n\t\t\t// value (lenenc_str) -- string\n\t\t\t\n\t\t\t// Example\n\t\t\t// 03 66 6f 6f -- string = \"foo\"\n\t\t\tthis.fieldValues.add(fv);\n\t\t\tbreak;\n\t\tcase Fields.FIELD_TYPE_LONGLONG:\n\t\t\t// Fields\n\t\t\t// value (8) -- integer\n\n\t\t\t// Example\n\t\t\t// 01 00 00 00 00 00 00 00 -- int64 = 1\n\t\t\tlong longVar = ByteUtil.getLong(fv);\n\t\t\tthis.fieldValues.add(ByteUtil.getBytes(longVar));\n\t\t\tbreak;\n\t\tcase Fields.FIELD_TYPE_LONG:\n\t\tcase Fields.FIELD_TYPE_INT24:\n\t\t\t// Fields\n\t\t\t// value (4) -- integer\n\n\t\t\t// Example\n\t\t\t// 01 00 00 00 -- int32 = 1\n\t\t\tint intVar = ByteUtil.getInt(fv);\n\t\t\tthis.fieldValues.add(ByteUtil.getBytes(intVar));\n\t\t\tbreak;\n\t\tcase Fields.FIELD_TYPE_SHORT:\n\t\tcase Fields.FIELD_TYPE_YEAR:\n\t\t\t// Fields\n\t\t\t// value (2) -- integer\n\n\t\t\t// Example\n\t\t\t// 01 00 -- int16 = 1\n            if (isUnsignedField(fieldPk)) {\n                this.fieldValues.add(ByteUtil.convertUnsignedShort2Binary(fv));\n            } else {\n                short shortVar = ByteUtil.getShort(fv);\n                this.fieldValues.add(ByteUtil.getBytes(shortVar));\n            }\n\n\t\t\tbreak;\n\t\tcase Fields.FIELD_TYPE_TINY:\n\t\t\t// Fields\n\t\t\t// value (1) -- integer\n\n\t\t\t// Example\n\t\t\t// 01 -- int8 = 1\n\t\t\tint tinyVar = ByteUtil.getInt(fv);\n\t\t\tbyte[] bytes = new byte[1];\n\t\t\tbytes[0] = (byte)tinyVar;\n\t\t\tthis.fieldValues.add(bytes);\n\t\t\tbreak;\n\t\tcase Fields.FIELD_TYPE_DOUBLE:\n\t\t\t// Fields\n\t\t\t// value (string.fix_len) -- (len=8) double\n\n\t\t\t// Example\n\t\t\t// 66 66 66 66 66 66 24 40 -- double = 10.2\n\t\t\tdouble doubleVar = ByteUtil.getDouble(fv);\n\t\t\tthis.fieldValues.add(ByteUtil.getBytes(doubleVar));\n\t\t\tbreak;\n\t\tcase Fields.FIELD_TYPE_FLOAT:\n\t\t\t// Fields\n\t\t\t// value (string.fix_len) -- (len=4) float\n\n\t\t\t// Example\n\t\t\t// 33 33 23 41 -- float = 10.2\n\t\t\tfloat floatVar = ByteUtil.getFloat(fv);\n\t\t\tthis.fieldValues.add(ByteUtil.getBytes(floatVar));\n\t\t\tbreak;\n\t\tcase Fields.FIELD_TYPE_DATE:\n\t\t\ttry {\n\t\t\t\tDate dateVar = DateUtil.parseDate(ByteUtil.getDate(fv), DateUtil.DATE_PATTERN_ONLY_DATE);\n\t\t\t\tthis.fieldValues.add(ByteUtil.getBytes(dateVar, false));\t\t\t\t\n\t\t\t} catch(org.joda.time.IllegalFieldValueException e1) {\n\t\t\t\t// 当时间为 0000-00-00 00:00:00 的时候, 默认返回 1970-01-01 08:00:00.0\n\t\t\t\tthis.fieldValues.add(ByteUtil.getBytes(new Date(0L), false));\n\t\t\t} catch (ParseException e) {\n\t\t\t\tLOGGER.error(\"error\",e);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Fields.FIELD_TYPE_DATETIME:\n\t\tcase Fields.FIELD_TYPE_TIMESTAMP:\n\t\t\tString dateStr = ByteUtil.getDate(fv);\n\t\t\tDate dateTimeVar = null;\n\t\t\ttry {\n\t\t\t\tif (dateStr.indexOf(\".\") > 0) {\n\t\t\t\t\tdateTimeVar = DateUtil.parseDate(dateStr, DateUtil.DATE_PATTERN_FULL);\n\t\t\t\t\tthis.fieldValues.add(ByteUtil.getBytes(dateTimeVar, false));\n\t\t\t\t} else {\n\t\t\t\t\tdateTimeVar = DateUtil.parseDate(dateStr, DateUtil.DEFAULT_DATE_PATTERN);\n\t\t\t\t\tthis.fieldValues.add(ByteUtil.getBytes(dateTimeVar, false));\n\t\t\t\t}\n\t\t\t} catch(org.joda.time.IllegalFieldValueException e1) {\n\t\t\t\t// 当时间为 0000-00-00 00:00:00 的时候, 默认返回 1970-01-01 08:00:00.0\n\t\t\t\tthis.fieldValues.add(ByteUtil.getBytes(new Date(0L), false));\n\t\t\t\t\n\t\t\t} catch (ParseException e) {\n\t\t\t\tLOGGER.error(\"error\",e);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Fields.FIELD_TYPE_TIME:\n\t\t\tString timeStr = ByteUtil.getTime(fv);\n\t\t\tDate timeVar = null;\n\t\t\ttry {\n\t\t\t\tif (timeStr.indexOf(\".\") > 0) {\n\t\t\t\t\ttimeVar = DateUtil.parseDate(timeStr, DateUtil.TIME_PATTERN_FULL);\n\t\t\t\t\tthis.fieldValues.add(ByteUtil.getBytes(timeVar, true));\n\t\t\t\t} else {\n\t\t\t\t\ttimeVar = DateUtil.parseDate(timeStr, DateUtil.DEFAULT_TIME_PATTERN);\n\t\t\t\t\tthis.fieldValues.add(ByteUtil.getBytes(timeVar, true));\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} catch(org.joda.time.IllegalFieldValueException e1) {\n\t\t\t\t// 当时间为 0000-00-00 00:00:00 的时候, 默认返回 1970-01-01 08:00:00.0\n\t\t\t\tthis.fieldValues.add(ByteUtil.getBytes(new Date(0L), true));\n\t\t\t\t\n\t\t\t} catch (ParseException e) {\n\t\t\t\tLOGGER.error(\"error\",e);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\t\n\t}\n\t\n    private boolean isUnsignedField(FieldPacket field) {\n        return (field.flags & FieldPacket.UNSIGNED_FLAG) > 0;\n    }\n\tpublic void write(FrontendConnection conn) {\n\t\t\n\t\tint size = calcPacketSize();\n\t\tint packetHeaderSize = conn.getPacketHeaderSize();\n\t\tint totalSize = size + packetHeaderSize;\n\t\tByteBuffer bb = null;\n\t\t\n\t\tbb = conn.getProcessor().getBufferPool().allocate(totalSize);\n\n\t\tBufferUtil.writeUB3(bb, calcPacketSize());\n\t\tbb.put(packetId);\n\t\tbb.put(packetHeader); // packet header [00]\n\t\tbb.put(nullBitMap); // NULL-Bitmap\n\t\tfor(int i = 0; i < fieldCount; i++) { // values\n\t\t\tbyte[] fv = fieldValues.get(i);\n\t\t\tif(fv != null) {\n\t\t\t\tFieldPacket fieldPk = this.fieldPackets.get(i);\n\t\t\t\tint fieldType = fieldPk.type;\n\t\t\t\tswitch(fieldType) {\n\t\t\t\tcase Fields.FIELD_TYPE_STRING:\n\t\t\t\tcase Fields.FIELD_TYPE_VARCHAR:\n\t\t\t\tcase Fields.FIELD_TYPE_VAR_STRING:\n\t\t\t\tcase Fields.FIELD_TYPE_ENUM:\n\t\t\t\tcase Fields.FIELD_TYPE_SET:\n\t\t\t\tcase Fields.FIELD_TYPE_LONG_BLOB:\n\t\t\t\tcase Fields.FIELD_TYPE_MEDIUM_BLOB:\n\t\t\t\tcase Fields.FIELD_TYPE_BLOB:\n\t\t\t\tcase Fields.FIELD_TYPE_TINY_BLOB:\n\t\t\t\tcase Fields.FIELD_TYPE_GEOMETRY:\n\t\t\t\tcase Fields.FIELD_TYPE_BIT:\n\t\t\t\tcase Fields.FIELD_TYPE_DECIMAL:\n\t\t\t\tcase Fields.FIELD_TYPE_NEW_DECIMAL:\n\t\t\t\t\t// 长度编码的字符串需要一个字节来存储长度(0表示空字符串)\n\t\t\t\t\tBufferUtil.writeLength(bb, fv.length);\n\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif(fv.length > 0) {\n\t\t\t\t\tbb.put(fv);\n\t\t\t\t} \n\t\t\t}\n\t\t}\n\t\tconn.write(bb);\n\t\t\n\t}\n\t\n\t@Override\n\tpublic ByteBuffer write(ByteBuffer bb, FrontendConnection c,\n\t\t\tboolean writeSocketIfFull) {\n\t\tint size = calcPacketSize();\n\t\tint packetHeaderSize = c.getPacketHeaderSize();\n\t\tint totalSize = size + packetHeaderSize;\n\t\tbb = c.checkWriteBuffer(bb, totalSize, writeSocketIfFull);\n\t\tBufferUtil.writeUB3(bb, size);\n\t\tbb.put(packetId);\n\t\tbb.put(packetHeader); // packet header [00]\n\t\tbb.put(nullBitMap); // NULL-Bitmap\n\t\tfor(int i = 0; i < fieldCount; i++) { // values\n\t\t\tbyte[] fv = fieldValues.get(i);\n\t\t\tif(fv != null) {\n\t\t\t\tFieldPacket fieldPk = this.fieldPackets.get(i);\n\t\t\t\tint fieldType = fieldPk.type;\n\t\t\t\tswitch(fieldType) {\n\t\t\t\tcase Fields.FIELD_TYPE_STRING:\n\t\t\t\tcase Fields.FIELD_TYPE_VARCHAR:\n\t\t\t\tcase Fields.FIELD_TYPE_VAR_STRING:\n\t\t\t\tcase Fields.FIELD_TYPE_ENUM:\n\t\t\t\tcase Fields.FIELD_TYPE_SET:\n\t\t\t\tcase Fields.FIELD_TYPE_LONG_BLOB:\n\t\t\t\tcase Fields.FIELD_TYPE_MEDIUM_BLOB:\n\t\t\t\tcase Fields.FIELD_TYPE_BLOB:\n\t\t\t\tcase Fields.FIELD_TYPE_TINY_BLOB:\n\t\t\t\tcase Fields.FIELD_TYPE_GEOMETRY:\n\t\t\t\tcase Fields.FIELD_TYPE_BIT:\n\t\t\t\tcase Fields.FIELD_TYPE_DECIMAL:\n\t\t\t\tcase Fields.FIELD_TYPE_NEW_DECIMAL:\n\t\t\t\t\t// 长度编码的字符串需要一个字节来存储长度(0表示空字符串)\n\t\t\t\t\tBufferUtil.writeLength(bb, fv.length);\n\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif(fv.length > 0) {\n\t\t\t\t\tbb.put(fv);\n\t\t\t\t} \n\t\t\t}\n\t\t}\n\t\treturn bb;\n\t}\n\n\t@Override\n\tpublic int calcPacketSize() {\n\t\tint size = 0;\n\t\tsize = size + 1 + nullBitMap.length;\n\t\tfor(int i = 0, n = fieldValues.size(); i < n; i++) {\n\t\t\tbyte[] value = fieldValues.get(i);\n\t\t\tif(value != null) {\n\t\t\t\tFieldPacket fieldPk = this.fieldPackets.get(i);\n\t\t\t\tint fieldType = fieldPk.type;\n\t\t\t\tswitch(fieldType) {\n\t\t\t\tcase Fields.FIELD_TYPE_STRING:\n\t\t\t\tcase Fields.FIELD_TYPE_VARCHAR:\n\t\t\t\tcase Fields.FIELD_TYPE_VAR_STRING:\n\t\t\t\tcase Fields.FIELD_TYPE_ENUM:\n\t\t\t\tcase Fields.FIELD_TYPE_SET:\n\t\t\t\tcase Fields.FIELD_TYPE_LONG_BLOB:\n\t\t\t\tcase Fields.FIELD_TYPE_MEDIUM_BLOB:\n\t\t\t\tcase Fields.FIELD_TYPE_BLOB:\n\t\t\t\tcase Fields.FIELD_TYPE_TINY_BLOB:\n\t\t\t\tcase Fields.FIELD_TYPE_GEOMETRY:\n\t\t\t\tcase Fields.FIELD_TYPE_BIT:\n\t\t\t\tcase Fields.FIELD_TYPE_DECIMAL:\n\t\t\t\tcase Fields.FIELD_TYPE_NEW_DECIMAL:\n\t\t\t\t\t/*\n\t\t\t\t\t * 长度编码的字符串需要计算存储长度, 根据mysql协议文档描述\n\t\t\t\t\t * To convert a length-encoded integer into its numeric value, check the first byte:\n\t\t\t\t\t * If it is < 0xfb, treat it as a 1-byte integer.\n                     * If it is 0xfc, it is followed by a 2-byte integer.\n                     * If it is 0xfd, it is followed by a 3-byte integer.\n                     * If it is 0xfe, it is followed by a 8-byte integer.\n\t\t\t\t\t * \n\t\t\t\t\t */\n\t\t\t\t\tif(value.length != 0) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * 长度编码的字符串需要计算存储长度,不能简单默认只有1个字节是表示长度,当数据足够长,占用的就不止1个字节\n\t\t\t\t\t\t */\n//\t\t\t\t\t\tsize = size + 1 + value.length;\n\t\t\t\t\t\tsize = size + BufferUtil.getLength(value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsize = size + 1; // 处理空字符串,只计算长度1个字节\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tsize = size + value.length;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn size;\n\t}\n\n\t@Override\n\tprotected String getPacketInfo() {\n\t\treturn \"MySQL Binary RowData Packet\";\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/CommandPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.backend.mysql.StreamUtil;\nimport io.mycat.net.BackendAIOConnection;\n\n/**\n * From client to server whenever the client wants the server to do something.\n * \n * <pre>\n * Bytes         Name\n * -----         ----\n * 1             command\n * n             arg\n * \n * command:      The most common value is 03 COM_QUERY, because\n *               INSERT UPDATE DELETE SELECT etc. have this code.\n *               The possible values at time of writing (taken\n *               from /include/mysql_com.h for enum_server_command) are:\n * \n *               #      Name                Associated client function\n *               -      ----                --------------------------\n *               0x00   COM_SLEEP           (none, this is an internal thread state)\n *               0x01   COM_QUIT            mysql_close\n *               0x02   COM_INIT_DB         mysql_select_db \n *               0x03   COM_QUERY           mysql_real_query\n *               0x04   COM_FIELD_LIST      mysql_list_fields\n *               0x05   COM_CREATE_DB       mysql_create_db (deprecated)\n *               0x06   COM_DROP_DB         mysql_drop_db (deprecated)\n *               0x07   COM_REFRESH         mysql_refresh\n *               0x08   COM_SHUTDOWN        mysql_shutdown\n *               0x09   COM_STATISTICS      mysql_stat\n *               0x0a   COM_PROCESS_INFO    mysql_list_processes\n *               0x0b   COM_CONNECT         (none, this is an internal thread state)\n *               0x0c   COM_PROCESS_KILL    mysql_kill\n *               0x0d   COM_DEBUG           mysql_dump_debug_info\n *               0x0e   COM_PING            mysql_ping\n *               0x0f   COM_TIME            (none, this is an internal thread state)\n *               0x10   COM_DELAYED_INSERT  (none, this is an internal thread state)\n *               0x11   COM_CHANGE_USER     mysql_change_user\n *               0x12   COM_BINLOG_DUMP     sent by the slave IO thread to request a binlog\n *               0x13   COM_TABLE_DUMP      LOAD TABLE ... FROM MASTER (deprecated)\n *               0x14   COM_CONNECT_OUT     (none, this is an internal thread state)\n *               0x15   COM_REGISTER_SLAVE  sent by the slave to register with the master (optional)\n *               0x16   COM_STMT_PREPARE    mysql_stmt_prepare\n *               0x17   COM_STMT_EXECUTE    mysql_stmt_execute\n *               0x18   COM_STMT_SEND_LONG_DATA mysql_stmt_send_long_data\n *               0x19   COM_STMT_CLOSE      mysql_stmt_close\n *               0x1a   COM_STMT_RESET      mysql_stmt_reset\n *               0x1b   COM_SET_OPTION      mysql_set_server_option\n *               0x1c   COM_STMT_FETCH      mysql_stmt_fetch\n * \n * arg:          The text of the command is just the way the user typed it, there is no processing\n *               by the client (except removal of the final ';').\n *               This field is not a null-terminated string; however,\n *               the size can be calculated from the packet size,\n *               and the MySQL client appends '\\0' when receiving.\n *               \n * @see http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Command_Packet_.28Overview.29\n * </pre>\n * \n * @author mycat\n */\npublic class CommandPacket extends MySQLPacket {\n\n    public byte command;\n    public byte[] arg;\n\n    public void read(byte[] data) {\n        MySQLMessage mm = new MySQLMessage(data);\n        packetLength = mm.readUB3();\n        packetId = mm.read();\n        command = mm.read();\n        arg = mm.readBytes();\n    }\n\n\n\n    public void write(OutputStream out) throws IOException {\n        StreamUtil.writeUB3(out, calcPacketSize());\n        StreamUtil.write(out, packetId);\n        StreamUtil.write(out, command);\n        out.write(arg);\n    }\n\n    @Override\n    public void write(BackendAIOConnection c) {\n        ByteBuffer buffer = c.allocate();\n        try {    \n\t        BufferUtil.writeUB3(buffer, calcPacketSize());\n\t        buffer.put(packetId);\n\t        buffer.put(command);\n\t        buffer = c.writeToBuffer(arg, buffer);\n\t        c.write(buffer);\t        \n        } catch(java.nio.BufferOverflowException e1) { \n        \t//fixed issues #98 #1072\n        \tbuffer =  c.checkWriteBuffer(buffer, c.getPacketHeaderSize() + calcPacketSize(), false);\n\t        BufferUtil.writeUB3(buffer, calcPacketSize());\n\t        buffer.put(packetId);\n\t        buffer.put(command);\n\t        buffer = c.writeToBuffer(arg, buffer);\n\t        c.write(buffer);\n        }\n    }\n\n    @Override\n    public int calcPacketSize() {\n        return 1 + arg.length;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL Command Packet\";\n    }\n\n\t\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/EOFPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.buffer.BufferArray;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * From Server To Client, at the end of a series of Field Packets, and at the\n * end of a series of Data Packets.With prepared statements, EOF Packet can also\n * end parameter information, which we'll describe later.\n * \n * <pre>\n * Bytes                 Name\n * -----                 ----\n * 1                     field_count, always = 0xfe\n * 2                     warning_count\n * 2                     Status Flags\n * \n * @see http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#EOF_Packet\n * </pre>\n * \n * @author mycat\n */\npublic class EOFPacket extends MySQLPacket {\n    public static final byte FIELD_COUNT = (byte) 0xfe;\n\n    public byte fieldCount = FIELD_COUNT;\n    public int warningCount;\n    public int status = 2;\n\n    public void read(byte[] data) {\n        MySQLMessage mm = new MySQLMessage(data);\n        packetLength = mm.readUB3();\n        packetId = mm.read();\n        fieldCount = mm.read();\n        warningCount = mm.readUB2();\n        status = mm.readUB2();\n    }\n\n    @Override\n    public ByteBuffer write(ByteBuffer buffer, FrontendConnection c,boolean writeSocketIfFull) {\n        int size = calcPacketSize();\n        buffer = c.checkWriteBuffer(buffer, c.getPacketHeaderSize() + size,writeSocketIfFull);\n        BufferUtil.writeUB3(buffer, size);\n        buffer.put(packetId);\n        buffer.put(fieldCount);\n        BufferUtil.writeUB2(buffer, warningCount);\n        BufferUtil.writeUB2(buffer, status);\n        return buffer;\n    }\n\n    @Override\n    public int calcPacketSize() {\n        return 5;// 1+2+2;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL EOF Packet\";\n    }\n\n\tpublic void write(BufferArray bufferArray) {\n\t\tint size = calcPacketSize();\n\t\tByteBuffer buffer = bufferArray.checkWriteBuffer(packetHeaderSize\n\t\t\t\t+ size);\n\t\tBufferUtil.writeUB3(buffer, size);\n\t\tbuffer.put(packetId);\n\t\tbuffer.put(fieldCount);\n\t\tBufferUtil.writeUB2(buffer, warningCount);\n\t\tBufferUtil.writeUB2(buffer, status);\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/EmptyPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\n/**\n * @author mycat暂时只发现在load data infile时用到\n */\npublic class EmptyPacket extends MySQLPacket {\n    public static final byte[] EMPTY = new byte[] { 0, 0, 0,3 };\n\n    @Override\n    public int calcPacketSize() {\n        return 0;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL Empty Packet\";\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/ErrorPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.io.ByteArrayOutputStream;\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * From server to client in response to command, if error.\n * \n * <pre>\n * Bytes                       Name\n * -----                       ----\n * 1                           field_count, always = 0xff\n * 2                           errno\n * 1                           (sqlstate marker), always '#'\n * 5                           sqlstate (5 characters)\n * n                           message\n * \n * @see http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Error_Packet\n * </pre>\n * \n * @author mycat\n */\npublic class ErrorPacket extends MySQLPacket {\n\tpublic static final byte FIELD_COUNT = (byte) 0xff;\n\tprivate static final byte SQLSTATE_MARKER = (byte) '#';\n\tprivate static final byte[] DEFAULT_SQLSTATE = \"HY000\".getBytes();\n\n\tpublic byte fieldCount = FIELD_COUNT;\n\tpublic int errno;\n\tpublic byte mark = SQLSTATE_MARKER;\n\tpublic byte[] sqlState = DEFAULT_SQLSTATE;\n\tpublic byte[] message;\n\n\tpublic void read(BinaryPacket bin) {\n\t\tpacketLength = bin.packetLength;\n\t\tpacketId = bin.packetId;\n\t\tMySQLMessage mm = new MySQLMessage(bin.data);\n\t\tfieldCount = mm.read();\n\t\terrno = mm.readUB2();\n\t\tif (mm.hasRemaining() && (mm.read(mm.position()) == SQLSTATE_MARKER)) {\n\t\t\tmm.read();\n\t\t\tsqlState = mm.readBytes(5);\n\t\t}\n\t\tmessage = mm.readBytes();\n\t}\n\n\tpublic void read(byte[] data) {\n\t\tMySQLMessage mm = new MySQLMessage(data);\n\t\tpacketLength = mm.readUB3();\n\t\tpacketId = mm.read();\n\t\tfieldCount = mm.read();\n\t\terrno = mm.readUB2();\n\t\tif (mm.hasRemaining() && (mm.read(mm.position()) == SQLSTATE_MARKER)) {\n\t\t\tmm.read();\n\t\t\tsqlState = mm.readBytes(5);\n\t\t}\n\t\tmessage = mm.readBytes();\n\t}\n\n\tpublic byte[] writeToBytes(FrontendConnection c) {\n\t\tByteBuffer buffer = c.allocate();\n\t\tbuffer = write(buffer, c, false);\n\t\tbuffer.flip();\n\t\tbyte[] data = new byte[buffer.limit()];\n\t\tbuffer.get(data);\n\t\tc.recycle(buffer);\n\t\treturn data;\n\t}\n\tpublic byte[] writeToBytes() {\n\t\tByteBuffer buffer = ByteBuffer.allocate(calcPacketSize()+4);\n\t\tint size = calcPacketSize();\n\t\tBufferUtil.writeUB3(buffer, size);\n\t\tbuffer.put(packetId);\n\t\tbuffer.put(fieldCount);\n\t\tBufferUtil.writeUB2(buffer, errno);\n\t\tbuffer.put(mark);\n\t\tbuffer.put(sqlState);\n\t\tif (message != null) {\n\t\t\tbuffer.put(message);\n\t\t}\n\t\tbuffer.flip();\n\t\tbyte[] data = new byte[buffer.limit()];\n\t\tbuffer.get(data);\n\n\t\treturn data;\n\t}\n\t@Override\n\tpublic ByteBuffer write(ByteBuffer buffer, FrontendConnection c,\n\t\t\tboolean writeSocketIfFull) {\n\t\tint size = calcPacketSize();\n\t\tbuffer = c.checkWriteBuffer(buffer, c.getPacketHeaderSize() + size,\n\t\t\t\twriteSocketIfFull);\n\t\tBufferUtil.writeUB3(buffer, size);\n\t\tbuffer.put(packetId);\n\t\tbuffer.put(fieldCount);\n\t\tBufferUtil.writeUB2(buffer, errno);\n\t\tbuffer.put(mark);\n\t\tbuffer.put(sqlState);\n\t\tif (message != null) {\n\t\t\tbuffer = c.writeToBuffer(message, buffer);\n\t\t}\n\t\treturn buffer;\n\t}\n\n\n\n\tpublic void write(FrontendConnection c) {\n\t\tByteBuffer buffer = c.allocate();\n\t\tbuffer = this.write(buffer, c, true);\n\t\tc.write(buffer);\n\t}\n\n\t@Override\n\tpublic int calcPacketSize() {\n\t\tint size = 9;// 1 + 2 + 1 + 5\n\t\tif (message != null) {\n\t\t\tsize += message.length;\n\t\t}\n\t\treturn size;\n\t}\n\n\t@Override\n\tprotected String getPacketInfo() {\n\t\treturn \"MySQL Error Packet\";\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/ExecutePacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.io.UnsupportedEncodingException;\n\nimport io.mycat.backend.mysql.BindValue;\nimport io.mycat.backend.mysql.BindValueUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.backend.mysql.PreparedStatement;\n\n/**\n * <pre>\n *  Bytes                      Name\n *  -----                      ----\n *  1                          code\n *  4                          statement_id\n *  1                          flags\n *  4                          iteration_count \n *  (param_count+7)/8          null_bit_map\n *  1                          new_parameter_bound_flag (if new_params_bound == 1:)\n *  n*2                        type of parameters\n *  n                          values for the parameters   \n *  --------------------------------------------------------------------------------\n *  code:                      always COM_EXECUTE\n *  \n *  statement_id:              statement identifier\n *  \n *  flags:                     reserved for future use. In MySQL 4.0, always 0.\n *                             In MySQL 5.0: \n *                               0: CURSOR_TYPE_NO_CURSOR\n *                               1: CURSOR_TYPE_READ_ONLY\n *                               2: CURSOR_TYPE_FOR_UPDATE\n *                               4: CURSOR_TYPE_SCROLLABLE\n *  \n *  iteration_count:           reserved for future use. Currently always 1.\n *  \n *  null_bit_map:              A bitmap indicating parameters that are NULL.\n *                             Bits are counted from LSB, using as many bytes\n *                             as necessary ((param_count+7)/8)\n *                             i.e. if the first parameter (parameter 0) is NULL, then\n *                             the least significant bit in the first byte will be 1.\n *  \n *  new_parameter_bound_flag:  Contains 1 if this is the first time\n *                             that \"execute\" has been called, or if\n *                             the parameters have been rebound.\n *  \n *  type:                      Occurs once for each parameter; \n *                             The highest significant bit of this 16-bit value\n *                             encodes the unsigned property. The other 15 bits\n *                             are reserved for the type (only 8 currently used).\n *                             This block is sent when parameters have been rebound\n *                             or when a prepared statement is executed for the \n *                             first time.\n * \n *  values:                    for all non-NULL values, each parameters appends its value\n *                             as described in Row Data Packet: Binary (column values)\n * @see https://dev.mysql.com/doc/internals/en/com-stmt-execute.html\n * </pre>\n * \n * @author mycat, CrazyPig\n */\npublic class ExecutePacket extends MySQLPacket {\n\n    public byte code;\n    public long statementId;\n    public byte flags;\n    public long iterationCount;\n    public byte[] nullBitMap;\n    public byte newParameterBoundFlag;\n    public BindValue[] values;\n    protected PreparedStatement pstmt;\n\n    public ExecutePacket(PreparedStatement pstmt) {\n        this.pstmt = pstmt;\n        this.values = new BindValue[pstmt.getParametersNumber()];\n    }\n\n    public void read(byte[] data, String charset) throws UnsupportedEncodingException {\n        MySQLMessage mm = new MySQLMessage(data);\n        packetLength = mm.readUB3();\n        packetId = mm.read();\n        code = mm.read();\n        statementId = mm.readUB4();\n        flags = mm.read();\n        iterationCount = mm.readUB4();\n\n        // 读取NULL指示器数据\n        int parameterCount = values.length;\n        if(parameterCount > 0) {\n\t        nullBitMap = new byte[(parameterCount + 7) / 8];\n\t        for (int i = 0; i < nullBitMap.length; i++) {\n\t            nullBitMap[i] = mm.read();\n\t        }\n\t\n\t        // 当newParameterBoundFlag==1时，更新参数类型。\n\t        newParameterBoundFlag = mm.read();\n        }\n        if (newParameterBoundFlag == (byte) 1) {\n            for (int i = 0; i < parameterCount; i++) {\n                pstmt.getParametersType()[i] = mm.readUB2();\n            }\n        }\n\n        // 设置参数类型和读取参数值\n        byte[] nullBitMap = this.nullBitMap;\n        for (int i = 0; i < parameterCount; i++) {\n            BindValue bv = new BindValue();\n            bv.type = pstmt.getParametersType()[i];\n            if ((nullBitMap[i / 8] & (1 << (i & 7))) != 0) {\n                bv.isNull = true;\n            } else {\n            \tif (!pstmt.hasLongData(i)) {\n            \t\tBindValueUtil.read(mm, bv, charset);\n            \t} else {\n            \t\tbv.value = pstmt.getLongData(i).toByteArray();\n            \t}\n            }\n            values[i] = bv;\n        }\n    }\n\n    @Override\n    public int calcPacketSize() {\n        \n        return 0;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL Execute Packet\";\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/FieldPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.buffer.BufferArray;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * From Server To Client, part of Result Set Packets. One for each column in the\n * result set. Thus, if the value of field_columns in the Result Set Header\n * Packet is 3, then the Field Packet occurs 3 times.\n * \n * <pre>\n * Bytes                      Name\n * -----                      ----\n * n (Length Coded String)    catalog\n * n (Length Coded String)    db\n * n (Length Coded String)    table\n * n (Length Coded String)    org_table\n * n (Length Coded String)    name\n * n (Length Coded String)    org_name\n * 1                          (filler)\n * 2                          charsetNumber\n * 4                          length\n * 1                          type\n * 2                          flags\n * 1                          decimals\n * 2                          (filler), always 0x00\n * n (Length Coded Binary)    default\n * \n * @see http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Field_Packet\n * </pre>\n * \n * @author mycat\n */\npublic class FieldPacket extends MySQLPacket {\n    public static final int UNSIGNED_FLAG = 0x0020;\n\n\tprivate static final byte[] DEFAULT_CATALOG = \"def\".getBytes();\n\tprivate static final byte[] FILLER = new byte[2];\n\n\tpublic byte[] catalog = DEFAULT_CATALOG;\n\tpublic byte[] db;\n\tpublic byte[] table;\n\tpublic byte[] orgTable;\n\tpublic byte[] name;\n\tpublic byte[] orgName;\n\tpublic int charsetIndex;\n\tpublic long length;\n\tpublic int type;\n\tpublic int flags;\n\tpublic byte decimals;\n\tpublic byte[] definition;\n\n\t/**\n\t * 把字节数组转变成FieldPacket\n\t */\n\tpublic void read(byte[] data) {\n\t\tMySQLMessage mm = new MySQLMessage(data);\n\t\tthis.packetLength = mm.readUB3();\n\t\tthis.packetId = mm.read();\n\t\treadBody(mm);\n\t}\n\n\t/**\n\t * 把BinaryPacket转变成FieldPacket\n\t */\n\tpublic void read(BinaryPacket bin) {\n\t\tthis.packetLength = bin.packetLength;\n\t\tthis.packetId = bin.packetId;\n\t\treadBody(new MySQLMessage(bin.data));\n\t}\n\n\t@Override\n\tpublic ByteBuffer write(ByteBuffer buffer, FrontendConnection c,\n\t\t\tboolean writeSocketIfFull) {\n\t\tint size = calcPacketSize();\n\t\tbuffer = c.checkWriteBuffer(buffer, c.getPacketHeaderSize() + size,\n\t\t\t\twriteSocketIfFull);\n\t\tBufferUtil.writeUB3(buffer, size);\n\t\tbuffer.put(packetId);\n\t\twriteBody(buffer);\n\t\treturn buffer;\n\t}\n\n\t@Override\n\tpublic int calcPacketSize() {\n\t\tint size = (catalog == null ? 1 : BufferUtil.getLength(catalog));\n\t\tsize += (db == null ? 1 : BufferUtil.getLength(db));\n\t\tsize += (table == null ? 1 : BufferUtil.getLength(table));\n\t\tsize += (orgTable == null ? 1 : BufferUtil.getLength(orgTable));\n\t\tsize += (name == null ? 1 : BufferUtil.getLength(name));\n\t\tsize += (orgName == null ? 1 : BufferUtil.getLength(orgName));\n\t\tsize += 13;// 1+2+4+1+2+1+2\n\t\tif (definition != null) {\n\t\t\tsize += BufferUtil.getLength(definition);\n\t\t}\n\t\treturn size;\n\t}\n\n\t@Override\n\tprotected String getPacketInfo() {\n\t\treturn \"MySQL Field Packet\";\n\t}\n\n\tprivate void readBody(MySQLMessage mm) {\n\t\tthis.catalog = mm.readBytesWithLength();\n\t\tthis.db = mm.readBytesWithLength();\n\t\tthis.table = mm.readBytesWithLength();\n\t\tthis.orgTable = mm.readBytesWithLength();\n\t\tthis.name = mm.readBytesWithLength();\n\t\tthis.orgName = mm.readBytesWithLength();\n\t\tmm.move(1);\n\t\tthis.charsetIndex = mm.readUB2();\n\t\tthis.length = mm.readUB4();\n\t\tthis.type = mm.read() & 0xff;\n\t\tthis.flags = mm.readUB2();\n\t\tthis.decimals = mm.read();\n\t\tmm.move(FILLER.length);\n\t\tif (mm.hasRemaining()) {\n\t\t\tthis.definition = mm.readBytesWithLength();\n\t\t}\n\t}\n\n\tprivate void writeBody(ByteBuffer buffer) {\n\t\tbyte nullVal = 0;\n\t\tBufferUtil.writeWithLength(buffer, catalog, nullVal);\n\t\tBufferUtil.writeWithLength(buffer, db, nullVal);\n\t\tBufferUtil.writeWithLength(buffer, table, nullVal);\n\t\tBufferUtil.writeWithLength(buffer, orgTable, nullVal);\n\t\tBufferUtil.writeWithLength(buffer, name, nullVal);\n\t\tBufferUtil.writeWithLength(buffer, orgName, nullVal);\n\t\tbuffer.put((byte) 0x0C);\n\t\tBufferUtil.writeUB2(buffer, charsetIndex);\n\t\tBufferUtil.writeUB4(buffer, length);\n\t\tbuffer.put((byte) (type & 0xff));\n\t\tBufferUtil.writeUB2(buffer, flags);\n\t\tbuffer.put(decimals);\n        buffer.put((byte)0x00);\n        buffer.put((byte)0x00);\n\t\t//buffer.position(buffer.position() + FILLER.length);\n\t\tif (definition != null) {\n\t\t\tBufferUtil.writeWithLength(buffer, definition);\n\t\t}\n\t}\n\n\tpublic  void write(BufferArray bufferArray) {\n\t\tint size = calcPacketSize();\n\t\tByteBuffer buffer = bufferArray.checkWriteBuffer(packetHeaderSize + size);\n\t\tBufferUtil.writeUB3(buffer, size);\n\t\tbuffer.put(packetId);\n\t\twriteBody(buffer);\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/HandshakePacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * From server to client during initial handshake.\n * \n * <pre>\n * Bytes                        Name\n * -----                        ----\n * 1                            protocol_version\n * n (Null-Terminated String)   server_version\n * 4                            thread_id\n * 8                            scramble_buff\n * 1                            (filler) always 0x00\n * 2                            server_capabilities\n * 1                            server_language\n * 2                            server_status\n * 13                           (filler) always 0x00 ...\n * 13                           rest of scramble_buff (4.1)\n * \n * @see http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Handshake_Initialization_Packet\n * </pre>\n * \n * @author mycat\n */\npublic class HandshakePacket extends MySQLPacket {\n    private static final byte[] FILLER_13 = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };\n\n    public byte protocolVersion;\n    public byte[] serverVersion;\n    public long threadId;\n    public byte[] seed;\n    public int serverCapabilities;\n    public byte serverCharsetIndex;\n    public int serverStatus;\n    public byte[] restOfScrambleBuff;\n\n    public void read(BinaryPacket bin) {\n        packetLength = bin.packetLength;\n        packetId = bin.packetId;\n        MySQLMessage mm = new MySQLMessage(bin.data);\n        protocolVersion = mm.read();\n        serverVersion = mm.readBytesWithNull();\n        threadId = mm.readUB4();\n        seed = mm.readBytesWithNull();\n        serverCapabilities = mm.readUB2();\n        serverCharsetIndex = mm.read();\n        serverStatus = mm.readUB2();\n        mm.move(13);\n        restOfScrambleBuff = mm.readBytesWithNull();\n    }\n\n    public void read(byte[] data) {\n        MySQLMessage mm = new MySQLMessage(data);\n        packetLength = mm.readUB3();\n        packetId = mm.read();\n        protocolVersion = mm.read();\n        serverVersion = mm.readBytesWithNull();\n        threadId = mm.readUB4();\n        seed = mm.readBytesWithNull();\n        serverCapabilities = mm.readUB2();\n        serverCharsetIndex = mm.read();\n        serverStatus = mm.readUB2();\n        mm.move(13);\n        restOfScrambleBuff = mm.readBytesWithNull();\n    }\n\n    public void write(FrontendConnection c) {\n        ByteBuffer buffer = c.allocate();\n        BufferUtil.writeUB3(buffer, calcPacketSize());\n        buffer.put(packetId);\n        buffer.put(protocolVersion);\n        BufferUtil.writeWithNull(buffer, serverVersion);\n        BufferUtil.writeUB4(buffer, threadId);\n        BufferUtil.writeWithNull(buffer, seed);\n        BufferUtil.writeUB2(buffer, serverCapabilities);\n        buffer.put(serverCharsetIndex);\n        BufferUtil.writeUB2(buffer, serverStatus);\n        buffer.put(FILLER_13);\n        //        buffer.position(buffer.position() + 13);\n        BufferUtil.writeWithNull(buffer, restOfScrambleBuff);\n        c.write(buffer);\n    }\n\n    @Override\n    public int calcPacketSize() {\n        int size = 1;\n        size += serverVersion.length;// n\n        size += 5;// 1+4\n        size += seed.length;// 8\n        size += 19;// 1+2+1+2+13\n        size += restOfScrambleBuff.length;// 12\n        size += 1;// 1\n        return size;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL Handshake Packet\";\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/HandshakeV10Packet.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.config.Capabilities;\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * From mycat server to client during initial handshake.\n * \n * <pre>\n * Bytes                        Name\n * -----                        ----\n * 1                            protocol_version (always 0x0a)\n * n (string[NULL])             server_version\n * 4                            thread_id\n * 8 (string[8])                auth-plugin-data-part-1\n * 1                            (filler) always 0x00\n * 2                            capability flags (lower 2 bytes)\n *   if more data in the packet:\n * 1                            character set\n * 2                            status flags\n * 2                            capability flags (upper 2 bytes)\n *   if capabilities & CLIENT_PLUGIN_AUTH {\n * 1                            length of auth-plugin-data\n *   } else {\n * 1                            0x00\n *   }\n * 10 (string[10])              reserved (all 0x00)\n *   if capabilities & CLIENT_SECURE_CONNECTION {\n * string[$len]   auth-plugin-data-part-2 ($len=MAX(13, length of auth-plugin-data - 8))\n *   }\n *   if capabilities & CLIENT_PLUGIN_AUTH {\n * string[NUL]    auth-plugin name\n * }\n * \n * @see http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#Protocol::HandshakeV10\n * </pre>\n * \n * @author CrazyPig\n * @since 2016-11-13\n * \n */\npublic class HandshakeV10Packet extends MySQLPacket {\n    private static final byte[] FILLER_10 = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };\n    private static final byte[] DEFAULT_AUTH_PLUGIN_NAME = \"mysql_native_password\".getBytes();\n    \n    public byte protocolVersion;\n    public byte[] serverVersion;\n    public long threadId;\n    public byte[] seed; // auth-plugin-data-part-1\n    public int serverCapabilities;\n    public byte serverCharsetIndex;\n    public int serverStatus;\n    public byte[] restOfScrambleBuff; // auth-plugin-data-part-2\n    public byte[] authPluginName = DEFAULT_AUTH_PLUGIN_NAME;\n\n    public void write(FrontendConnection c) {\n\n    \tByteBuffer buffer = c.allocate();\n        BufferUtil.writeUB3(buffer, calcPacketSize());\n        buffer.put(packetId);\n        buffer.put(protocolVersion);\n        BufferUtil.writeWithNull(buffer, serverVersion);\n        BufferUtil.writeUB4(buffer, threadId);\n        buffer.put(seed);\n        buffer.put((byte)0); // [00] filler\n        BufferUtil.writeUB2(buffer, serverCapabilities); // capability flags (lower 2 bytes)\n        buffer.put(serverCharsetIndex);\n        BufferUtil.writeUB2(buffer, serverStatus);\n        BufferUtil.writeUB2(buffer, (serverCapabilities >> 16)); // capability flags (upper 2 bytes)\n        if((serverCapabilities & Capabilities.CLIENT_PLUGIN_AUTH) != 0) {\n        \tif(restOfScrambleBuff.length <= 13) {\n        \t\tbuffer.put((byte) (seed.length + 13));\n        \t} else {\n        \t\tbuffer.put((byte) (seed.length + restOfScrambleBuff.length));\n        \t}\n        } else {\n        \tbuffer.put((byte) 0);\n        }\n        buffer.put(FILLER_10);\n        if((serverCapabilities & Capabilities.CLIENT_SECURE_CONNECTION) != 0) {\n        \tbuffer.put(restOfScrambleBuff);\n        \t// restOfScrambleBuff.length always to be 12\n        \tif(restOfScrambleBuff.length < 13) {\n        \t\tfor(int i = 13 - restOfScrambleBuff.length; i > 0; i--) {\n        \t\t\tbuffer.put((byte)0);\n        \t\t}\n        \t}\n        }\n        if((serverCapabilities & Capabilities.CLIENT_PLUGIN_AUTH) != 0) {\n        \tBufferUtil.writeWithNull(buffer, authPluginName);\n        }\n        c.write(buffer);\n    }\n\n    @Override\n    public int calcPacketSize() {\n        int size = 1; // protocol version\n        size += (serverVersion.length + 1); // server version\n        size += 4; // connection id\n        size += seed.length;\n        size += 1; // [00] filler\n        size += 2; // capability flags (lower 2 bytes)\n        size += 1; // character set\n        size += 2; // status flags\n        size += 2; // capability flags (upper 2 bytes)\n        size += 1;\n        size += 10; // reserved (all [00])\n        if((serverCapabilities & Capabilities.CLIENT_SECURE_CONNECTION) != 0) {\n        \t// restOfScrambleBuff.length always to be 12\n        \tif(restOfScrambleBuff.length <= 13) {\n        \t\tsize += 13;\n        \t} else {\n        \t\tsize += restOfScrambleBuff.length;\n        \t}\n        }\n        if((serverCapabilities & Capabilities.CLIENT_PLUGIN_AUTH) != 0) {\n        \tsize += (authPluginName.length + 1); // auth-plugin name\n        }\n        return size;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL HandshakeV10 Packet\";\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/HeartbeatPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.net.BackendAIOConnection;\n\n/**\n * From client to server when the client do heartbeat between mycat cluster.\n * \n * <pre>\n * Bytes         Name\n * -----         ----\n * 1             command\n * n             id\n * \n * @author mycat\n */\npublic class HeartbeatPacket extends MySQLPacket {\n\n    public byte command;\n    public long id;\n\n    public void read(byte[] data) {\n        MySQLMessage mm = new MySQLMessage(data);\n        packetLength = mm.readUB3();\n        packetId = mm.read();\n        command = mm.read();\n        id = mm.readLength();\n    }\n\n    @Override\n    public void write(BackendAIOConnection c) {\n        ByteBuffer buffer = c.allocate();\n        BufferUtil.writeUB3(buffer, calcPacketSize());\n        buffer.put(packetId);\n        buffer.put(command);\n        BufferUtil.writeLength(buffer, id);\n        c.write(buffer);\n    }\n\n    @Override\n    public int calcPacketSize() {\n        return 1 + BufferUtil.getLength(id);\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"Mycat Heartbeat Packet\";\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/LongDataPacket.java",
    "content": "package io.mycat.net.mysql;\n\nimport io.mycat.backend.mysql.MySQLMessage;\n\n/**\n * \n * <pre>\n * \n * COM_STMT_SEND_LONG_DATA sends the data for a column. Repeating to send it, appends the data to the parameter.\n * No response is sent back to the client.\n\n * COM_STMT_SEND_LONG_DATA:\n * COM_STMT_SEND_LONG_DATA\n * direction: client -> server\n * response: none\n\n * payload:\n *   1              [18] COM_STMT_SEND_LONG_DATA\n *   4              statement-id\n *   2              param-id\n *   n              data\n * \n * </pre>\n * \n * @see https://dev.mysql.com/doc/internals/en/com-stmt-send-long-data.html\n * \n * @author CrazyPig\n * @since 2016-09-08\n *\n */\npublic class LongDataPacket extends MySQLPacket {\n\n\tprivate static final byte PACKET_FALG = (byte) 24;\n\tprivate long pstmtId;\n\tprivate long paramId;\n\tprivate byte[] longData = new byte[0];\n\t\n\tpublic void read(byte[] data) {\n\t\tMySQLMessage mm = new MySQLMessage(data);\n\t\tpacketLength = mm.readUB3();\n\t\tpacketId = mm.read();\n\t\tbyte code = mm.read();\n\t\tassert code == PACKET_FALG;\n\t\tpstmtId = mm.readUB4();\n\t\tparamId = mm.readUB2();\n\t\tthis.longData = mm.readBytes(packetLength - (1 + 4 + 2));\n\t}\n\n\t@Override\n\tpublic int calcPacketSize() {\n\t\treturn 1 + 4 + 2 + this.longData.length;\n\t}\n\n\t@Override\n\tprotected String getPacketInfo() {\n\t\treturn \"MySQL Long Data Packet\";\n\t}\n\n\tpublic long getPstmtId() {\n\t\treturn pstmtId;\n\t}\n\n\tpublic long getParamId() {\n\t\treturn paramId;\n\t}\n\n\tpublic byte[] getLongData() {\n\t\treturn longData;\n\t}\n\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/MySQLPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.net.BackendAIOConnection;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * @author mycat\n */\npublic abstract class MySQLPacket {\n    /**\n     * none, this is an internal thread state\n     */\n    public static final byte COM_SLEEP = 0;\n\n    /**\n     * mysql_close\n     */\n    public static final byte COM_QUIT = 1;\n\n    /**\n     * mysql_select_db\n     */\n    public static final byte COM_INIT_DB = 2;\n\n    /**\n     * mysql_real_query\n     */\n    public static final byte COM_QUERY = 3;\n\n    /**\n     * mysql_list_fields\n     */\n    public static final byte COM_FIELD_LIST = 4;\n\n    /**\n     * mysql_create_db (deprecated)\n     */\n    public static final byte COM_CREATE_DB = 5;\n\n    /**\n     * mysql_drop_db (deprecated)\n     */\n    public static final byte COM_DROP_DB = 6;\n\n    /**\n     * mysql_refresh\n     */\n    public static final byte COM_REFRESH = 7;\n\n    /**\n     * mysql_shutdown\n     */\n    public static final byte COM_SHUTDOWN = 8;\n\n    /**\n     * mysql_stat\n     */\n    public static final byte COM_STATISTICS = 9;\n\n    /**\n     * mysql_list_processes\n     */\n    public static final byte COM_PROCESS_INFO = 10;\n\n    /**\n     * none, this is an internal thread state\n     */\n    public static final byte COM_CONNECT = 11;\n\n    /**\n     * mysql_kill\n     */\n    public static final byte COM_PROCESS_KILL = 12;\n\n    /**\n     * mysql_dump_debug_info\n     */\n    public static final byte COM_DEBUG = 13;\n\n    /**\n     * mysql_ping\n     */\n    public static final byte COM_PING = 14;\n\n    /**\n     * none, this is an internal thread state\n     */\n    public static final byte COM_TIME = 15;\n\n    /**\n     * none, this is an internal thread state\n     */\n    public static final byte COM_DELAYED_INSERT = 16;\n\n    /**\n     * mysql_change_user\n     */\n    public static final byte COM_CHANGE_USER = 17;\n\n    /**\n     * used by slave server mysqlbinlog\n     */\n    public static final byte COM_BINLOG_DUMP = 18;\n\n    /**\n     * used by slave server to get master table\n     */\n    public static final byte COM_TABLE_DUMP = 19;\n\n    /**\n     * used by slave to log connection to master\n     */\n    public static final byte COM_CONNECT_OUT = 20;\n\n    /**\n     * used by slave to register to master\n     */\n    public static final byte COM_REGISTER_SLAVE = 21;\n\n    /**\n     * mysql_stmt_prepare\n     */\n    public static final byte COM_STMT_PREPARE = 22;\n\n    /**\n     * mysql_stmt_execute\n     */\n    public static final byte COM_STMT_EXECUTE = 23;\n\n    /**\n     * mysql_stmt_send_long_data\n     */\n    public static final byte COM_STMT_SEND_LONG_DATA = 24;\n\n    /**\n     * mysql_stmt_close\n     */\n    public static final byte COM_STMT_CLOSE = 25;\n\n    /**\n     * mysql_stmt_reset\n     */\n    public static final byte COM_STMT_RESET = 26;\n\n    /**\n     * mysql_set_server_option\n     */\n    public static final byte COM_SET_OPTION = 27;\n\n    /**\n     * mysql_stmt_fetch\n     */\n    public static final byte COM_STMT_FETCH = 28;\n\n    /**\n     * mysql_reset_connection\n     */\n    public static final byte COM_RESET_CONNECTION = 31;\n\n    /**\n     * Mycat heartbeat\n     */\n    public static final byte COM_HEARTBEAT = 64;\n    \n    //包头大小\n    public static final int packetHeaderSize = 4;\n\n\n    public int packetLength;\n    public byte packetId;\n\n    /**\n     * 把数据包写到buffer中，如果buffer满了就把buffer通过前端连接写出 (writeSocketIfFull=true)。\n     */\n    public ByteBuffer write(ByteBuffer buffer, FrontendConnection c,boolean writeSocketIfFull) {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * 把数据包通过后端连接写出，一般使用buffer机制来提高写的吞吐量。\n     */\n    public void write(BackendAIOConnection c) {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * 计算数据包大小，不包含包头长度。\n     */\n    public abstract int calcPacketSize();\n\n    /**\n     * 取得数据包信息\n     */\n    protected abstract String getPacketInfo();\n\n    @Override\n    public String toString() {\n        return new StringBuilder().append(getPacketInfo()).append(\"{length=\").append(packetLength).append(\",id=\")\n                .append(packetId).append('}').toString();\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/OkPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * From server to client in response to command, if no error and no result set.\n * \n * <pre>\n * Bytes                       Name\n * -----                       ----\n * 1                           field_count, always = 0\n * 1-9 (Length Coded Binary)   affected_rows\n * 1-9 (Length Coded Binary)   insert_id\n * 2                           server_status\n * 2                           warning_count\n * n   (until end of packet)   message fix:(Length Coded String)\n * \n * @see http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#OK_Packet\n * </pre>\n * \n * @author mycat\n */\npublic class OkPacket extends MySQLPacket {\n\tpublic static final byte FIELD_COUNT = 0x00;\n\tpublic static final byte[] OK = new byte[] { 7, 0, 0, 1, 0, 0, 0, 2, 0, 0,\n\t\t\t0 };\n\n\tpublic byte fieldCount = FIELD_COUNT;\n\tpublic long affectedRows;\n\tpublic long insertId;\n\tpublic int serverStatus;\n\tpublic int warningCount;\n\tpublic byte[] message;\n\n\tpublic void read(BinaryPacket bin) {\n\t\tpacketLength = bin.packetLength;\n\t\tpacketId = bin.packetId;\n\t\tMySQLMessage mm = new MySQLMessage(bin.data);\n\t\tfieldCount = mm.read();\n\t\taffectedRows = mm.readLength();\n\t\tinsertId = mm.readLength();\n\t\tserverStatus = mm.readUB2();\n\t\twarningCount = mm.readUB2();\n\t\tif (mm.hasRemaining()) {\n\t\t\tthis.message = mm.readBytesWithLength();\n\t\t}\n\t}\n\n\tpublic void read(byte[] data) {\n\t\tMySQLMessage mm = new MySQLMessage(data);\n\t\tpacketLength = mm.readUB3();\n\t\tpacketId = mm.read();\n\t\tfieldCount = mm.read();\n\t\taffectedRows = mm.readLength();\n\t\tinsertId = mm.readLength();\n\t\tserverStatus = mm.readUB2();\n\t\twarningCount = mm.readUB2();\n\t\tif (mm.hasRemaining()) {\n\t\t\tthis.message = mm.readBytesWithLength();\n\t\t}\n\t}\n\n\tpublic byte[] writeToBytes(FrontendConnection c) {\n\t\tByteBuffer buffer = c.allocate();\n\t\tthis.write(buffer, c);\n\t\tbuffer.flip();\n\t\tbyte[] data = new byte[buffer.limit()];\n\t\tbuffer.get(data);\n\t\tc.recycle(buffer);\n\t\treturn data;\n\t}\n\n\tprivate ByteBuffer write(ByteBuffer buffer, FrontendConnection c) {\n\n\t\tint size = calcPacketSize();\n\t\tbuffer = c.checkWriteBuffer(buffer, c.getPacketHeaderSize() + size,\n\t\t\t\ttrue);\n\t\tBufferUtil.writeUB3(buffer, calcPacketSize());\n\t\tbuffer.put(packetId);\n\t\tbuffer.put(fieldCount);\n\t\tBufferUtil.writeLength(buffer, affectedRows);\n\t\tBufferUtil.writeLength(buffer, insertId);\n\t\tBufferUtil.writeUB2(buffer, serverStatus);\n\t\tBufferUtil.writeUB2(buffer, warningCount);\n\t\tif (message != null) {\n\t\t\tBufferUtil.writeWithLength(buffer, message);\n\t\t}\n\n\t\treturn buffer;\n\n\t}\n\n\tpublic void write(FrontendConnection c) {\n\t\tByteBuffer buffer = write(c.allocate(), c);\n\t\tc.write(buffer);\n\t}\n\n\t@Override\n\tpublic int calcPacketSize() {\n\t\tint i = 1;\n\t\ti += BufferUtil.getLength(affectedRows);\n\t\ti += BufferUtil.getLength(insertId);\n\t\ti += 4;\n\t\tif (message != null) {\n\t\t\ti += BufferUtil.getLength(message);\n\t\t}\n\t\treturn i;\n\t}\n\n\t@Override\n\tprotected String getPacketInfo() {\n\t\treturn \"MySQL OK Packet\";\n\t}\n\n\t public byte[] writeToBytes() {\n\t\n\t   int totalSize = calcPacketSize() + packetHeaderSize;\n        ByteBuffer buffer=MycatServer.getInstance().getBufferPool().allocate(totalSize);\n        BufferUtil.writeUB3(buffer, calcPacketSize());\n        buffer.put(packetId);\n        buffer.put(fieldCount);\n        BufferUtil.writeLength(buffer, affectedRows);\n        BufferUtil.writeLength(buffer, insertId);\n        BufferUtil.writeUB2(buffer, serverStatus);\n        BufferUtil.writeUB2(buffer, warningCount);\n        if (message != null) {\n            BufferUtil.writeWithLength(buffer, message);\n        }\n        buffer.flip();\n        byte[] data = new byte[buffer.limit()];\n        buffer.get(data);\n\t\t MycatServer.getInstance().getBufferPool().recycle(buffer);\n\t\t return data;\n    }\n\n\tpublic void markMoreResultsExists() {\n\t\tserverStatus = serverStatus | StatusFlags.SERVER_MORE_RESULTS_EXISTS;\n\t}\n\n\tpublic void markNoMoreResultsExists() {\n\t\tserverStatus = serverStatus & (~StatusFlags.SERVER_MORE_RESULTS_EXISTS);\n\t}\n\n\tpublic boolean hasMoreResultsExists() {\n\t\treturn (serverStatus & StatusFlags.SERVER_MORE_RESULTS_EXISTS) > 0;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/PingPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\n/**\n * @author mycat\n */\npublic class PingPacket extends MySQLPacket {\n    public static final byte[] PING = new byte[] { 1, 0, 0, 0, 14 };\n\n    @Override\n    public int calcPacketSize() {\n        return 1;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL Ping Packet\";\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/PreparedOkPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * <pre>\n * From server to client, in response to prepared statement initialization packet. \n * It is made up of: \n *   1.a PREPARE_OK packet\n *   2.if \"number of parameters\" > 0 \n *       (field packets) as in a Result Set Header Packet \n *       (EOF packet)\n *   3.if \"number of columns\" > 0 \n *       (field packets) as in a Result Set Header Packet \n *       (EOF packet)\n *   \n * -----------------------------------------------------------------------------------------\n * \n *  Bytes              Name\n *  -----              ----\n *  1                  0 - marker for OK packet\n *  4                  statement_handler_id\n *  2                  number of columns in result set\n *  2                  number of parameters in query\n *  1                  filler (always 0)\n *  2                  warning count\n *  \n *  @see http://dev.mysql.com/doc/internals/en/prepared-statement-initialization-packet.html\n * </pre>\n * \n * @author mycat\n */\npublic class PreparedOkPacket extends MySQLPacket {\n    public static int PACKET_SIZE = 12;\n    public byte flag;\n    public long statementId;\n    public int columnsNumber;\n    public int parametersNumber;\n    public byte filler;\n    public int warningCount;\n\n    public PreparedOkPacket() {\n        this.flag = 0;\n        this.filler = 0;\n    }\n\n    public void read(byte[] data) {\n        MySQLMessage mm = new MySQLMessage(data);\n        packetLength = mm.readUB3();\n        packetId = mm.read();\n        flag = mm.read();\n        statementId = mm.readUB4();\n        columnsNumber = mm.readUB2();\n        parametersNumber = mm.readUB2();\n        warningCount = mm.read();\n\n    }\n    @Override\n    public ByteBuffer write(ByteBuffer buffer, FrontendConnection c,boolean writeSocketIfFull) {\n        int size = calcPacketSize();\n        buffer = c.checkWriteBuffer(buffer, c.getPacketHeaderSize() + size,writeSocketIfFull);\n        BufferUtil.writeUB3(buffer, size);\n        buffer.put(packetId);\n        buffer.put(flag);\n        BufferUtil.writeUB4(buffer, statementId);\n        BufferUtil.writeUB2(buffer, columnsNumber);\n        BufferUtil.writeUB2(buffer, parametersNumber);\n        buffer.put(filler);\n        BufferUtil.writeUB2(buffer, warningCount);\n        return buffer;\n    }\n\n    @Override\n    public int calcPacketSize() {\n        return PACKET_SIZE;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL PreparedOk Packet\";\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/QuitPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\n/**\n * @author mycat\n */\npublic class QuitPacket extends MySQLPacket {\n    public static final byte[] QUIT = new byte[] { 1, 0, 0, 0, 1 };\n\n    @Override\n    public int calcPacketSize() {\n        return 1;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL Quit Packet\";\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/Reply323Packet.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.StreamUtil;\nimport io.mycat.net.BackendAIOConnection;\n\n/**\n * @author mycat\n */\npublic class Reply323Packet extends MySQLPacket {\n\n    public byte[] seed;\n\n    public void write(OutputStream out) throws IOException {\n        StreamUtil.writeUB3(out, calcPacketSize());\n        StreamUtil.write(out, packetId);\n        if (seed == null) {\n            StreamUtil.write(out, (byte) 0);\n        } else {\n            StreamUtil.writeWithNull(out, seed);\n        }\n    }\n\n    @Override\n    public void write(BackendAIOConnection c) {\n        ByteBuffer buffer = c.allocate();\n        BufferUtil.writeUB3(buffer, calcPacketSize());\n        buffer.put(packetId);\n        if (seed == null) {\n            buffer.put((byte) 0);\n        } else {\n            BufferUtil.writeWithNull(buffer, seed);\n        }\n        c.write(buffer);\n    }\n\n    @Override\n    public int calcPacketSize() {\n        return seed == null ? 1 : seed.length + 1;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL Auth323 Packet\";\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/RequestFilePacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * load data local infile 向客户端请求发送文件用\n */\npublic class RequestFilePacket extends MySQLPacket\n{\n    public static final byte FIELD_COUNT = (byte) 251;\n    public byte command = FIELD_COUNT;\n    public byte[] fileName;\n\n\n    @Override\n    public ByteBuffer write(ByteBuffer buffer, FrontendConnection c, boolean writeSocketIfFull)\n    {\n        int size = calcPacketSize();\n        buffer = c.checkWriteBuffer(buffer, c.getPacketHeaderSize() + size, writeSocketIfFull);\n        BufferUtil.writeUB3(buffer, size);\n        buffer.put(packetId);\n        buffer.put(command);\n        if (fileName != null)\n        {\n\n            buffer.put(fileName);\n\n        }\n\n        c.write(buffer);\n\n        return buffer;\n    }\n\n    @Override\n    public int calcPacketSize()\n    {\n        return fileName == null ? 1 : 1 + fileName.length;\n    }\n\n    @Override\n    protected String getPacketInfo()\n    {\n        return \"MySQL Request File Packet\";\n    }\n\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/ResetPacket.java",
    "content": "package io.mycat.net.mysql;\n\nimport io.mycat.backend.mysql.MySQLMessage;\n\n/**\n * <pre>\n * \n * COM_STMT_RESET resets the data of a prepared statement which was accumulated with COM_STMT_SEND_LONG_DATA commands and closes the cursor if it was opened with COM_STMT_EXECUTE\n\n * The server will send a OK_Packet if the statement could be reset, a ERR_Packet if not.\n * \n * COM_STMT_RESET:\n * COM_STMT_RESET\n * direction: client -> server\n * response: OK or ERR\n\n * payload:\n *   1              [1a] COM_STMT_RESET\n *   4              statement-id\n * \n * </pre>\n * \n * @author CrazyPig\n * @since 2016-09-08\n *\n */\npublic class ResetPacket extends MySQLPacket {\n\n\tprivate static final byte PACKET_FALG = (byte) 26;\n\tprivate long pstmtId;\n\t\n\tpublic void read(byte[] data) {\n\t\tMySQLMessage mm = new MySQLMessage(data);\n\t\tpacketLength = mm.readUB3();\n\t\tpacketId = mm.read();\n\t\tbyte code = mm.read();\n\t\tassert code == PACKET_FALG;\n\t\tpstmtId = mm.readUB4();\n\t}\n\t\n\t@Override\n\tpublic int calcPacketSize() {\n\t\treturn 1 + 4;\n\t}\n\n\t@Override\n\tprotected String getPacketInfo() {\n\t\treturn \"MySQL Reset Packet\";\n\t}\n\n\tpublic long getPstmtId() {\n\t\treturn pstmtId;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/ResultSetHeaderPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.buffer.BufferArray;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * From server to client after command, if no error and result set -- that is,\n * if the command was a query which returned a result set. The Result Set Header\n * Packet is the first of several, possibly many, packets that the server sends\n * for result sets. The order of packets for a result set is:\n * \n * <pre>\n * (Result Set Header Packet)   the number of columns\n * (Field Packets)              column descriptors\n * (EOF Packet)                 marker: end of Field Packets\n * (Row Data Packets)           row contents\n * (EOF Packet)                 marker: end of Data Packets\n * \n * Bytes                        Name\n * -----                        ----\n * 1-9   (Length-Coded-Binary)  field_count\n * 1-9   (Length-Coded-Binary)  extra\n * \n * @see http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Result_Set_Header_Packet\n * </pre>\n * \n * @author mycat\n */\npublic class ResultSetHeaderPacket extends MySQLPacket {\n\n    public int fieldCount;\n    public long extra;\n\n    public void read(byte[] data) {\n        MySQLMessage mm = new MySQLMessage(data);\n        this.packetLength = mm.readUB3();\n        this.packetId = mm.read();\n        this.fieldCount = (int) mm.readLength();\n        if (mm.hasRemaining()) {\n            this.extra = mm.readLength();\n        }\n    }\n\n    @Override\n    public ByteBuffer write(ByteBuffer buffer, FrontendConnection c,boolean writeSocketIfFull) {\n        int size = calcPacketSize();\n        buffer = c.checkWriteBuffer(buffer, c.getPacketHeaderSize() + size,writeSocketIfFull);\n        BufferUtil.writeUB3(buffer, size);\n        buffer.put(packetId);\n        BufferUtil.writeLength(buffer, fieldCount);\n        if (extra > 0) {\n            BufferUtil.writeLength(buffer, extra);\n        }\n        return buffer;\n    }\n    \n    public void write(BufferArray bufferArray) {\n\t\tint size = calcPacketSize();\n\t\tByteBuffer buffer = bufferArray\n\t\t\t\t.checkWriteBuffer(packetHeaderSize + size);\n\t\tBufferUtil.writeUB3(buffer, size);\n\t\tbuffer.put(packetId);\n\t\tBufferUtil.writeLength(buffer, fieldCount);\n\t\tif (extra > 0) {\n\t\t\tBufferUtil.writeLength(buffer, extra);\n\t\t}\n\t}\n\n    @Override\n    public int calcPacketSize() {\n        int size = BufferUtil.getLength(fieldCount);\n        if (extra > 0) {\n            size += BufferUtil.getLength(extra);\n        }\n        return size;\n    }\n\n    @Override\n    protected String getPacketInfo() {\n        return \"MySQL ResultSetHeader Packet\";\n    }\n\n\t\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/RowDataPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.mysql;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.buffer.BufferArray;\nimport io.mycat.net.FrontendConnection;\n\n/**\n * From server to client. One packet for each row in the result set.\n * \n * <pre>\n * Bytes                   Name\n * -----                   ----\n * n (Length Coded String) (column value)\n * ...\n * \n * (column value):         The data in the column, as a character string.\n *                         If a column is defined as non-character, the\n *                         server converts the value into a character\n *                         before sending it. Since the value is a Length\n *                         Coded String, a NULL can be represented with a\n *                         single byte containing 251(see the description\n *                         of Length Coded Strings in section \"Elements\" above).\n * \n * @see http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Row_Data_Packet\n * </pre>\n * \n * @author mycat\n */\npublic class RowDataPacket extends MySQLPacket {\n\tprivate static final byte NULL_MARK = (byte) 251;\n    private static final byte EMPTY_MARK = (byte) 0;\n\n\tpublic byte[] value;\n\tpublic int fieldCount;\n\tpublic final List<byte[]> fieldValues;\n\n\tpublic RowDataPacket(int fieldCount) {\n\t\tthis.fieldCount = fieldCount;\n\t\tthis.fieldValues = new ArrayList<byte[]>(fieldCount);\n\t}\n\n\tpublic void add(byte[] value) {\n\t\t//这里应该修改value\n\t\tfieldValues.add(value);\n\t}\n\tpublic void addFieldCount(int add) {\n\t\t//这里应该修改field\n\t\tfieldCount=fieldCount+add;\n\t}\n\t\n\tpublic void read(byte[] data) {\n\t\tvalue = data;\n\t\tMySQLMessage mm = new MySQLMessage(data);\n\t\tpacketLength = mm.readUB3();\n\t\tpacketId = mm.read();\n\t\tfor (int i = 0; i < fieldCount; i++) {\n\t\t\tfieldValues.add(mm.readBytesWithLength());\n\t\t}\n\t}\n\n\t@Override\n\tpublic ByteBuffer write(ByteBuffer bb, FrontendConnection c,\n\t\t\tboolean writeSocketIfFull) {\n\t\tbb = c.checkWriteBuffer(bb, c.getPacketHeaderSize(), writeSocketIfFull);\n\t\tBufferUtil.writeUB3(bb, calcPacketSize());\n\t\tbb.put(packetId);\n\t\tfor (int i = 0; i < fieldCount; i++) {\n\t\t\tbyte[] fv = fieldValues.get(i);\n\t\t\tif (fv == null ) {\n\t\t\t\tbb = c.checkWriteBuffer(bb, 1, writeSocketIfFull);\n\t\t\t\tbb.put(RowDataPacket.NULL_MARK);\n\t\t\t}else if (fv.length == 0) {\n                bb = c.checkWriteBuffer(bb, 1, writeSocketIfFull);\n                bb.put(RowDataPacket.EMPTY_MARK);\n            }\n            else {\n\t\t\t\tbb = c.checkWriteBuffer(bb, BufferUtil.getLength(fv),\n\t\t\t\t\t\twriteSocketIfFull);\n\t\t\t\tBufferUtil.writeLength(bb, fv.length);\n\t\t\t\tbb = c.writeToBuffer(fv, bb);\n\t\t\t}\n\t\t}\n\t\treturn bb;\n\t}\n\n\t@Override\n\tpublic int calcPacketSize() {\n\t\tint size = 0;\n\t\tfor (int i = 0; i < fieldCount; i++) {\n\t\t\tbyte[] v = fieldValues.get(i);\n\t\t\tsize += (v == null || v.length == 0) ? 1 : BufferUtil.getLength(v);\n\t\t}\n\t\treturn size;\n\t}\n\n\t@Override\n\tprotected String getPacketInfo() {\n\t\treturn \"MySQL RowData Packet\";\n\t}\n\n\tpublic void write(BufferArray bufferArray) {\n\t\tint size = calcPacketSize();\n\t\tByteBuffer buffer = bufferArray.checkWriteBuffer(packetHeaderSize + size);\n\t\tBufferUtil.writeUB3(buffer, size);\n\t\tbuffer.put(packetId);\n\t\tfor (int i = 0; i < fieldCount; i++) {\n\t\t\tbyte[] fv = fieldValues.get(i);\n\t\t\tif (fv == null) {\n\t\t\t\tbuffer = bufferArray.checkWriteBuffer(1);\n\t\t\t\tbuffer.put(RowDataPacket.NULL_MARK);\n\t\t\t} else if (fv.length == 0) {\n\t\t\t\tbuffer = bufferArray.checkWriteBuffer(1);\n\t\t\t\tbuffer.put(RowDataPacket.EMPTY_MARK);\n\t\t\t} else {\n\t\t\t\tbuffer = bufferArray.checkWriteBuffer(BufferUtil\n\t\t\t\t\t\t.getLength(fv.length));\n\t\t\t\tBufferUtil.writeLength(buffer, fv.length);\n\t\t\t\tbufferArray.write(fv);\n\t\t\t}\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/mysql/StatusFlags.java",
    "content": "package io.mycat.net.mysql;\n\npublic final class StatusFlags {\n    private StatusFlags() {\n    }\n\n    // a transaction is active\n    public static final int SERVER_STATUS_IN_TRANS = 0x0001;\n    // auto-commit is enabled\n    public static final int SERVER_STATUS_AUTOCOMMIT = 0x0002;\n    public static final int SERVER_MORE_RESULTS_EXISTS = 0x0008;\n    public static final int SERVER_STATUS_NO_GOOD_INDEX_USED = 0x0010;\n    public static final int SERVER_STATUS_NO_INDEX_USED = 0x0020;\n    // Used by Binary Protocol Resultset to signal that COM_STMT_FETCH has to be\n    // used to fetch the row-data.\n    public static final int SERVER_STATUS_CURSOR_EXISTS = 0x0040;\n    public static final int SERVER_STATUS_LAST_ROW_SENT = 0x0080;\n    public static final int SERVER_STATUS_DB_DROPPED = 0x0100;\n    public static final int SERVER_STATUS_NO_BACKSLASH_ESCAPES = 0x0200;\n    public static final int SERVER_STATUS_METADATA_CHANGED = 0x0400;\n    public static final int SERVER_QUERY_WAS_SLOW = 0x0800;\n    public static final int SERVER_PS_OUT_PARAMS = 0x1000;\n    // in a read-only transaction\n    public static final int SERVER_STATUS_IN_TRANS_READONLY = 0x2000;\n    public static final int SERVER_SESSION_STATE_CHANGED = 0x4000;\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/AuthenticationCleartextPassword.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * AuthenticationCleartextPassword (B)\n * Byte1('R') Identifies the message as an authentication request. \n * Int32(8) Length of message contents in bytes, including self. \n * Int32(3) Specifies that a clear-text password is required.\n * </pre>\n * \n * @author mycat\n */\npublic class AuthenticationCleartextPassword extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/AuthenticationGSS.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * AuthenticationGSS (B) \n * Byte1('R') Identifies the message as an authentication request. \n * Int32(8) Length of message contents in bytes, including self. \n * Int32(7) Specifies that GSSAPI authentication is required.\n * </pre>\n * \n * @author mycat\n */\npublic class AuthenticationGSS extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/AuthenticationGSSContinue.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * AuthenticationGSSContinue (B) \n * Byte1('R') Identifies the message as an authentication request. \n * Int32 Length of message contents in bytes, including self. \n * Int32(8) Specifies that this message contains GSSAPI or SSPI data. \n * Byten GSSAPI or SSPI authentication data.\n * </pre>\n * \n * @author mycat\n */\npublic class AuthenticationGSSContinue extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/AuthenticationKerberosV5.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * AuthenticationKerberosV5 (B) \n * Byte1('R') Identifies the message as an authentication request. \n * Int32(8) Length of message contents in bytes, including self. \n * Int32(2) Specifies that Kerberos V5 authentication is required.\n * </pre>\n * \n * @author mycat\n */\npublic class AuthenticationKerberosV5 extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/AuthenticationMD5Password.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * AuthenticationMD5Password (B)\n * Byte1('R') Identifies the message as an authentication request. \n * Int32(12) Length of message contents in bytes, including self. \n * Int32(5) Specifies that an MD5-encrypted password is required. \n * Byte4 The salt to use when encrypting the password.\n * </pre>\n * \n * @author mycat\n */\npublic class AuthenticationMD5Password extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/AuthenticationOk.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * AuthenticationOk (B) \n * Byte1('R') Identifies the message as an authentication request. \n * Int32(8) Length of message contents in bytes, including self. \n * Int32(0) Specifies that the authentication was successful.\n * </pre>\n * \n * @author mycat\n */\npublic class AuthenticationOk extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/AuthenticationSCMCredential.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * AuthenticationSCMCredential (B) \n * Byte1('R') Identifies the message as an authentication request. \n * Int32(8) Length of message contents in bytes, including self. \n * Int32(6) Specifies that an SCM credentials message is required.\n * </pre>\n * \n * @author mycat\n */\npublic class AuthenticationSCMCredential extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/AuthenticationSSPI.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * AuthenticationSSPI (B) \n * Byte1('R') Identifies the message as an authentication request. \n * Int32(8) Length of message contents in bytes, including self. \n * Int32(9) Specifies that SSPI authentication is required.\n * </pre>\n * \n * @author mycat\n */\npublic class AuthenticationSSPI extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/BackendKeyData.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * BackendKeyData (B) \n * Byte1('K') Identifies the message as cancellation key data. \n *            The frontend must save these values if it wishes to be able to\n *            issue CancelRequest messages later. \n * Int32(12) Length of message contents in bytes, including self. \n * Int32 The process ID of this backend. \n * Int32 The secret key of this backend.\n * </pre>\n * \n * @author mycat\n */\npublic class BackendKeyData extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/Bind.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * Bind (F) \n * Byte1('B') Identifies the message as a Bind command. \n * Int32 Length of message contents in bytes, including self. \n * String The name of the destination portal (an empty string selects the unnamed portal).\n * String The name of the source prepared statement (an empty string selects the unnamed \n *        prepared statement). \n * Int16 The number of parameter format codes that follow (denoted C below). \n *       This can be zero to indicate that there are no parameters or that the parameters \n *       all use the default format(text); or one, in which case the specified format code \n *       is applied to all parameters; or it can equal the actual number of parameters. \n * Int16[C] The parameter format codes. Each must presently be zero (text) or one(binary). \n * Int16 The number of parameter values that follow (possibly zero). This must match the \n *       number of parameters needed by the query. Next, the following pair of fields appear \n *       for each parameter: \n * Int32 The length of the parameter value, in bytes (this count does not include\n *       itself). Can be zero. As a special case, -1 indicates a NULL parameter\n *       value. No value bytes follow in the NULL case. \n * Byten The value of the parameter, in the format indicated by the associated format code. \n *       n is the above length. After the last parameter, the following fields appear:\n * Int16 The number of result-column format codes that follow (denoted R\n *       below). This can be zero to indicate that there are no result columns or\n *       that the result columns should all use the default format (text); or one,\n *       in which case the specified format code is applied to all result columns\n *       (if any); or it can equal the actual number of result columns of the query. \n * Int16[R] The result-column format codes. Each must presently be zero (text) or one (binary).\n * </pre>\n * \n * @author mycat\n */\npublic class Bind extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/BindComplete.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * BindComplete (B) \n * Byte1('2') Identifies the message as a Bind-complete indicator. \n * Int32(4) Length of message contents in bytes, including self.\n * </pre>\n * \n * @author mycat\n */\npublic class BindComplete extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/CancelRequest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * CancelRequest (F) \n * Int32(16) Length of message contents in bytes,including self. \n * Int32(80877102) The cancel request code. The value is chosen to \n *                 contain 1234 in the most significant 16 bits, and \n *                 5678 in the least 16 significant bits. (To avoid \n *                 confusion, this code must not be the same as any \n *                 protocol version number.) \n * Int32 The process ID of the target backend. \n * Int32 The secret key for the target backend.\n * </pre>\n * \n * @author mycat\n */\npublic class CancelRequest extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/Close.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * Close (F) \n * Byte1('C') Identifies the message as a Close command. \n * Int32 Length of message contents in bytes, including self. \n * Byte1 'S' to close a prepared statement; or 'P' to close a portal. \n * String The name of the prepared statement or portal to close (an \n *        empty string selects the unnamed prepared statement or portal).\n * </pre>\n * \n * @author mycat\n */\npublic class Close extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/CloseComplete.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * CloseComplete (B) \n * Byte1('3') Identifies the message as a Close-complete indicator. \n * Int32(4) Length of message contents in bytes, including self.\n * </pre>\n * \n * @author mycat\n */\npublic class CloseComplete extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/CommandComplete.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * CommandComplete (B)\n * Byte1('C') Identifies the message as a command-completed response.     \n * Int32 Length of message contents in bytes, including self.     \n * String The command tag. This is usually a single word that identifies which SQL command was completed. \n *        For an INSERT command, the tag is INSERT oid rows, where rows is the number of rows inserted. \n *        oid is the object ID of the inserted row if rows is 1 and the target table has OIDs; otherwise oid is 0.  \n *        For a DELETE command, the tag is DELETE rows where rows is the number of rows deleted. \n *        For an UPDATE command, the tag is UPDATE rows where rows is the number of rows updated. \n *        For a SELECT or CREATE TABLE AS command, the tag is SELECT rows where rows is the number of rows retrieved. \n *        For a MOVE command, the tag is MOVE rows where rows is the number of rows the cursor's position has been changed by. \n *        For a FETCH command, the tag is FETCH rows where rows is the number of rows that have been retrieved from the cursor. \n *        For a COPY command, the tag is COPY rows where rows is the number of rows copied. \n *        (Note: the row count appears only in PostgreSQL 8.2 and later.)\n * </pre>\n * \n * @author mycat\n */\npublic class CommandComplete extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/CopyBothResponse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * CopyBothResponse (B) \n * Byte1('W') Identifies the message as a Start Copy Both response. \n *            This message is used only for Streaming Replication. \n * Int32 Length of message contents in bytes, including self. \n * Int8 0 indicates the overall COPY format is textual (rows separated \n *      by newlines, columns separated by separator characters, etc). \n *      1 indicates the overall copy format is binary (similar to DataRow \n *      format). See COPY for more information. \n * Int16 The number of columns in the data to be copied(denoted N below). \n * Int16[N] The format codes to be used for each column. Each must presently \n *          be zero (text) or one (binary). All must be zero if the overall \n *          copy format is textual.\n * </pre>\n * \n * @author mycat\n */\npublic class CopyBothResponse extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/CopyData.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * CopyData (F & B) \n * Byte1('d') Identifies the message as COPY data. \n * Int32 Length of message contents in bytes, including self. \n * Byten Data that forms part of a COPY data stream. Messages sent from the backend will\n *       always correspond to single data rows, but messages sent by frontends\n *       might divide the data stream arbitrarily.\n * </pre>\n * \n * @author mycat\n */\npublic class CopyData extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/CopyDone.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * CopyDone (F & B) \n * Byte1('c') Identifies the message as a COPY-complete indicator. \n * Int32(4) Length of message contents in bytes, including self.\n * </pre>\n * \n * @author mycat\n */\npublic class CopyDone extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/CopyFail.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * CopyFail (F) \n * Byte1('f') Identifies the message as a COPY-failure indicator. \n * Int32 Length of message contents in bytes, including self.\n * String An error message to report as the cause of failure.\n * </pre>\n * \n * @author mycat\n */\npublic class CopyFail extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/CopyInResponse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * CopyInResponse (B)     \n * Byte1('G') Identifies the message as a Start Copy In response. \n *            The frontend must now send copy-in data (if not prepared \n *            to do so, send a CopyFail message).\n * Int32 Length of message contents in bytes, including self.\n * Int8 0 indicates the overall COPY format is textual (rows separated \n *      by newlines, columns separated by separator characters, etc). \n *      1 indicates the overall copy format is binary (similar to DataRow \n *      format). See COPY for more information.\n * Int16 The number of columns in the data to be copied (denoted N below).\n * Int16[N] The format codes to be used for each column. Each must presently \n *          be zero (text) or one (binary). All must be zero if the overall \n *          copy format is textual.\n * </pre>\n * \n * @author mycat\n */\npublic class CopyInResponse extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/CopyOutResponse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * CopyOutResponse (B) \n * Byte1('H') Identifies the message as a Start Copy Out response. \n *            This message will be followed by copy-out data. Int32 Length of\n *            message contents in bytes, including self. \n * Int8 0 indicates the overall COPY format is textual (rows separated by \n *      newlines, columns separated by separator characters, etc). 1 indicates \n *      the overall copy format is binary(similar to DataRow format). \n *      See COPY for more information. \n * Int16 The number of columns in the data to be copied (denoted N below). \n * Int16[N] The format codes to be used for each column. Each must presently \n *          be zero(text) or one (binary). All must be zero if the overall \n *          copy format is textual.\n * </pre>\n * \n * @author mycat\n */\npublic class CopyOutResponse extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/DataRow.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * DataRow (B) \n * Byte1('D') Identifies the message as a data row. \n * Int32 Length of message contents in bytes, including self. \n * Int16 The number of column values that follow (possibly zero). \n *       Next, the following pair of fields appear for each column: \n * Int32 The length of the column value, in bytes(this count does not \n *       include itself). Can be zero. As a special case, -1 indicates \n *       a NULL column value. No value bytes follow in the NULL case.\n * Byten The value of the column, in the format indicated by the associated\n *       format code. n is the above length.\n * </pre>\n * \n * @author mycat\n */\npublic class DataRow extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/Describe.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * Describe (F) \n * Byte1('D') Identifies the message as a Describe command.\n * Int32 Length of message contents in bytes, including self. \n * Byte1 'S' to describe a prepared statement; or 'P' to describe a portal. \n * String The name of the prepared statement or portal to describe (an empty \n *        string selects the unnamed prepared statement or portal).\n * </pre>\n * \n * @author mycat\n */\npublic class Describe extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/EmptyQueryResponse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * EmptyQueryResponse (B) \n * Byte1('I') Identifies the message as a response to an empty query \n *            string. (This substitutes for CommandComplete.) \n * Int32(4) Length of message contents in bytes, including self.\n * </pre>\n * \n * @author mycat\n */\npublic class EmptyQueryResponse extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/ErrorResponse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * ErrorResponse (B) \n * Byte1('E') Identifies the message as an error. \n * Int32 Length of message contents in bytes, including self. \n *       The message body consists of one or more identified fields, \n *       followed by a zero byte as a terminator. Fields can appear \n *       in any order. For each field there is the following: \n * Byte1 A code identifying the field type; if zero, this is the\n *       message terminator and no string follows. The presently defined \n *       field types are listed in Section 46.6. Since more field types \n *       might be added in future, frontends should silently ignore \n *       fields of unrecognized type.\n * String The field value.\n * </pre>\n * \n * @author mycat\n */\npublic class ErrorResponse extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/Execute.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * Execute (F) \n * Byte1('E') Identifies the message as an Execute command.\n * Int32 Length of message contents in bytes, including self. \n * String The name of the portal to execute (an empty string \n *        selects the unnamed portal). \n * Int32 Maximum number of rows to return, if portal contains a\n *       query that returns rows (ignored otherwise). \n *       Zero denotes \"no limit\".\n * </pre>\n * \n * @author mycat\n */\npublic class Execute extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/Flush.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * Flush (F) \n * Byte1('H') Identifies the message as a Flush command. \n * Int32(4) Length of message contents in bytes, including self.\n * </pre>\n * \n * @author mycat\n */\npublic class Flush extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/FunctionCall.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * FunctionCall (F) \n * Byte1('F') Identifies the message as a function call.\n * Int32 Length of message contents in bytes, including self. \n * Int32 Specifies the object ID of the function to call. \n * Int16 The number of argument format codes that follow (denoted C below). \n *       This can be zero to indicate that there are no arguments or that \n *       the arguments all use the default format (text); or one, in which \n *       case the specified format code is applied to all arguments; or it \n *       can equal the actual number of arguments.\n * Int16[C] The argument format codes. Each must presently be zero (text) or\n *          one (binary). \n * Int16 Specifies the number of arguments being supplied to the function. \n *       Next, the following pair of fields appear for each argument: \n * Int32 The length of the argument value, in bytes (this count does not include \n *       itself). Can be zero. As a special case, -1 indicates a NULL argument \n *       value. No value bytes follow in the NULL case. \n * Byten The value of the argument, in the format indicated by the associated \n *       format code. n is the above length. After the last argument, the \n *       following field appears: \n * Int16 The format code for the function result. Must presently be zero (text) \n *       or one (binary).\n * </pre>\n * \n * @author mycat\n */\npublic class FunctionCall extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/FunctionCallResponse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * FunctionCallResponse (B) \n * Byte1('V') Identifies the message as a function call result. \n * Int32 Length of message contents in bytes, including self.\n * Int32 The length of the function result value, in bytes (this count does\n *       not include itself). Can be zero. As a special case, -1 indicates a \n *       NULL function result. No value bytes follow in the NULL case. \n * Byten The value of the function result, in the format indicated by the \n *       associated format code. n is the above length.\n * </pre>\n * \n * @author mycat\n */\npublic class FunctionCallResponse extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/NoData.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * NoData (B) \n * Byte1('n') Identifies the message as a no-data indicator.\n * Int32(4) Length of message contents in bytes, including self.\n * </pre>\n * \n * @author mycat\n */\npublic class NoData extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/NoticeResponse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * NoticeResponse (B) \n * Byte1('N') Identifies the message as a notice. \n * Int32 Length of message contents in bytes, including self. The message \n *       body consists of one or more identified fields, followed by a zero \n *       byte as a terminator. Fields can appear in any order. For each \n *       field there is the following: \n * Byte1 A code identifying the field type; if zero, this is the message \n *       terminator and no string follows. The presently defined field types \n *       are listed in Section 46.6. Since more field types might be added\n *       in future, frontends should silently ignore fields of unrecognized \n *       type.\n * String The field value.\n * </pre>\n * \n * @author mycat\n */\npublic class NoticeResponse extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/NotificationResponse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * NotificationResponse (B) \n * Byte1('A') Identifies the message as a notification response. \n * Int32 Length of message contents in bytes,including self. \n * Int32 The process ID of the notifying backend process.\n * String The name of the channel that the notify has been raised on. \n * String The \"payload\" string passed from the notifying process.\n * </pre>\n * \n * @author mycat\n */\npublic class NotificationResponse extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/ParameterDescription.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * ParameterDescription (B) \n * Byte1('t') Identifies the message as a parameter description. \n * Int32 Length of message contents in bytes, including self.\n * Int16 The number of parameters used by the statement (can be zero). \n *       Then,for each parameter, there is the following: \n * Int32 Specifies the object ID of the parameter data type.\n * </pre>\n * \n * @author mycat\n */\npublic class ParameterDescription extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/ParameterStatus.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * ParameterStatus (B) \n * Byte1('S') Identifies the message as a run-time parameter status report. \n * Int32 Length of message contents in bytes,including self. \n * String The name of the run-time parameter being reported.\n * String The current value of the parameter.\n * </pre>\n * \n * @author mycat\n */\npublic class ParameterStatus extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/Parse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * Parse (F) \n * Byte1('P') Identifies the message as a Parse command. \n * Int32 Length of message contents in bytes, including self. \n * String The name of the destination prepared statement (an empty string \n *        selects the unnamed prepared statement). \n * String The query string to be parsed. \n * Int16 The number of parameter data types specified (can be zero). Note \n *       that this is not an indication of the number of parameters that \n *       might appear in the query string, only the number that the frontend \n *       wants to prespecify types for. Then, for each parameter, there is \n *       the following: \n * Int32 Specifies the object ID of the parameter data type. Placing a zero \n *       here is equivalent to leaving the type unspecified.\n * </pre>\n * \n * @author mycat\n */\npublic class Parse extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/ParseComplete.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * ParseComplete (B) \n * Byte1('1') Identifies the message as a Parse-complete indicator. \n * Int32(4) Length of message contents in bytes, including self.\n * </pre>\n * \n * @author mycat\n */\npublic class ParseComplete extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/PasswordMessage.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * PasswordMessage (F) \n * Byte1('p') Identifies the message as a password response. Note that this is \n *            also used for GSSAPI and SSPI response messages (which is really\n *            a design error, since the contained data is not a null-terminated \n *            string in that case, but can be arbitrary binary data).\n * Int32 Length of message contents in bytes, including self. \n * String The password(encrypted, if requested).\n * </pre>\n * \n * @author mycat\n */\npublic class PasswordMessage extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/PortalSuspended.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * PortalSuspended (B) \n * Byte1('s') Identifies the message as a portal-suspended indicator. Note this \n *            only appears if an Execute message's row-count limit was reached. \n * Int32(4) Length of message contents in bytes, including self.\n * </pre>\n * \n * @author mycat\n */\npublic class PortalSuspended extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/PostgresPacket.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * @see http://www.postgresql.org/docs/9.1/interactive/protocol.html\n * @author mycat\n */\npublic abstract class PostgresPacket {\n    /**\n     * <pre>\n     * AuthenticationOk (B)   \n     * AuthenticationKerberosV5 (B)       \n     * AuthenticationCleartextPassword (B)    \n     * AuthenticationMD5Password (B)    \n     * AuthenticationSCMCredential (B)     \n     * AuthenticationGSS (B)     \n     * AuthenticationSSPI (B)      \n     * AuthenticationGSSContinue (B)\n     * </pre>\n     */\n    public static final byte AUTHENTICATION = (byte) 'R';\n\n    /**\n     * BackendKeyData (B)\n     */\n    public static final byte BACKEND_KEY_DATA = (byte) 'K';\n\n    /**\n     * Bind (F)\n     */\n    public static final byte BIND = (byte) 'B';\n\n    /**\n     * BindComplete (B)\n     */\n    public static final byte BIND_COMPLETE = (byte) '2';\n\n    /**\n     * CancelRequest (F)\n     */\n\n    /**\n     * Close (F)\n     */\n    public static final byte CLOSE = (byte) 'C';\n\n    /**\n     * CloseComplete (B)\n     */\n    public static final byte CLOSE_COMPLETE = (byte) '3';\n\n    /**\n     * CommandComplete (B)\n     */\n    public static final byte COMMAND_COMPLETE = (byte) 'C';\n\n    /**\n     * CopyData (F & B)\n     */\n    public static final byte COPY_DATA = (byte) 'd';\n\n    /**\n     * CopyDone (F & B)\n     */\n    public static final byte COPY_DONE = (byte) 'c';\n\n    /**\n     * CopyFail (F)\n     */\n    public static final byte COPY_FAIL = (byte) 'f';\n\n    /**\n     * CopyInResponse (B)\n     */\n    public static final byte COPY_IN_RESPONSE = (byte) 'G';\n\n    /**\n     * CopyOutResponse (B)\n     */\n    public static final byte COPY_OUT_RESPONSE = (byte) 'H';\n\n    /**\n     * CopyBothResponse (B)\n     */\n    public static final byte COPY_BOTH_RESPONSE = (byte) 'W';\n\n    /**\n     * DataRow (B)\n     */\n    public static final byte DATA_ROW = (byte) 'D';\n\n    /**\n     * Describe (F)\n     */\n    public static final byte DESCRIBE = (byte) 'D';\n\n    /**\n     * EmptyQueryResponse (B)\n     */\n    public static final byte EMPTY_QUERY_RESPONSE = (byte) 'I';\n\n    /**\n     * ErrorResponse (B)\n     */\n    public static final byte ERROR_RESPONSE = (byte) 'E';\n\n    /**\n     * Execute (F)\n     */\n    public static final byte EXECUTE = (byte) 'E';\n\n    /**\n     * Flush (F)\n     */\n    public static final byte FLUSH = (byte) 'H';\n\n    /**\n     * FunctionCall (F)\n     */\n    public static final byte FUNCTION_CALL = (byte) 'F';\n\n    /**\n     * FunctionCallResponse (B)\n     */\n    public static final byte FUNCTION_CALL_RESPONSE = (byte) 'V';\n\n    /**\n     * NoData (B)\n     */\n    public static final byte NO_DATA = (byte) 'n';\n\n    /**\n     * NoticeResponse (B)\n     */\n    public static final byte NOTICE_RESPONSE = (byte) 'N';\n\n    /**\n     * NotificationResponse (B)\n     */\n    public static final byte NOTIFICATION_RESPONSE = (byte) 'A';\n\n    /**\n     * ParameterDescription (B)\n     */\n    public static final byte PARAMETER_DESCRIPTION = (byte) 't';\n\n    /**\n     * ParameterStatus (B)\n     */\n    public static final byte PARAMETER_STATUS = (byte) 'S';\n\n    /**\n     * Parse (F)\n     */\n    public static final byte PARSE = (byte) 'P';\n\n    /**\n     * ParseComplete (B)\n     */\n    public static final byte PARSE_COMPLETE = (byte) '1';\n\n    /**\n     * PasswordMessage (F)\n     */\n    public static final byte PASSWORD_MESSAGE = (byte) 'p';\n\n    /**\n     * PortalSuspended (B)\n     */\n    public static final byte PORTAL_SUSPENDED = (byte) 's';\n\n    /**\n     * Query (F)\n     */\n    public static final byte QUERY = (byte) 'Q';\n\n    /**\n     * ReadyForQuery (B)\n     */\n    public static final byte READY_FOR_QUERY = (byte) 'Z';\n\n    /**\n     * RowDescription (B)\n     */\n    public static final byte ROW_DESCRIPTION = (byte) 'T';\n\n    /**\n     * SSLRequest (F)\n     */\n\n    /**\n     * StartupMessage (F)\n     */\n\n    /**\n     * Sync (F)\n     */\n    public static final byte SYNC = (byte) 'S';\n\n    /**\n     * Terminate (F)\n     */\n    public static final byte TERMINATE = (byte) 'X';\n\n    private byte             type;\n    private int              length;\n    \n    public byte getType() {\n        return type;\n    }\n    \n    public void setType(byte type) {\n        this.type = type;\n    }\n    \n    public int getLength() {\n        return length;\n    }\n    \n    public void setLength(int length) {\n        this.length = length;\n    }\n    \n    \n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/Query.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * Query (F) \n * Byte1('Q') Identifies the message as a simple query. \n * Int32 Length of message contents in bytes, including self. \n * String The query string itself.\n * </pre>\n * \n * @author mycat\n */\npublic class Query extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/ReadyForQuery.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * ReadyForQuery (B) \n * Byte1('Z') Identifies the message type. ReadyForQuery is sent whenever the \n *            backend is ready for a new query cycle. \n * Int32(5) Length of message contents in bytes, including self. \n * Byte1 Current backend transaction status indicator. Possible values are 'I' \n *       if idle(not in a transaction block); 'T' if in a transaction block; \n *       or 'E' if in a failed transaction block (queries will be rejected until\n *       block is ended).\n * </pre>\n * \n * @author mycat\n */\npublic class ReadyForQuery extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/RowDescription.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * RowDescription (B) \n * Byte1('T') Identifies the message as a row description. \n * Int32 Length of message contents in bytes, including self.\n * Int16 Specifies the number of fields in a row (can be zero). Then, for\n *       each field,there is the following: String The field name. \n * Int32 If the field can be identified as a column of a specific table, \n *       the object ID of the table; otherwise zero. \n * Int16 If the field can be identified as a column of a specific table, the \n *       attribute number of the column; otherwise zero. \n * Int32 The object ID of the field's data type. \n * Int16 The data type size (see pg_type.typlen). Note that negative values \n *       denote variable-width types. \n * Int32 The type modifier (see pg_attribute.atttypmod). The meaning of the \n *       modifier is type-specific.\n * Int16 The format code being used for the field. Currently will be zero\n *       (text) or one (binary). In a RowDescription returned from the \n *       statement variant of Describe, the format code is not yet known and \n *       will always be zero.\n * </pre>\n * \n * @author mycat\n */\npublic class RowDescription extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/SSLRequest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * SSLRequest (F) \n * Int32(8) Length of message contents in bytes, including self. \n * Int32(80877103) The SSL request code. The value is chosen to contain 1234 in \n *                 the most significant 16 bits, and 5679 in the least 16 significant \n *                 bits. (To avoid confusion, this code must not be the same as any \n *                 protocol version number.)\n * </pre>\n * \n * @author mycat\n */\npublic class SSLRequest extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/StartupMessage.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * StartupMessage (F) \n * Int32 Length of message contents in bytes, including self. \n * Int32(196608) The protocol version number. The most significant 16 bits are \n *               the major version number (3 for the protocol described here).\n *               The least significant 16 bits are the minor version number (0 \n *               for the protocol described here). The protocol version number \n *               is followed by one or more pairs of parameter name and value \n *               strings. A zero byte is required as a terminator after the \n *               last name/value pair. Parameters can appear in any order. user \n *               is required, others are optional. Each parameter is specified as: \n * String The parameter name. Currently recognized names are: \n *        user The database user name to connect as. Required; there is no default. \n *        database The database to connect to. Defaults to the user name. \n *        options Command-line arguments for the backend. (This is deprecated in \n *                favor of setting individual run-time parameters.) In addition to\n *                the above, any run-time parameter that can be set at backend start \n *                time might be listed. Such settings will be applied during backend \n *                start (after parsing the command-line options if any). The values \n *                will act as session defaults. \n * String The parameter value.\n * </pre>\n * \n * @author mycat\n */\npublic class StartupMessage extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/Sync.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * Sync (F) \n * Byte1('S') Identifies the message as a Sync command. \n * Int32(4) Length of message contents in bytes, including self.\n * </pre>\n * \n * @author mycat\n */\npublic class Sync extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/net/postgres/Terminate.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.net.postgres;\n\n/**\n * <pre>\n * Terminate (F) \n * Byte1('X') Identifies the message as a termination.\n * Int32(4) Length of message contents in bytes, including self.\n * </pre>\n * \n * @author mycat\n */\npublic class Terminate extends PostgresPacket {\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/MyCATSequnceProcessor.java",
    "content": "package io.mycat.route;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.route.parser.druid.DruidSequenceHandler;\n\npublic class MyCATSequnceProcessor {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(MyCATSequnceProcessor.class);\n\t\n\t//使用Druid解析器实现sequence处理  @兵临城下\n\tprivate static final DruidSequenceHandler sequenceHandler = new DruidSequenceHandler(MycatServer\n\t\t\t.getInstance().getConfig().getSystem().getSequenceHandlerType(),MycatServer.getInstance().getConfig().getSystem().getSequnceHandlerPattern());\n\t\n\tprivate static class InnerMyCATSequnceProcessor{\n\t\tprivate static MyCATSequnceProcessor INSTANCE = new MyCATSequnceProcessor();\n\t}\n\t\n\tpublic static MyCATSequnceProcessor getInstance(){\n\t\treturn InnerMyCATSequnceProcessor.INSTANCE;\n\t}\n\t\n\tprivate MyCATSequnceProcessor() {\n\t}\n\n\t/**\n\t *  锁的粒度控制到序列级别.一个序列一把锁.\n\t *  如果是 db 方式, 可以 给 mycat_sequence表的 name 列 加索引.可以借助mysql 行级锁 提高并发 \n\t * @param pair\n\t */\n\tpublic void executeSeq(SessionSQLPair pair) {\n\t\ttry {\n\t\t\t/*// @micmiu 扩展NodeToString实现自定义全局序列号\n\t\t\tNodeToString strHandler = new ExtNodeToString4SEQ(MycatServer\n\t\t\t\t\t.getInstance().getConfig().getSystem()\n\t\t\t\t\t.getSequenceHandlerType());\n\t\t\t// 如果存在sequence 转化sequence为实际数值\n\t\t\tString charset = pair.session.getSource().getCharset();\n\t\t\tQueryTreeNode ast = SQLParserDelegate.parse(pair.sql,\n\t\t\t\t\tcharset == null ? \"utf-8\" : charset);\n\t\t\tString sql = strHandler.toString(ast);\n\t\t\tif (sql.toUpperCase().startsWith(\"SELECT\")) {\n\t\t\t\tString value=sql.substring(\"SELECT\".length()).trim();\n\t\t\t\toutRawData(pair.session.getSource(),value);\n\t\t\t\treturn;\n\t\t\t}*/\n\t\t\t\n\t\t\tString charset = pair.session.getSource().getCharset();\n\t\t\tString executeSql = sequenceHandler.getExecuteSql(pair,charset == null ? \"utf-8\":charset);\n\t\t\t\n\t\t\tpair.session.getSource().routeEndExecuteSQL(executeSql, pair.type,pair.schema);\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(\"MyCATSequenceProcessor.executeSeq(SesionSQLPair)\",e);\n\t\t\tpair.session.getSource().writeErrMessage(ErrorCode.ER_YES,\"mycat sequnce err.\" + e);\n\t\t\treturn;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/Procedure.java",
    "content": "package io.mycat.route;\n\nimport com.google.common.base.*;\n\nimport java.io.Serializable;\nimport java.sql.Types;\nimport java.util.*;\n\n/**\n * Created by magicdoom on 2016/3/24.\n *\n *\n * 1.no return\n\n ok\n\n\n 2.simple\n\n ok\n row\n eof\n\n\n 3.list\n\n\n row\n row\n row\n row\n eof\n ok\n\n */\npublic class Procedure implements Serializable\n{\n    private String originSql;\n    private String name;\n    private String callSql;\n    private String setSql ;\n    private String selectSql;\n    private Set<String> selectColumns=new LinkedHashSet<>();\n    private Set<String> listFields=new LinkedHashSet<>();\n    private boolean isResultList=false;\n\n    public boolean isResultList()\n    {\n        return isResultList;\n    }\n    public boolean isResultSimpleValue()\n    {\n        return selectSql!=null&&!isResultList;\n    }\n    public boolean isResultNothing()\n    {\n        return selectSql==null&&!isResultList;\n    }\n    public void setResultList(boolean resultList)\n    {\n        isResultList = resultList;\n    }\n\n    public String toPreCallSql(String dbType)\n    {\n        StringBuilder sb=new StringBuilder();\n        sb.append(\"{ call \")  ;\n        sb.append(this.getName()).append(\"(\") ;\n        Collection<ProcedureParameter> paramters=    this.getParamterMap().values();\n        int j=0;\n        for (ProcedureParameter paramter : paramters)\n        {\n\n            String name=\"?\";\n            String joinStr=  j==this.getParamterMap().size()-1?name:name+\",\" ;\n            sb.append(joinStr);\n            j++;\n        }\n        sb.append(\")}\")  ;\n        return sb.toString();\n    }\n\n    public String toChangeCallSql(String dbType)\n    {\n        StringBuilder sb=new StringBuilder();\n        sb.append(\"call \")  ;\n        sb.append(this.getName()).append(\"(\") ;\n        Collection<ProcedureParameter> paramters=    this.getParamterMap().values();\n        int j=0;\n        for (ProcedureParameter paramter : paramters)\n        {\n            Object value=paramter.getValue()!=null&& Types.VARCHAR==paramter.getJdbcType() ?\"'\"+paramter.getValue()+\"'\":paramter.getValue();\n             String name=paramter.getValue()==null?paramter.getName():String.valueOf(value);\n            String joinStr=  j==this.getParamterMap().size()-1?name:name+\",\" ;\n            sb.append(joinStr);\n            j++;\n        }\n        sb.append(\")\")  ;\n        if(isResultSimpleValue())\n        {\n            sb.append(\";select \");\n          sb.append(  Joiner.on(\",\").join(selectColumns)  );\n        }\n        return sb.toString();\n    }\n\n    public Set<String> getListFields()\n    {\n        return listFields;\n    }\n\n    public void setListFields(Set<String> listFields)\n    {\n        this.listFields = listFields;\n    }\n\n    public Set<String> getSelectColumns()\n    {\n        return selectColumns;\n    }\n\n    public String getSetSql()\n    {\n        return setSql;\n    }\n\n    public void setSetSql(String setSql)\n    {\n        this.setSql = setSql;\n    }\n\n    public String getSelectSql()\n    {\n        return selectSql;\n    }\n\n    public void setSelectSql(String selectSql)\n    {\n        this.selectSql = selectSql;\n    }\n\n    private Map<String,ProcedureParameter> paramterMap=new LinkedHashMap<>();\n\n    public String getOriginSql()\n    {\n        return originSql;\n    }\n\n    public void setOriginSql(String originSql)\n    {\n        this.originSql = originSql;\n    }\n\n    public Map<String, ProcedureParameter> getParamterMap()\n    {\n        return paramterMap;\n    }\n\n    public void setParamterMap(Map<String, ProcedureParameter> paramterMap)\n    {\n        this.paramterMap = paramterMap;\n    }\n\n    public String getName()\n    {\n        return name;\n    }\n\n    public void setName(String name)\n    {\n        this.name = name;\n    }\n\n    public String getCallSql()\n    {\n        return callSql;\n    }\n\n    public void setCallSql(String callSql)\n    {\n        this.callSql = callSql;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/ProcedureParameter.java",
    "content": "package io.mycat.route;\n\nimport java.io.Serializable;\nimport java.sql.Types;\n\n/**\n * Created by magicdoom on 2016/3/24.\n */\npublic class ProcedureParameter implements Serializable\n{\n    public static final String IN=\"in\";\n    public static final String OUT=\"out\";\n    public static final String INOUT=\"inout\";\n\n\n   private int index;\n    private String name;\n\n    //in out inout\n    private String parameterType;\n\n    //java.sql.Types\n    private int jdbcType= Types.VARCHAR;\n\n    private Object value;\n\n\n    public Object getValue()\n    {\n        return value;\n    }\n\n    public void setValue(Object value)\n    {\n        this.value = value;\n    }\n\n    public int getIndex()\n    {\n        return index;\n    }\n\n    public void setIndex(int index)\n    {\n        this.index = index;\n    }\n\n    public String getName()\n    {\n        return name;\n    }\n\n    public void setName(String name)\n    {\n        this.name = name;\n    }\n\n    public String getParameterType()\n    {\n        return parameterType;\n    }\n\n    public void setParameterType(String parameterType)\n    {\n        this.parameterType = parameterType;\n    }\n\n    public int getJdbcType()\n    {\n        return jdbcType;\n    }\n\n    public void setJdbcType(int jdbcType)\n    {\n        this.jdbcType = jdbcType;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/RouteCheckRule.java",
    "content": "package io.mycat.route;\n\nimport io.mycat.route.function.PartitionByCRC32PreSlot;\nimport io.mycat.route.function.PartitionByCRC32PreSlot.Range;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n *    迁移切换时准备切换阶段需要禁止写操作和读所有分片的sql\n */\npublic class RouteCheckRule {\n    public static ConcurrentMap<String,ConcurrentMap<String,List<Range>>> migrateRuleMap=new ConcurrentHashMap<>();\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/RouteResultset.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.route.parser.util.PageSQLUtil;\nimport io.mycat.sqlengine.mpp.HavingCols;\nimport io.mycat.util.FormatUtil;\n\nimport java.io.Serializable;\nimport java.util.*;\nimport java.util.Map.Entry;\n\n/**\n * @author mycat\n */\npublic final class RouteResultset implements Serializable {\n    private String statement; // 原始语句\n    private final int sqlType;\n    private RouteResultsetNode[] nodes; // 路由结果节点\n    private Set<String> subTables;\n    private SQLStatement sqlStatement;\n\n\n    private int limitStart;\n    private boolean cacheAble;\n    // used to store table's ID->datanodes cache\n    // format is table.primaryKey\n    private String primaryKey;\n    // limit output total\n    private int limitSize;\n    private SQLMerge sqlMerge;\n\n    private boolean callStatement = false; // 处理call关键字\n\n    // 是否为全局表，只有在insert、update、delete、ddl里会判断并修改。默认不是全局表，用于修正全局表修改数据的反馈。\n    private boolean globalTableFlag = false;\n\n    //是否完成了路由\n    private boolean isFinishedRoute = false;\n\n    //是否自动提交，此属性主要用于记录ServerConnection上的autocommit状态\n    private boolean autocommit = true;\n\n    private boolean isLoadData=false;\n\n    //是否可以在从库运行,此属性主要供RouteResultsetNode获取\n    private Boolean canRunInReadDB;\n\n    // 强制走 master，可以通过 RouteResultset的属性canRunInReadDB=false\n    // 传给 RouteResultsetNode 来实现，但是 强制走 slave需要增加一个属性来实现:\n    private Boolean runOnSlave = null;\t// 默认null表示不施加影响\n\n    //key=dataNode    value=slot\n    private Map<String,Integer>   dataNodeSlotMap=new HashMap<>();\n\n    private boolean selectForUpdate;\n    private boolean autoIncrement;\n    private Map<String, List<String>> subTableMaps;\n\n    public boolean isSelectForUpdate() {\n        return selectForUpdate;\n    }\n\n    public void setSelectForUpdate(boolean selectForUpdate) {\n        this.selectForUpdate = selectForUpdate;\n    }\n\n\n    private List<String> tables;\n\n    public List<String> getTables() {\n        return tables;\n    }\n\n    public void setTables(List<String> tables) {\n        this.tables = tables;\n    }\n\n    public Map<String, Integer> getDataNodeSlotMap() {\n        return dataNodeSlotMap;\n    }\n\n    public void setDataNodeSlotMap(Map<String, Integer> dataNodeSlotMap) {\n        this.dataNodeSlotMap = dataNodeSlotMap;\n    }\n\n    public Boolean getRunOnSlave() {\n        return runOnSlave;\n    }\n    public String getRunOnSlaveDebugInfo() {\n        return runOnSlave == null?\"default\":Boolean.toString(runOnSlave);\n    }\n    public void setRunOnSlave(Boolean runOnSlave) {\n        this.runOnSlave = runOnSlave;\n    }\n    private Procedure procedure;\n\n    public Procedure getProcedure()\n    {\n        return procedure;\n    }\n\n    public void setProcedure(Procedure procedure)\n    {\n        this.procedure = procedure;\n    }\n\n    public boolean isLoadData()\n    {\n        return isLoadData;\n    }\n\n    public void setLoadData(boolean isLoadData)\n    {\n        this.isLoadData = isLoadData;\n    }\n\n    public boolean isFinishedRoute() {\n        return isFinishedRoute;\n    }\n\n    public void setFinishedRoute(boolean isFinishedRoute) {\n        this.isFinishedRoute = isFinishedRoute;\n    }\n\n    public boolean isGlobalTable() {\n        return globalTableFlag;\n    }\n\n    public void setGlobalTable(boolean globalTableFlag) {\n        this.globalTableFlag = globalTableFlag;\n    }\n\n    public RouteResultset(String stmt, int sqlType) {\n        this.statement = stmt;\n        this.limitSize = -1;\n        this.sqlType = sqlType;\n    }\n\n    public void resetNodes() {\n        if (nodes != null) {\n            for (RouteResultsetNode node : nodes) {\n                node.resetStatement();\n            }\n        }\n    }\n\n    public void copyLimitToNodes() {\n\n        if(nodes!=null)\n        {\n            for (RouteResultsetNode node : nodes)\n            {\n                if(node.getLimitSize()==-1&&node.getLimitStart()==0)\n                {\n                    node.setLimitStart(limitStart);\n                    node.setLimitSize(limitSize);\n                }\n            }\n\n        }\n    }\n\n\n    public SQLMerge getSqlMerge() {\n        return sqlMerge;\n    }\n\n    public boolean isCacheAble() {\n        return cacheAble;\n    }\n\n    public void setCacheAble(boolean cacheAble) {\n        this.cacheAble = cacheAble;\n    }\n\n    public boolean needMerge() {\n        return limitSize > 0 || sqlMerge != null;\n    }\n\n    public int getSqlType() {\n        return sqlType;\n    }\n\n    public boolean isHasAggrColumn() {\n        return (sqlMerge != null) && sqlMerge.isHasAggrColumn();\n    }\n\n    public int getLimitStart() {\n        return limitStart;\n    }\n\n    public String[] getGroupByCols() {\n        return (sqlMerge != null) ? sqlMerge.getGroupByCols() : null;\n    }\n\n    private SQLMerge createSQLMergeIfNull() {\n        if (sqlMerge == null) {\n            sqlMerge = new SQLMerge();\n        }\n        return sqlMerge;\n    }\n\n    public Map<String, Integer> getMergeCols() {\n        return (sqlMerge != null) ? sqlMerge.getMergeCols() : null;\n    }\n\n    public void setLimitStart(int limitStart) {\n        this.limitStart = limitStart;\n    }\n\n    public String getPrimaryKey() {\n        return primaryKey;\n    }\n\n    public boolean hasPrimaryKeyToCache() {\n        return primaryKey != null;\n    }\n\n    public void setPrimaryKey(String primaryKey) {\n        if (!primaryKey.contains(\".\")) {\n            throw new java.lang.IllegalArgumentException(\n                \"must be table.primarykey fomat :\" + primaryKey);\n        }\n        this.primaryKey = primaryKey;\n    }\n\n    /**\n     * return primary key items ,first is table name ,seconds is primary key\n     *\n     * @return\n     */\n    public String[] getPrimaryKeyItems() {\n        return primaryKey.split(\"\\\\.\");\n    }\n\n    public void setOrderByCols(LinkedHashMap<String, Integer> orderByCols) {\n        if (orderByCols != null && !orderByCols.isEmpty()) {\n            createSQLMergeIfNull().setOrderByCols(orderByCols);\n        }\n    }\n\n    public void setHasAggrColumn(boolean hasAggrColumn) {\n        if (hasAggrColumn) {\n            createSQLMergeIfNull().setHasAggrColumn(true);\n        }\n    }\n\n    public void setGroupByCols(String[] groupByCols) {\n        if (groupByCols != null && groupByCols.length > 0) {\n            createSQLMergeIfNull().setGroupByCols(groupByCols);\n        }\n    }\n\n    public void setMergeCols(Map<String, Integer> mergeCols) {\n        if (mergeCols != null && !mergeCols.isEmpty()) {\n            createSQLMergeIfNull().setMergeCols(mergeCols);\n        }\n\n    }\n\n    public LinkedHashMap<String, Integer> getOrderByCols() {\n        return (sqlMerge != null) ? sqlMerge.getOrderByCols() : null;\n\n    }\n\n    public String getStatement() {\n        return statement;\n    }\n\n    public RouteResultsetNode[] getNodes() {\n        return nodes;\n    }\n\n    public void setNodes(RouteResultsetNode[] nodes) {\n        if(nodes!=null)\n        {\n            int nodeSize=nodes.length;\n            for (RouteResultsetNode node : nodes)\n            {\n                node.setTotalNodeSize(nodeSize);\n            }\n\n        }\n        this.nodes = nodes;\n    }\n\n    /**\n     * @return -1 if no limit\n     */\n    public int getLimitSize() {\n        return limitSize;\n    }\n\n    public void setLimitSize(int limitSize) {\n        this.limitSize = limitSize;\n    }\n\n    public void setStatement(String statement) {\n        this.statement = statement;\n    }\n\n    public boolean isCallStatement() {\n        return callStatement;\n    }\n\n    public void setCallStatement(boolean callStatement) {\n        this.callStatement = callStatement;\n        if(nodes!=null)\n        {\n            for (RouteResultsetNode node : nodes)\n            {\n                node.setCallStatement(callStatement);\n            }\n\n        }\n    }\n\n    public void changeNodeSqlAfterAddLimit(SchemaConfig schemaConfig, String sourceDbType, String sql, int offset, int count, boolean isNeedConvert) {\n        if (nodes != null)\n        {\n\n            Map<String, String> dataNodeDbTypeMap = schemaConfig.getDataNodeDbTypeMap();\n            Map<String, String> sqlMapCache = new HashMap<>();\n            for (RouteResultsetNode node : nodes)\n            {\n                String dbType = dataNodeDbTypeMap.get(node.getName());\n                if (dbType.equalsIgnoreCase(\"mysql\"))\n                {\n                    node.setStatement(sql);   //mysql之前已经加好limit\n                } else if (sqlMapCache.containsKey(dbType))\n                {\n                    node.setStatement(sqlMapCache.get(dbType));\n                } else if(isNeedConvert)\n                {\n                    String nativeSql = PageSQLUtil.convertLimitToNativePageSql(dbType, sql, offset, count);\n                    sqlMapCache.put(dbType, nativeSql);\n                    node.setStatement(nativeSql);\n                }  else {\n                    node.setStatement(sql);\n                }\n\n                node.setLimitStart(offset);\n                node.setLimitSize(count);\n            }\n\n\n        }\n    }\n\n    public boolean isAutocommit() {\n        return autocommit;\n    }\n\n    public void setAutocommit(boolean autocommit) {\n        this.autocommit = autocommit;\n    }\n\n    public Boolean getCanRunInReadDB() {\n        return canRunInReadDB;\n    }\n\n    public void setCanRunInReadDB(Boolean canRunInReadDB) {\n        this.canRunInReadDB = canRunInReadDB;\n    }\n\n    public HavingCols getHavingCols() {\n        return (sqlMerge != null) ? sqlMerge.getHavingCols() : null;\n    }\n\n    public void setSubTables(Set<String> subTables) {\n        this.subTables = subTables;\n    }\n\n    public void setHavings(HavingCols havings) {\n        if (havings != null) {\n            createSQLMergeIfNull().setHavingCols(havings);\n        }\n    }\n\n    // Added by winbill, 20160314, for having clause, Begin ==>\n    public void setHavingColsName(Object[] names) {\n        if (names != null && names.length > 0) {\n            createSQLMergeIfNull().setHavingColsName(names);\n        }\n    }\n    // Added by winbill, 20160314, for having clause, End  <==\n\n    public SQLStatement getSqlStatement() {\n        return this.sqlStatement;\n    }\n\n    public void setSqlStatement(SQLStatement sqlStatement) {\n        this.sqlStatement = sqlStatement;\n    }\n\n    public Set<String> getSubTables() {\n        return this.subTables;\n    }\n\n    public boolean isDistTable(){\n        if(this.getSubTables()!=null && !this.getSubTables().isEmpty() ){\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder s = new StringBuilder();\n        s.append(statement).append(\", route={\");\n        if (nodes != null) {\n            for (int i = 0; i < nodes.length; ++i) {\n                s.append(\"\\n \").append(FormatUtil.format(i + 1, 3));\n                s.append(\" -> \").append(nodes[i]);\n            }\n        }\n        s.append(\"\\n}\");\n        return s.toString();\n    }\n\n    public void setAutoIncrement(boolean b) {\n        autoIncrement = b;\n    }\n\n    public boolean getAutoIncrement() {\n        return autoIncrement;\n    }\n\n    public Map<String, List<String>> getSubTableMaps() {\n        return subTableMaps;\n    }\n\n    public void setSubTableMaps(Map<String, List<String>> subTableMaps) {\n        this.subTableMaps = subTableMaps;\n    }\n\n\t/**\n\t * 合并路由节点相同的节点\n\t */\n\tpublic void mergeSameNode() {\n\t\tMap<String, RouteResultsetNode> mapNodes = new HashMap<>(64);\n\t\tfor (RouteResultsetNode node : nodes) {\n\t\t\tif (mapNodes.containsKey(node.getName())) {\n\t\t\t\t// merge node\n\t\t\t\tRouteResultsetNode tmpNode = mapNodes.get(node.getName());\n\t\t\t\ttmpNode.setStatement(tmpNode.getStatement() + \";\" + node.getStatement());\n\t\t\t} else {\n\t\t\t\tmapNodes.put(node.getName(), node);\n\t\t\t}\n\t\t}\n\n\t\tRouteResultsetNode[] newNodes = new RouteResultsetNode[mapNodes.size()];\n\t\tint i = 0;\n\t\tfor (Entry<String, RouteResultsetNode> entry : mapNodes.entrySet()) {\n\t\t\tnewNodes[i++] = entry.getValue();\n\t\t}\n\t\tthis.setNodes(newNodes);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/RouteResultsetNode.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese\r\n * opensource volunteers. you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n *\r\n * Any questions about this component can be directed to it's project Web address\r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.route;\r\n\r\nimport java.io.Serializable;\r\nimport java.util.Map;\r\n\r\nimport com.google.common.base.Strings;\r\n\r\nimport io.mycat.server.parser.ServerParse;\r\nimport io.mycat.sqlengine.mpp.LoadData;\r\n\r\n/**\r\n * @author mycat\r\n */\r\npublic final class RouteResultsetNode implements Serializable , Comparable<RouteResultsetNode> {\r\n\t/**\r\n\t *\r\n\t */\r\n\tprivate static final long serialVersionUID = 1L;\r\n\tprivate final String name; // 数据节点名称\r\n\tprivate String statement; // 执行的语句\r\n\tprivate final String srcStatement;\r\n\tprivate final int sqlType;\r\n\tprivate volatile boolean canRunInReadDB;\r\n\tprivate final boolean hasBlanceFlag;\r\n    private boolean callStatement = false; // 处理call关键字\r\n\tprivate int limitStart;\r\n\tprivate int limitSize;\r\n\tprivate int totalNodeSize =0; //方便后续jdbc批量获取扩展\r\n   private Procedure procedure;\r\n\tprivate LoadData loadData;\r\n\tprivate RouteResultset source;\r\n\r\n\t// 强制走 master，可以通过 RouteResultset的属性canRunInReadDB(false)\r\n\t// 传给 RouteResultsetNode 来实现，但是 强制走 slave需要增加一个属性来实现:\r\n\tprivate Boolean runOnSlave = null;\t// 默认null表示不施加影响, true走slave,false走master\r\n\r\n\tprivate String subTableName; // 分表的表名\r\n\tprivate Map<String, String> subTableNames;//分表的表名集合\r\n\r\n\t//迁移算法用     -2代表不是slot分片  ，-1代表扫描所有分片\r\n\tprivate int slot=-2;\r\n\r\n\tpublic RouteResultsetNode(String name, int sqlType, String srcStatement) {\r\n\t\tthis.name = name;\r\n\t\tlimitStart=0;\r\n\t\tthis.limitSize = -1;\r\n\t\tthis.sqlType = sqlType;\r\n\t\tthis.srcStatement = srcStatement;\r\n\t\tthis.statement = srcStatement;\r\n\t\tcanRunInReadDB = (sqlType == ServerParse.SELECT || sqlType == ServerParse.SHOW);\r\n\t\thasBlanceFlag = (statement != null)\r\n\t\t\t\t&& statement.startsWith(\"/*balance*/\");\r\n\t}\r\n\r\n\tpublic Map<String, String> getSubTableNames() {\r\n      return subTableNames;\r\n    }\r\n\r\n    public void setSubTableNames(Map<String, String> subTableNames) {\r\n      this.subTableNames = subTableNames;\r\n    }\r\n\r\n    public Boolean getRunOnSlave() {\r\n\t\treturn runOnSlave;\r\n\t}\r\n\tpublic String getRunOnSlaveDebugInfo() {\r\n\t\treturn runOnSlave == null?\" default \":Boolean.toString(runOnSlave);\r\n\t}\r\n\tpublic boolean isUpdateSql() {\r\n\t\tint type=sqlType;\r\n\t\treturn ServerParse.INSERT==type||ServerParse.UPDATE==type||ServerParse.DELETE==type||ServerParse.DDL==type;\r\n\t}\r\n\tpublic void setRunOnSlave(Boolean runOnSlave) {\r\n\t\tthis.runOnSlave = runOnSlave;\r\n\t}\r\n\t  private Map hintMap;\r\n\r\n    public Map getHintMap()\r\n    {\r\n        return hintMap;\r\n    }\r\n\r\n    public void setHintMap(Map hintMap)\r\n    {\r\n        this.hintMap = hintMap;\r\n    }\r\n\r\n\tpublic void setStatement(String statement) {\r\n\t\tthis.statement = statement;\r\n\t}\r\n\r\n\tpublic void setCanRunInReadDB(boolean canRunInReadDB) {\r\n\t\tthis.canRunInReadDB = canRunInReadDB;\r\n\t}\r\n\r\n\tpublic boolean getCanRunInReadDB() {\r\n\t\treturn this.canRunInReadDB;\r\n\t}\r\n\r\n\tpublic void resetStatement() {\r\n\t\tthis.statement = srcStatement;\r\n\t}\r\n\r\n\t/**\r\n\t * 这里的逻辑是为了优化，实现：非业务sql可以在负载均衡走slave的效果。因为业务sql一般是非自动提交，\r\n\t * 而非业务sql一般默认是自动提交，比如mysql client，还有SQLJob, heartbeat都可以使用\r\n\t * 了Leader-us优化的query函数，该函数实现为自动提交；\r\n\t *\r\n\t * 在非自动提交的情况下(有事物)，除非使用了  balance 注解的情况下，才可以走slave.\r\n\t *\r\n\t * 当然还有一个大前提，必须是 select 或者 show 语句(canRunInReadDB=true)\r\n\t * @param autocommit\r\n\t * @return\r\n\t */\r\n\tpublic boolean canRunnINReadDB(boolean autocommit) {\r\n\t\treturn canRunInReadDB && ( autocommit || (!autocommit && hasBlanceFlag) );\r\n\t}\r\n\r\n//\tpublic boolean canRunnINReadDB(boolean autocommit) {\r\n//\t\treturn canRunInReadDB && autocommit && !hasBlanceFlag\r\n//\t\t\t|| canRunInReadDB && !autocommit && hasBlanceFlag;\r\n//\t}\r\n  public Procedure getProcedure()\r\n    {\r\n        return procedure;\r\n    }\r\n\r\n\tpublic int getSlot() {\r\n\t\treturn slot;\r\n\t}\r\n\r\n\tpublic void setSlot(int slot) {\r\n\t\tthis.slot = slot;\r\n\t}\r\n\r\n\tpublic void setProcedure(Procedure procedure)\r\n    {\r\n        this.procedure = procedure;\r\n    }\r\n\r\n    public boolean isCallStatement()\r\n    {\r\n        return callStatement;\r\n    }\r\n\r\n    public void setCallStatement(boolean callStatement)\r\n    {\r\n        this.callStatement = callStatement;\r\n    }\r\n\tpublic String getName() {\r\n\t\treturn name;\r\n\t}\r\n\r\n\tpublic int getSqlType() {\r\n\t\treturn sqlType;\r\n\t}\r\n\r\n\tpublic String getStatement() {\r\n\t\treturn statement;\r\n\t}\r\n\r\n\tpublic int getLimitStart()\r\n\t{\r\n\t\treturn limitStart;\r\n\t}\r\n\r\n\tpublic void setLimitStart(int limitStart)\r\n\t{\r\n\t\tthis.limitStart = limitStart;\r\n\t}\r\n\r\n\tpublic int getLimitSize()\r\n\t{\r\n\t\treturn limitSize;\r\n\t}\r\n\r\n\tpublic void setLimitSize(int limitSize)\r\n\t{\r\n\t\tthis.limitSize = limitSize;\r\n\t}\r\n\r\n\tpublic int getTotalNodeSize()\r\n\t{\r\n\t\treturn totalNodeSize;\r\n\t}\r\n\r\n\tpublic void setTotalNodeSize(int totalNodeSize)\r\n\t{\r\n\t\tthis.totalNodeSize = totalNodeSize;\r\n\t}\r\n\r\n\tpublic LoadData getLoadData()\r\n\t{\r\n\t\treturn loadData;\r\n\t}\r\n\r\n\tpublic void setLoadData(LoadData loadData)\r\n\t{\r\n\t\tthis.loadData = loadData;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int hashCode() {\r\n\t\treturn name.hashCode();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean equals(Object obj) {\r\n\t\tif (this == obj) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tif (obj instanceof RouteResultsetNode) {\r\n\t\t\tRouteResultsetNode rrn = (RouteResultsetNode) obj;\r\n\t\t\tif(subTableName!=null){\r\n\t\t\t\tif (equals(name, rrn.getName()) && equals(subTableName, rrn.getSubTableName())) {\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t}else{\r\n\t\t\t\tif (equals(name, rrn.getName())) {\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String toString() {\r\n\t\tStringBuilder s = new StringBuilder();\r\n\t\ts.append(name);\r\n\t\ts.append('{').append(statement).append('}');\r\n\t\treturn s.toString();\r\n\t}\r\n\r\n\tprivate static boolean equals(String str1, String str2) {\r\n\t\tif (str1 == null) {\r\n\t\t\treturn str2 == null;\r\n\t\t}\r\n\t\treturn str1.equals(str2);\r\n\t}\r\n\r\n\tpublic String getSubTableName() {\r\n\t\treturn this.subTableName;\r\n\t}\r\n\r\n\tpublic void setSubTableName(String subTableName) {\r\n\t\tthis.subTableName = subTableName;\r\n\t}\r\n\r\n\tpublic boolean isModifySQL() {\r\n\t\treturn !canRunInReadDB;\r\n\t}\r\n\r\n    /**\r\n     * 非DDL的修改语句，如INSERT,DELETE,UPDATE\r\n     * @return\r\n     */\r\n    public boolean isModifySQLExceptDDL() {\r\n        boolean isDDL = (sqlType == ServerParse.DDL);\r\n        return !canRunInReadDB && !isDDL;\r\n    }\r\n\r\n\tpublic boolean isDisctTable() {\r\n\t\tif(subTableName!=null && !subTableName.equals(\"\")){\r\n\t\t\treturn true;\r\n\t\t};\r\n\t\treturn false;\r\n\t}\r\n\r\n\r\n\t@Override\r\n\tpublic int compareTo(RouteResultsetNode obj) {\r\n\t\tif(obj == null) {\r\n\t\t\treturn 1;\r\n\t\t}\r\n\t\tif(this.name == null) {\r\n\t\t\treturn -1;\r\n\t\t}\r\n\t\tif(obj.name == null) {\r\n\t\t\treturn 1;\r\n\t\t}\r\n\t\tint c = this.name.compareTo(obj.name);\r\n\t\tif(!this.isDisctTable()||obj.subTableName == null){\r\n\t\t\treturn c;\r\n\t\t}else{\r\n\t\t\tif(c==0){\r\n\t\t\t    if (Strings.isNullOrEmpty(obj.subTableName)) {\r\n\t\t\t        return 1;\r\n\t\t\t    }\r\n\t\t\t\treturn this.subTableName.compareTo(obj.subTableName);\r\n\t\t\t}\r\n\t\t\treturn c;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic boolean isHasBlanceFlag() {\r\n\t\treturn hasBlanceFlag;\r\n\t}\r\n\r\n\tpublic RouteResultset getSource() {\r\n\t\treturn source;\r\n\t}\r\n\r\n\tpublic void setSource(RouteResultset source) {\r\n\t\tthis.source = source;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/route/RouteService.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route;\n\nimport java.sql.SQLNonTransientException;\nimport java.sql.SQLSyntaxErrorException;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentMap;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.cache.CachePool;\nimport io.mycat.cache.CacheService;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.route.function.PartitionByCRC32PreSlot;\nimport io.mycat.route.handler.HintHandler;\nimport io.mycat.route.handler.HintHandlerFactory;\nimport io.mycat.route.handler.HintSQLHandler;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\n\npublic class RouteService {\n    private static final Logger LOGGER = LoggerFactory\n            .getLogger(RouteService.class);\n    public static final String MYCAT_HINT_TYPE = \"_mycatHintType\";\n    private final CachePool sqlRouteCache;\n\tprivate final LayerCachePool tableId2DataNodeCache;\t\n\n\tprivate final String OLD_MYCAT_HINT = \"/*!mycat:\"; \t// 处理自定义分片注解, 注解格式：/*!mycat: type = value */ sql\n\tprivate final String NEW_MYCAT_HINT = \"/*#mycat:\"; \t// 新的注解格式:/* !mycat: type = value */ sql，oldMycatHint的格式不兼容直连mysql\n    private final String HINT_SPLIT = \"=\";\n\n\tpublic RouteService(CacheService cachService) {\n\t\tsqlRouteCache = cachService.getCachePool(\"SQLRouteCache\");\n\t\ttableId2DataNodeCache = (LayerCachePool) cachService\n\t\t\t\t.getCachePool(\"TableID2DataNodeCache\");\n\t}\n\n\tpublic LayerCachePool getTableId2DataNodeCache() {\n\t\treturn tableId2DataNodeCache;\n\t}\n\n\tpublic RouteResultset route(SystemConfig sysconf, SchemaConfig schema,\n\t\t\tint sqlType, String stmt, String charset, ServerConnection sc)\n\t\t\tthrows SQLNonTransientException {\n\t\tstmt = stmt.trim();\n\t\tRouteResultset rrs = null;\n\t\tString cacheKey = null;\n\n\t\t/**\n\t\t *  SELECT 类型的SQL, 检测\n\t\t */\n\t\tif (sqlType == ServerParse.SELECT) {\n\t\t\tcacheKey = schema.getName() + stmt;\t\t\t\n\t\t\trrs = (RouteResultset) sqlRouteCache.get(cacheKey);\n\t\t\tif (rrs != null) {\n\t\t\t\tcheckMigrateRule(schema.getName(),rrs,sqlType);\n\t\t\t\treturn rrs;\n\t\t\t}\n\t\t}\n        \n\t\t/*!mycat: sql = select name from aa */\n        /*!mycat: schema = test */\n//      boolean isMatchOldHint = stmt.startsWith(OLD_MYCAT_HINT);\n//      boolean isMatchNewHint = stmt.startsWith(NEW_MYCAT_HINT);\n//\t\tif (isMatchOldHint || isMatchNewHint ) {\n\t\tint hintLength = RouteService.isHintSql(stmt);\n\t\tif(hintLength != -1){\n\t\t\tint endPos = stmt.indexOf(\"*/\");\n\t\t\tif (endPos > 0) {\t\t\t\t\n\t\t\t\t// 用!mycat:内部的语句来做路由分析\n//\t\t\t\tint hintLength = isMatchOldHint ? OLD_MYCAT_HINT.length() : NEW_MYCAT_HINT.length();\n\t\t\t\tString hint = stmt.substring(hintLength, endPos).trim();\t\n\t\t\t\t\n                int firstSplitPos = hint.indexOf(HINT_SPLIT);                \n                if(firstSplitPos > 0 ){\n                    Map<String, String> hintMap = new HashMap<>(); // parseHint(hint);\n                    parseKeyValue(hintMap, hint);\n                \tString hintType = (String) hintMap.get(MYCAT_HINT_TYPE);\n                    String hintSql = (String) hintMap.get(hintType);\n                    if( hintSql.length() == 0 ) {\n                    \tLOGGER.warn(\"comment int sql must meet :/*!mycat:type=value*/ or /*#mycat:type=value*/ or /*mycat:type=value*/: \"+stmt);\n                    \tthrow new SQLSyntaxErrorException(\"comment int sql must meet :/*!mycat:type=value*/ or /*#mycat:type=value*/ or /*mycat:type=value*/: \"+stmt);\n                    }\n                    String realSQL = stmt.substring(endPos + \"*/\".length()).trim();\n\n                    HintHandler hintHandler = HintHandlerFactory.getHintHandler(hintType);\n                    if( hintHandler != null ) {    \n\n                    \tif ( hintHandler instanceof  HintSQLHandler) {                    \t\t\n                          \t/**\n                        \t * 修复 注解SQL的 sqlType 与 实际SQL的 sqlType 不一致问题， 如： hint=SELECT，real=INSERT\n                        \t * fixed by zhuam\n                        \t */\n                    \t\tint hintSqlType = ServerParse.parse( hintSql ) & 0xff;     \n                    \t\trrs = hintHandler.route(sysconf, schema, sqlType, realSQL, charset, sc, tableId2DataNodeCache, hintSql,hintSqlType,hintMap);\n                    \t\t\n                    \t} else {                    \t\t\n                    \t\trrs = hintHandler.route(sysconf, schema, sqlType, realSQL, charset, sc, tableId2DataNodeCache, hintSql,sqlType,hintMap);\n                    \t}\n \n                    }else{\n                        LOGGER.warn(\"TODO , support hint sql type : \" + hintType);\n                    }\n                    \n                }else{//fixed by runfriends@126.com\n                \tLOGGER.warn(\"comment in sql must meet :/*!mycat:type=value*/ or /*#mycat:type=value*/ or /*mycat:type=value*/: \"+stmt);\n                \tthrow new SQLSyntaxErrorException(\"comment in sql must meet :/*!mcat:type=value*/ or /*#mycat:type=value*/ or /*mycat:type=value*/: \"+stmt);\n                }\n\t\t\t}\n\t\t} else {\n\t\t\tstmt = stmt.trim();\n\t\t\trrs = RouteStrategyFactory.getRouteStrategy().route(sysconf, schema, sqlType, stmt,\n\t\t\t\t\tcharset, sc, tableId2DataNodeCache);\n\t\t}\n\n\t\tif (rrs != null && sqlType == ServerParse.SELECT && rrs.isCacheAble()) {\n\t\t\tsqlRouteCache.putIfAbsent(cacheKey, rrs);\n\t\t}\n\t\tcheckMigrateRule(schema.getName(),rrs,sqlType);\n\t\treturn rrs;\n\t}\n\n\t //数据迁移的切换准备阶段，需要拒绝写操作和所有的跨多节点写操作\n\t\tprivate   void checkMigrateRule(String schemal,RouteResultset rrs,int sqlType ) throws SQLNonTransientException {\n\t\tif(rrs!=null&&rrs.getTables()!=null){\n\t\t\tboolean isUpdate=isUpdateSql(sqlType);\n\t\t\tif(!isUpdate)return;\n\t\t\tConcurrentMap<String,List<PartitionByCRC32PreSlot.Range>> tableRules= RouteCheckRule.migrateRuleMap.get(schemal.toUpperCase()) ;\n\t\t\tif(tableRules!=null){\n\t\t\tfor (String table : rrs.getTables()) {\n\t\t\t\tList<PartitionByCRC32PreSlot.Range> rangeList= tableRules.get(table.toUpperCase()) ;\n\t\t\t\tif(rangeList!=null&&!rangeList.isEmpty()){\n\t\t\t\t\tif(rrs.getNodes().length>1&&isUpdate){\n\t\t\t\t\t\tthrow new   SQLNonTransientException (\"schema:\"+schemal+\",table:\"+table+\",sql:\"+rrs.getStatement()+\" is not allowed,because table is migrate switching,please wait for a moment\");\n\t\t\t\t\t}\n\t\t\t\t\tfor (PartitionByCRC32PreSlot.Range range : rangeList) {\n\t\t\t\t\t\tRouteResultsetNode[] routeResultsetNodes=\trrs.getNodes();\n\t\t\t\t\t\tfor (RouteResultsetNode routeResultsetNode : routeResultsetNodes) {\n\t\t\t\t\t\t\tint slot=routeResultsetNode.getSlot();\n\t\t\t\t\t\t\tif(isUpdate&&slot>=range.start&&slot<=range.end){\n\t\t\t\t\t\t\t\tthrow new   SQLNonTransientException (\"schema:\"+schemal+\",table:\"+table+\",sql:\"+rrs.getStatement()+\" is not allowed,because table is migrate switching,please wait for a moment\");\n\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\tprivate boolean isUpdateSql(int type) {\n\t\treturn ServerParse.INSERT==type||ServerParse.UPDATE==type||ServerParse.DELETE==type||ServerParse.DDL==type;\n\t}\n\n\tpublic static int isHintSql(String sql){\n\t\tint j = 0;\n\t\tint len = sql.length();\n\t\tif(sql.charAt(j++) == '/' && sql.charAt(j++) == '*'){\n\t\t\tchar c = sql.charAt(j);\n\t\t\t// 过滤掉 空格 和 * 两种字符, 支持： \"/** !mycat: */\" 和 \"/** #mycat: */\" 形式的注解\n\t\t\twhile(j < len && c != '!' && c != '#' && (c == ' ' || c == '*')){\n\t\t\t\tc = sql.charAt(++j);\n\t\t\t}\n\t\t\t//注解支持的'!'不被mysql单库兼容，\n\t\t\t//注解支持的'#'不被mybatis兼容\n\t\t\t//注解支持的':'不被hibernate兼容\n\t\t\t//考虑用mycat字符前缀标志Hintsql:\"/** mycat: */\"\n\t\t\tif(sql.charAt(j)=='m'){\n\t\t\t\tj--;\n\t\t\t}\n\t\t\tif(j + 6 >= len)\t{// prevent the following sql.charAt overflow\n\t\t\t\treturn -1;        // false\n\t\t\t}\n\t\t\tif(sql.charAt(++j) == 'm' && sql.charAt(++j) == 'y' && sql.charAt(++j) == 'c'\n\t\t\t\t&& sql.charAt(++j) == 'a' && sql.charAt(++j) == 't' && (sql.charAt(++j) == ':' || sql.charAt(j) == '#' || sql.charAt(j) == '-')) {\n\t\t\t\treturn j + 1;    // true，同时返回注解部分的长度\n\t\t\t}\n\t\t}\n\t\treturn -1;\t// false\n\t}\n\t\n    /**\n      最原始的代码，不知道要实现什么功能，注释掉。会有#2754 bug\n     */\n    @Deprecated\n\t private   Map parseHint( String sql)\n    {\n        Map map=new HashMap();\n        int y=0;\n        int begin=0;\n        for(int i=0;i<sql.length();i++)\n        {\n            char cur=sql.charAt(i);\n            if(cur==','&& y%2==0)\n            {\n                String substring = sql.substring(begin, i);\n\n                parseKeyValue(map, substring);\n                begin=i+1;\n            }\n            else\n            if(cur=='\\'')\n            {\n                y++;\n            } if(i==sql.length()-1)\n        {\n            parseKeyValue(map, sql.substring(begin));\n\n        }\n\n\n        }\n        return map;\n    }\n\n    private  void parseKeyValue(Map map, String substring)\n    {\n        int indexOf = substring.indexOf('=');\n        if(indexOf!=-1)\n        {\n\n            String key=substring.substring(0,indexOf).trim().toLowerCase();\n            String value=substring.substring(indexOf+1,substring.length());\n            if(value.endsWith(\"'\")&&value.startsWith(\"'\"))\n            {\n                value=value.substring(1,value.length()-1);\n            }\n            if(map.isEmpty())\n            {\n              map.put(MYCAT_HINT_TYPE,key)  ;\n            }\n            map.put(key,value.trim());\n\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/RouteStrategy.java",
    "content": "package io.mycat.route;\n\nimport java.sql.SQLNonTransientException;\n\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.server.ServerConnection;\n\n/**\n * 路由策略接口\n * @author wang.dw\n *\n */\npublic interface RouteStrategy {\n\tpublic RouteResultset route(SystemConfig sysConfig,\n\t\t\tSchemaConfig schema,int sqlType, String origSQL, String charset, ServerConnection sc, LayerCachePool cachePool)\n\t\t\tthrows SQLNonTransientException;\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/SQLMerge.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route;\n\nimport java.io.Serializable;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport io.mycat.sqlengine.mpp.HavingCols;\n\npublic class SQLMerge implements Serializable {\n\tprivate LinkedHashMap<String, Integer> orderByCols;\n\tprivate HavingCols havingCols;\n\tprivate Object[] havingColsName;\t\t\t// Added by winbill, 20160314, for having clause\n\tprivate Map<String, Integer> mergeCols;\n\tprivate String[] groupByCols;\n\tprivate boolean hasAggrColumn;\n\n\tpublic LinkedHashMap<String, Integer> getOrderByCols() {\n\t\treturn orderByCols;\n\t}\n\n\tpublic void setOrderByCols(LinkedHashMap<String, Integer> orderByCols) {\n\t\tthis.orderByCols = orderByCols;\n\t}\n\n\tpublic Map<String, Integer> getMergeCols() {\n\t\treturn mergeCols;\n\t}\n\n\tpublic void setMergeCols(Map<String, Integer> mergeCols) {\n\t\tthis.mergeCols = mergeCols;\n\t}\n\n\tpublic String[] getGroupByCols() {\n\t\treturn groupByCols;\n\t}\n\n\tpublic void setGroupByCols(String[] groupByCols) {\n\t\tthis.groupByCols = groupByCols;\n\t}\n\n\tpublic boolean isHasAggrColumn() {\n\t\treturn hasAggrColumn;\n\t}\n\n\tpublic void setHasAggrColumn(boolean hasAggrColumn) {\n\t\tthis.hasAggrColumn = hasAggrColumn;\n\t}\n\n\tpublic HavingCols getHavingCols() {\n\t\treturn havingCols;\n\t}\n\n\tpublic void setHavingCols(HavingCols havingCols) {\n\t\tthis.havingCols = havingCols;\n\t}\n\n\tpublic Object[] getHavingColsName() {\n\t\treturn havingColsName;\n\t}\n\n\tpublic void setHavingColsName(Object[] havingColsName) {\n\t\tthis.havingColsName = havingColsName;\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/SessionSQLPair.java",
    "content": "package io.mycat.route;\n\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.server.NonBlockingSession;\n\npublic class SessionSQLPair {\n\tpublic final NonBlockingSession session;\n\t\n\tpublic final SchemaConfig schema;\n\tpublic final String sql;\n\tpublic final int type;\n\n\tpublic SessionSQLPair(NonBlockingSession session, SchemaConfig schema,\n\t\t\tString sql,int type) {\n\t\tsuper();\n\t\tthis.session = session;\n\t\tthis.schema = schema;\n\t\tthis.sql = sql;\n\t\tthis.type=type;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/factory/RouteStrategyFactory.java",
    "content": "package io.mycat.route.factory;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.RouteStrategy;\nimport io.mycat.route.impl.DruidMycatRouteStrategy;\n\n/**\n * 路由策略工厂类\n * @author wang.dw\n *\n */\npublic class RouteStrategyFactory {\n\tprivate static RouteStrategy defaultStrategy = null;\n\tprivate static volatile boolean isInit = false;\n\tprivate static ConcurrentMap<String,RouteStrategy> strategyMap = new ConcurrentHashMap<String,RouteStrategy>();\n\tpublic static void init() {\n\t\tSystemConfig config = MycatServer.getInstance().getConfig().getSystem();\n\n\t\tString defaultSqlParser = config.getDefaultSqlParser();\n\t\tdefaultSqlParser = defaultSqlParser == null ? \"\" : defaultSqlParser;\n\t\t//修改为ConcurrentHashMap，避免并发问题\n\t\tstrategyMap.putIfAbsent(\"druidparser\", new DruidMycatRouteStrategy());\n\n\t\tdefaultStrategy = strategyMap.get(defaultSqlParser);\n\t\tif(defaultStrategy == null) {\n\t\t\tdefaultStrategy = strategyMap.get(\"druidparser\");\n\t\t\tdefaultSqlParser = \"druidparser\";\n\t\t}\n\t\tconfig.setDefaultSqlParser(defaultSqlParser);\n\t\tisInit = true;\n\t}\n\tprivate RouteStrategyFactory() {\n\t    \n\t}\n\n\t\n\tpublic static RouteStrategy getRouteStrategy() {\n//\t\tif(!isInit) {\n//\t\t\tsynchronized(RouteStrategyFactory.class){\n//\t\t\t\tif(!isInit){\n//\t\t\t\t\tinit();\n//\t\t\t\t}\n//\t\t\t}\n//\t\t}\n\t\treturn defaultStrategy;\n\t}\n\t\n\tpublic static RouteStrategy getRouteStrategy(String parserType) {\n//\t\tif(!isInit) {\n//\t\t\tsynchronized(RouteStrategyFactory.class){\n//\t\t\t\tif(!isInit){\n//\t\t\t\t\tinit();\n//\t\t\t\t}\n//\t\t\t}\n//\t\t}\n\t\treturn strategyMap.get(parserType);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/AbstractPartitionAlgorithm.java",
    "content": "package io.mycat.route.function;\n\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.config.model.rule.RuleAlgorithm;\n\nimport io.mycat.util.StringUtil;\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 路由分片函数抽象类\n * 为了实现一个默认的支持范围分片的函数 calcualteRange\n * 重写它以实现自己的范围路由规则\n * @author lxy\n *\n */\npublic abstract class AbstractPartitionAlgorithm implements RuleAlgorithm ,Serializable {\n\n\t@Override\n\tpublic void init() {\n\t}\n\n\t/**\n\t * 返回所有被路由到的节点的编号\n\t * 返回长度为0的数组表示所有节点都被路由（默认）\n\t * 返回null表示没有节点被路由到\n\t */\n\t@Override\n\tpublic Integer[] calculateRange(String beginValue, String endValue)  {\n\t\treturn new Integer[0];\n\t}\n\t\n\t/**\n\t * 对于存储数据按顺序存放的字段做范围路由，可以使用这个函数\n\t * @param algorithm\n\t * @param beginValue\n\t * @param endValue\n\t * @return\n\t */\n\tpublic static Integer[] calculateSequenceRange(AbstractPartitionAlgorithm algorithm, String beginValue, String endValue)  {\n\t\tInteger begin = 0, end = 0;\n\t\tbegin = algorithm.calculate(StringUtil.removeBackquote(beginValue));\n\t\tend = algorithm.calculate(StringUtil.removeBackquote(endValue));\n\n\t\tif(begin == null || end == null){\n\t\t\treturn new Integer[0];\n\t\t}\n\t\t\n\t\tif (end >= begin) {\n\t\t\tint len = end-begin+1;\n\t\t\tInteger [] re = new Integer[len];\n\t\t\t\n\t\t\tfor(int i =0;i<len;i++){\n\t\t\t\tre[i]=begin+i;\n\t\t\t}\n\t\t\t\n\t\t\treturn re;\n\t\t}else{\n\t\t\treturn new Integer[0];\n\t\t}\n\t}\n\t\n\t/**\n\t * \n\t * 分片表所跨的节点数与分片算法分区数一致性校验\n\t * @param tableConf\n\t * @return \n\t * -1 if table datanode size < rule function partition size\n\t * 0 if table datanode size == rule function partition size\n\t * 1 if table datanode size > rule function partition size\n\t */\n\tpublic final int suitableFor(TableConfig tableConf) {\n\t\tint nPartition = getPartitionNum();\n\t\tif(nPartition > 0) { // 对于有限制分区数的规则,进行检查\n\t\t\tint dnSize = tableConf.getDataNodes().size();\n\t\t\tboolean  distTable = tableConf.isDistTable();\n\t\t\tList tables = tableConf.getDistTables();\n\t\t\tif(distTable){\n\t\t\t\tif(tables.size() < nPartition){\n\t\t\t\t\treturn  -1;\n\t\t\t\t} else if(dnSize > nPartition) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tif(dnSize < nPartition) {\n\t\t\t\t\treturn  -1;\n\t\t\t\t} else if(dnSize > nPartition) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\t\n\t/**\n\t * 返回分区数, 返回-1表示分区数没有限制\n\t * @return\n\t */\n\tpublic int getPartitionNum() {\n\t\treturn -1; // 表示没有限制\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/AutoPartitionByLong.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.route.function;\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.InputStream;\r\nimport java.io.InputStreamReader;\r\nimport java.util.HashSet;\r\nimport java.util.LinkedList;\r\nimport java.util.Set;\r\n\r\nimport io.mycat.config.model.rule.RuleAlgorithm;\r\n\r\n/**\r\n * auto partition by Long ,can be used in auto increment primary key partition\r\n * \r\n * @author wuzhi\r\n */\r\npublic class AutoPartitionByLong extends AbstractPartitionAlgorithm implements RuleAlgorithm{\r\n\r\n\tprivate String mapFile;\r\n\tprivate LongRange[] longRongs;\r\n\t\r\n\tprivate int defaultNode = -1;\r\n\t@Override\r\n\tpublic void init() {\r\n\r\n\t\tinitialize();\r\n\t}\r\n\r\n\tpublic void setMapFile(String mapFile) {\r\n\t\tthis.mapFile = mapFile;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Integer calculate(String columnValue)  {\r\n//\t\tcolumnValue = NumberParseUtil.eliminateQoute(columnValue);\r\n\t\ttry {\r\n\t\t\tlong value = Long.parseLong(columnValue);\r\n\t\t\tInteger rst = null;\r\n\t\t\tfor (LongRange longRang : this.longRongs) {\r\n\t\t\t\tif (value <= longRang.valueEnd && value >= longRang.valueStart) {\r\n\t\t\t\t\treturn longRang.nodeIndx;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t//数据超过范围，暂时使用配置的默认节点\r\n\t\t\tif (rst == null && defaultNode >= 0) {\r\n\t\t\t\treturn defaultNode;\r\n\t\t\t}\r\n\t\t\treturn rst;\r\n\t\t} catch (NumberFormatException e){\r\n\t\t\tthrow new IllegalArgumentException(new StringBuilder().append(\"columnValue:\").append(columnValue).append(\" Please eliminate any quote and non number within it.\").toString(),e);\r\n\t\t}\r\n\t}\r\n\t\r\n\t@Override\r\n\tpublic Integer[] calculateRange(String beginValue, String endValue)  {\r\n\t\treturn AbstractPartitionAlgorithm.calculateSequenceRange(this, beginValue, endValue);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getPartitionNum() {\r\n//\t\tint nPartition = longRongs.length;\r\n\t\t\r\n\t\t/*\r\n\t\t * fix #1284 这里的统计应该统计Range的nodeIndex的distinct总数\r\n\t\t */\r\n\t\tSet<Integer> distNodeIdxSet = new HashSet<Integer>();\r\n\t\tfor(LongRange range : longRongs) {\r\n\t\t\tdistNodeIdxSet.add(range.nodeIndx);\r\n\t\t}\r\n\t\tint nPartition = distNodeIdxSet.size();\r\n\t\treturn nPartition;\r\n\t}\r\n\r\n\tprivate void initialize() {\r\n\t\tBufferedReader in = null;\r\n\t\ttry {\r\n\t\t\t// FileInputStream fin = new FileInputStream(new File(fileMapPath));\r\n\t\t\tInputStream fin = this.getClass().getClassLoader()\r\n\t\t\t\t\t.getResourceAsStream(mapFile);\r\n\t\t\tif (fin == null) {\r\n\t\t\t\tthrow new RuntimeException(\"can't find class resource file \"\r\n\t\t\t\t\t\t+ mapFile);\r\n\t\t\t}\r\n\t\t\tin = new BufferedReader(new InputStreamReader(fin));\r\n\t\t\tLinkedList<LongRange> longRangeList = new LinkedList<LongRange>();\r\n\r\n\t\t\tfor (String line = null; (line = in.readLine()) != null;) {\r\n\t\t\t\tline = line.trim();\r\n\t\t\t\tif (line.startsWith(\"#\") || line.startsWith(\"//\")) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\tint ind = line.indexOf('=');\r\n\t\t\t\tif (ind < 0) {\r\n\t\t\t\t\tSystem.out.println(\" warn: bad line int \" + mapFile + \" :\"\r\n\t\t\t\t\t\t\t+ line);\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\t\tString pairs[] = line.substring(0, ind).trim().split(\"-\");\r\n\t\t\t\t\tlong longStart = NumberParseUtil.parseLong(pairs[0].trim());\r\n\t\t\t\t\tlong longEnd = NumberParseUtil.parseLong(pairs[1].trim());\r\n\t\t\t\t\tint nodeId = Integer.parseInt(line.substring(ind + 1)\r\n\t\t\t\t\t\t\t.trim());\r\n\t\t\t\t\tlongRangeList\r\n\t\t\t\t\t\t\t.add(new LongRange(nodeId, longStart, longEnd));\r\n\r\n\t\t\t}\r\n\t\t\tlongRongs = longRangeList.toArray(new LongRange[longRangeList\r\n\t\t\t\t\t.size()]);\r\n\t\t} catch (Exception e) {\r\n\t\t\tif (e instanceof RuntimeException) {\r\n\t\t\t\tthrow (RuntimeException) e;\r\n\t\t\t} else {\r\n\t\t\t\tthrow new RuntimeException(e);\r\n\t\t\t}\r\n\r\n\t\t} finally {\r\n\t\t\ttry {\r\n\t\t\t\tin.close();\r\n\t\t\t} catch (Exception e2) {\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\t\r\n\tpublic int getDefaultNode() {\r\n\t\treturn defaultNode;\r\n\t}\r\n\r\n\tpublic void setDefaultNode(int defaultNode) {\r\n\t\tthis.defaultNode = defaultNode;\r\n\t}\r\n\r\n\tstatic class LongRange {\r\n\t\tpublic final int nodeIndx;\r\n\t\tpublic final long valueStart;\r\n\t\tpublic final long valueEnd;\r\n\r\n\t\tpublic LongRange(int nodeIndx, long valueStart, long valueEnd) {\r\n\t\t\tsuper();\r\n\t\t\tthis.nodeIndx = nodeIndx;\r\n\t\t\tthis.valueStart = valueStart;\r\n\t\t\tthis.valueEnd = valueEnd;\r\n\t\t}\r\n\r\n\t}\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/function/LatestMonthPartion.java",
    "content": "package io.mycat.route.function;\n\n/**\n * Latest one month data partions ,only reserve data of latest 31 days and one\n * day is partioned into N slide (splitOneDay), so total datanode is M*N table's\n * partion column must be int type and it's value format should be yyyyMMddHH\n * fomat for example colmn=2014050115 means: 15 clock of april 5 ,2014\n * \n * @author wuzhih\n * \n */\npublic class LatestMonthPartion extends AbstractPartitionAlgorithm {\n\tprivate int splitOneDay = 24;\n\tprivate int hourSpan;\n\tprivate String[] dataNodes;\n\n\tpublic String[] getDataNodes() {\n\t\treturn dataNodes;\n\t}\n\n\t/**\n\t * @param dataNodeExpression\n\t */\n\tpublic void setSplitOneDay(int split) {\n\t\tsplitOneDay = split;\n\t\thourSpan = 24 / splitOneDay;\n\t\tif (hourSpan * 24 < 24) {\n\t\t\tthrow new java.lang.IllegalArgumentException(\n\t\t\t\t\t\"invalid splitOnDay param:\"\n\t\t\t\t\t\t\t+ splitOneDay\n\t\t\t\t\t\t\t+ \" should be an even number and less or equals than 24\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic Integer calculate(String columnValue)  {\n\t\ttry {\n\t\t\tint valueLen = columnValue.length();\n\t\t\tint day = Integer.parseInt(columnValue.substring(valueLen - 4,\n\t\t\t\t\tvalueLen - 2));\n\t\t\tint hour = Integer.parseInt(columnValue.substring(valueLen - 2));\n\t\t\tint dnIndex = (day - 1) * splitOneDay + hour / hourSpan;\n\t\t\treturn dnIndex;\n\t\t}catch (NumberFormatException e){\n\t\t\tthrow new IllegalArgumentException(new StringBuilder().append(\"columnValue:\").append(columnValue).append(\" Please check if the format satisfied.\").toString(),e);\n\t\t}\n\t}\n\n\tpublic Integer[] calculateRange(String beginValue, String endValue)  {\n\t\treturn calculateSequenceRange(this,beginValue, endValue);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/NumberParseUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\npublic class NumberParseUtil {\n\t/**\n\t * 只去除开头结尾的引号，而且是结对去除，语法不对的话通不过\n\t * @param number\n\t * @return\n     */\n\tpublic static String eliminateQoute(String number){\n\t\tnumber = number.trim();\n\t\tif(number.contains(\"\\\"\")){\n\t\t\tif(number.charAt(0)=='\\\"'){\n\t\t\t\tnumber = number.substring(1);\n\t\t\t\tif(number.charAt(number.length()-1)=='\\\"'){\n\t\t\t\t\tnumber = number.substring(0,number.length()-1);\n\t\t\t\t}\n\t\t\t}\n\t\t}else if(number.contains(\"\\'\")){\n\t\t\tif(number.charAt(0)=='\\''){\n\t\t\t\tnumber = number.substring(1);\n\t\t\t\tif(number.charAt(number.length()-1)=='\\''){\n\t\t\t\t\tnumber = number.substring(0,number.length()-1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn number;\n\t}\n\n\t/**\n\t * can parse values like 200M ,200K,200M1(2000001)\n\t * \n\t * @param val\n\t * @return\n\t */\n\tpublic static long parseLong(String val) {\n\t\tval = val.toUpperCase();\n\t\tint indx = val.indexOf(\"M\");\n\n\t\tint plus = 10000;\n\t\tif (indx < 0) {\n\t\t\tindx = val.indexOf(\"K\");\n\t\t\tplus = 1000;\n\t\t}\n\t\tif (indx > 0) {\n\t\t\tString longVal = val.substring(0, indx);\n\n\t\t\tlong theVale = Long.parseLong(longVal) * plus;\n\t\t\tString remain = val.substring(indx + 1);\n\t\t\tif (remain.length() > 0) {\n\t\t\t\ttheVale += Integer.parseInt(remain);\n\t\t\t}\n\t\t\treturn theVale;\n\t\t} else {\n\t\t\treturn Long.parseLong(val);\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByCRC32PreSlot.java",
    "content": "package io.mycat.route.function;\n\nimport com.google.common.base.Joiner;\nimport com.google.common.base.Splitter;\nimport com.google.common.io.Files;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.config.model.rule.RuleAlgorithm;\nimport io.mycat.config.model.rule.RuleConfig;\nimport io.mycat.util.StringUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.*;\nimport java.nio.charset.Charset;\nimport java.util.*;\n\n/**\n * 自动迁移御用分片算法，预分slot 102400个，映射到dn上，再conf下会保存映射文件，请不要修改\n *\n * @author nange magicdoom@gmail.com\n */\npublic class PartitionByCRC32PreSlot extends AbstractPartitionAlgorithm\n        implements RuleAlgorithm, TableRuleAware, SlotFunction, ReloadFunction {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(\"PartitionByCRC32PreSlot\");\n\n    public static final int DEFAULT_SLOTS_NUM = 102400;\n\n    private static final Charset DEFAULT_CHARSET = Charset.forName(\"UTF-8\");\n    private Map<Integer, List<Range>> rangeMap = new TreeMap<>();\n\n    //slot:index\n    private int[] rangeMap2 = new int[DEFAULT_SLOTS_NUM];\n    private int slot = -1;\n\n    public Map<Integer, List<Range>> getRangeMap() {\n        return rangeMap;\n    }\n\n    public void saveSlotMapping(Map<Integer, List<Range>> rangeMap) {\n        this.rangeMap = rangeMap;\n\n        Properties prop = new Properties();\n        File file = getFile();\n        if (file.exists())\n            file.delete();\n        for (Map.Entry<Integer, List<Range>> integerListEntry : rangeMap.entrySet()) {\n            String key = String.valueOf(integerListEntry.getKey());\n            List<String> values = new ArrayList<>();\n            for (Range range : integerListEntry.getValue()) {\n                values.add(range.start + \"-\" + range.end);\n            }\n            prop.setProperty(key, Joiner.on(\",\").join(values));\n        }\n        try {\n            Files.createParentDirs(file);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n        try (FileOutputStream out = new FileOutputStream(file)) {\n            prop.store(out, \"WARNING   !!!Please do not modify or delete this file!!!\");\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n\n    }\n\n    private Properties loadProps(String name, boolean forceNew) {\n        Properties prop = new Properties();\n        File file = getFile();\n        if (file.exists() && forceNew)\n            file.delete();\n        if (!file.exists()) {\n            prop = genarateProperties();\n            try {\n                Files.createParentDirs(file);\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n            try (FileOutputStream out = new FileOutputStream(file)) {\n                prop.store(out, \"WARNING   !!!Please do not modify or delete this file!!!\");\n                out.flush();\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n            return prop;\n        }\n\n        try (FileInputStream filein = new FileInputStream(file)) {\n            prop.load(filein);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n        return prop;\n    }\n\n    private File getFile() {\n        return new File(SystemConfig.getHomePath(), \"conf\" + File.separator + \"ruledata\" + File.separator + ruleName + \".properties\");\n    }\n\n    /**\n     * 首次构造ruledata,根据table的dataNode数量构建Properties的分片范围\n     * @cjw\n     * @return\n     */\n    private Properties genarateProperties() {\n        int count = getCount();\n        int slotSize = DEFAULT_SLOTS_NUM / count;\n        Properties prop = new Properties();\n        for (int i = 0; i < count; i++) {\n            if (i == count - 1) {\n                prop.put(String.valueOf(i), i * slotSize + \"-\" + (DEFAULT_SLOTS_NUM - 1));\n            } else {\n                prop.put(String.valueOf(i), i * slotSize + \"-\" + ((i + 1) * slotSize - 1));\n            }\n        }\n\n        return prop;\n    }\n\n    private Map<Integer, List<Range>> convertToMap(Properties p) {\n        Map<Integer, List<Range>> map = new TreeMap<>();\n        for (Object o : p.keySet()) {\n            String k = (String) o;\n            String v = p.getProperty(k);\n            List<String> ranges = Splitter.on(\",\").omitEmptyStrings().trimResults().splitToList(v);\n            List<Range> rangeList = new ArrayList<>();\n            for (String range : ranges) {\n                List<String> vv = Splitter.on(\"-\").omitEmptyStrings().trimResults().splitToList(range);\n                if (vv.size() == 2) {\n                    Range ran = new Range(Integer.parseInt(vv.get(0)), Integer.parseInt(vv.get(1)));\n                    rangeList.add(ran);\n\n                } else if (vv.size() == 1) {\n                    Range ran = new Range(Integer.parseInt(vv.get(0)), Integer.parseInt(vv.get(0)));\n                    rangeList.add(ran);\n\n                } else {\n                    throw new RuntimeException(\"load crc32slot datafile error:dn=\" + k + \",value=\" + range);\n                }\n            }\n            map.put(Integer.parseInt(k), rangeList);\n        }\n\n        return map;\n    }\n\n    @Override\n    public void init() {\n\n        super.init();\n        if (ruleName != null) {\n            Properties p = loadProps(ruleName, false);\n            rangeMap = convertToMap(p);\n            checkSize();\n            hack();\n        }\n    }\n\n    private void checkSize(){\n        if (this.getCount() != this.rangeMap.size()){\n            throw new RuntimeException(ruleName + \"数量与dataNode数量不符\");\n        }\n    }\n\n    public void reInit() {\n\n        if (ruleName != null) {\n            Properties p = loadProps(ruleName, true);\n            rangeMap = convertToMap(p);\n            checkSize();\n            hack();\n        }\n    }\n\n\n    private void hack() {\n        //todo   优化\n        Iterator<Map.Entry<Integer, List<Range>>> iterator = rangeMap.entrySet().iterator();\n        while (iterator.hasNext()) {\n            Map.Entry<Integer, List<Range>> rangeEntry = iterator.next();\n            List<Range> range = rangeEntry.getValue();\n            for (Range range1 : range) {\n                for (int i = range1.start; i <= range1.end; i++) {\n                    rangeMap2[i] = rangeEntry.getKey();\n                }\n            }\n\n        }\n    }\n\n    @Override\n    public Integer calculate(String columnValue) {\n        if (ruleName == null)\n            throw new RuntimeException();\n        PureJavaCrc32 crc32 = new PureJavaCrc32();\n        byte[] bytes = columnValue.getBytes(DEFAULT_CHARSET);\n        crc32.update(bytes, 0, bytes.length);\n        long x = crc32.getValue();\n        int slot = (int) (x % DEFAULT_SLOTS_NUM);\n        this.slot = slot;\n        return rangeMap2[slot];\n//        //todo   优化\n//        for (Map.Entry<Integer, List<Range>> rangeEntry : rangeMap.entrySet()) {\n//            List<Range> range = rangeEntry.getValue();\n//            for (Range range1 : range) {\n//                if (slot >= range1.start && slot <= range1.end) {\n//                    this.slot = slot;\n//                    return rangeEntry.getKey();\n//                }\n//            }\n//\n//        }\n//        this.slot = slot;\n//        int slotSize = DEFAULT_SLOTS_NUM / count;\n//\n//        int index = slot / slotSize;\n//        if (slotSize * count != DEFAULT_SLOTS_NUM && index > count - 1) {\n//            index = (count - 1);\n//        }\n//        return index;\n    }\n\n    @Override\n    public int getPartitionNum() {\n        int count = getCount();\n        return count;\n    }\n\n    private static void hashTest() throws IOException {\n        PartitionByCRC32PreSlot hash = new PartitionByCRC32PreSlot();\n        hash.setRuleName(\"test\");\n        RuleConfig rule = new RuleConfig(\"id\", \"crc32slot\");\n        //考虑myccat1.65还有用户使用jdk7,故\n        int count = 1024;\n        String sb = genDataNodesString(count);\n        TableConfig tableConf = new TableConfig(\"test\", \"id\", true, false, -1, sb,\n                null, rule, true, null, false, null, null, null, false);\n\n        hash.setTableConfig(tableConf);\n        hash.reInit();\n        long start = System.currentTimeMillis();\n        int[] bucket = new int[hash.getCount()];\n\n        Map<Integer, List<Integer>> hashed = new HashMap<>();\n\n        int total = 1000_0000;//数据量\n        int c = 0;\n        for (int i = 100_0000; i < total + 100_0000; i++) {//假设分片键从100万开始\n            c++;\n            int h = hash.calculate(StringUtil.removeBackquote(Integer.toString(i)));\n            if (h >= count) {\n                System.out.println(\"error:\" + h);\n            }\n            bucket[h]++;\n            List<Integer> list = hashed.get(h);\n            if (list == null) {\n                list = new ArrayList<>();\n                hashed.put(h, list);\n            }\n            list.add(i);\n        }\n        System.out.println(c + \"   \" + total);\n        double d = 0;\n        c = 0;\n        int idx = 0;\n        System.out.println(\"index    bucket   ratio\");\n        for (int i : bucket) {\n            d += i / (double) total;\n            c += i;\n            System.out.println(idx++ + \"  \" + i + \"   \" + (i / (double) total));\n        }\n        System.out.println(d + \"  \" + c);\n\n        long used = System.currentTimeMillis() - start;\n\n        System.out.println(\"tps \" + total * 1000.0 / used);\n        System.out.println(\"****************************************************\");\n\n    }\n\n    public static String genDataNodesString(int count) {\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < count; i++) {//1024分片数\n            sb.append(\"db\").append(String.valueOf(i)).append(\",\");\n        }\n        sb.deleteCharAt(sb.length()-1);//cut last one ,\n        return sb.toString();\n    }\n\n    public static void main(String[] args) throws IOException {\n        hashTest();\n    }\n\n    private TableConfig tableConfig;\n    private String ruleName;\n\n    private int getCount() {\n        if (isIstance()){\n            return tableConfig.getDataNodes().size();\n        }\n        return 0;\n    }\n\n    @Override\n    public void setTableConfig(TableConfig tableConfig) {\n        this.tableConfig = tableConfig;\n    }\n\n    @Override\n    public void setRuleName(String ruleName) {\n        this.ruleName = ruleName;\n    }\n\n    @Override\n    public TableConfig getTableConfig() {\n        return this.tableConfig;\n    }\n\n    @Override\n    public String getRuleName() {\n        return ruleName;\n    }\n\n    @Override\n    public int slotValue() {\n        return slot;\n    }\n\n    @Override\n    public void reload() {\n        init();\n    }\n\n    @Override\n    public boolean isIstance() {\n        return this.tableConfig != null;\n    }\n\n    public static class Range implements Serializable {\n        public Range(int start, int end) {\n            this.start = start;\n            this.end = end;\n            size = end - start + 1;\n        }\n\n        public Range() {\n        }\n\n        public int start;\n        public int end;\n\n        public int size;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByDate.java",
    "content": "package io.mycat.route.function;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.config.model.rule.RuleAlgorithm;\n\n/**\n * 例子 按日期列分区  格式 between操作解析的范例\n *\n * @author lxy\n * @author Sean xiaoyouyy\n */\npublic class PartitionByDate extends AbstractPartitionAlgorithm implements RuleAlgorithm {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(PartitionByDate.class);\n\n\tprivate String sBeginDate;\n\tprivate String sEndDate;\n\tprivate String sPartionDay;\n\tprivate String dateFormat;\n\n\tprivate long beginDate;\n\tprivate long partionTime;\n\tprivate long endDate;\n\tprivate int nCount;\n\n\tprivate ThreadLocal<SimpleDateFormat> formatter;\n\n\tprivate static final long oneDay = 86400000;\n\t//支持自然日分区属性\n\tprivate String sNaturalDay;\n\t//是否自然日分区\n\tprivate boolean bNaturalDay;\n\t//自然日差额最少是28天\n\tprivate static final int naturalLimitDay =28;\n\t//开启自然日模式\n\tprivate static final String  naturalDayOpen =\"1\";\n\n\tprivate String oldsPartionDay;\n\t@Override\n\tpublic void init() {\n\t\ttry {\n\t\t\t//Support  Natural Day\n\t\t\tif(naturalDayOpen.equals(sNaturalDay)){\n\t\t\t\tbNaturalDay =true;\n\t\t\t\toldsPartionDay=sPartionDay;\n\t\t\t\tsPartionDay=\"1\";\n\t\t\t}\n\t\t\tif(sBeginDate!=null&&!sBeginDate.equals(\"\")) {\n\t\t\t\tpartionTime = Integer.parseInt(sPartionDay) * oneDay;\n\t\t\t\tbeginDate = new SimpleDateFormat(dateFormat).parse(sBeginDate).getTime();\n\t\t\t}\n\t\t\tif(sEndDate!=null&&!sEndDate.equals(\"\")&&beginDate>0){\n\t\t\t\tendDate = new SimpleDateFormat(dateFormat).parse(sEndDate).getTime();\n\t\t\t\tnCount = (int) ((endDate - beginDate) / partionTime) + 1;\n\t\t\t\tif(bNaturalDay&&nCount<naturalLimitDay){\n\t\t\t\t\tbNaturalDay =false;\n\t\t\t\t\tpartionTime = Integer.parseInt(oldsPartionDay) * oneDay;\n\t\t\t\t\tnCount = (int) ((endDate - beginDate) / partionTime) + 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tformatter = new ThreadLocal<SimpleDateFormat>() {\n\t\t\t\t@Override\n\t\t\t\tprotected SimpleDateFormat initialValue() {\n\t\t\t\t\treturn new SimpleDateFormat(dateFormat);\n\t\t\t\t}\n\t\t\t};\n\t\t} catch (ParseException e) {\n\t\t\tthrow new java.lang.IllegalArgumentException(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Integer calculate(String columnValue)  {\n\t\ttry {\n\t\t\tint targetPartition ;\n\t\t\tif(bNaturalDay){\n\t\t\t\tCalendar curTime = Calendar.getInstance();\n\t\t\t\tcurTime.setTime(formatter.get().parse(columnValue));\n\t\t\t\ttargetPartition = curTime.get(Calendar.DAY_OF_MONTH);\n\t\t\t\treturn  targetPartition-1;\n\t\t\t}\n\t\t\tlong targetTime = formatter.get().parse(columnValue).getTime();\n\t\t\ttargetPartition = (int) ((targetTime - beginDate) / partionTime);\n\t\t\tif(targetTime>endDate && nCount!=0) {\n\t\t\t\ttargetPartition = targetPartition % nCount;\n\t\t\t}\n\t\t\treturn targetPartition;\n\n\t\t} catch (ParseException e) {\n\t\t\tthrow new IllegalArgumentException(new StringBuilder().append(\"columnValue:\").append(columnValue).append(\" Please check if the format satisfied.\").toString(),e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Integer[] calculateRange(String beginValue, String endValue)  {\n\t\tSimpleDateFormat format = new SimpleDateFormat(this.dateFormat);\n\t\ttry {\n\t\t\tDate beginDate = format.parse(beginValue);\n\t\t\tDate endDate = format.parse(endValue);\n\t\t\tCalendar cal = Calendar.getInstance();\n\t\t\tList<Integer> list = new ArrayList<Integer>();\n\t\t\twhile(beginDate.getTime() <= endDate.getTime()){\n\t\t\t\tInteger nodeValue = this.calculate(format.format(beginDate));\n\t\t\t\tif(Collections.frequency(list, nodeValue) < 1) list.add(nodeValue);\n\t\t\t\tcal.setTime(beginDate);\n\t\t\t\tcal.add(Calendar.DATE, 1);\n\t\t\t\tbeginDate = cal.getTime();\n\t\t\t}\n\n\t\t\tInteger[] nodeArray = new Integer[list.size()];\n\t\t\tfor (int i=0;i<list.size();i++) {\n\t\t\t\tnodeArray[i] = list.get(i);\n\t\t\t}\n\n\t\t\treturn nodeArray;\n\t\t} catch (ParseException e) {\n\t\t\tLOGGER.error(\"error\",e);\n\t\t\treturn new Integer[0];\n\t\t}\n\t}\n\n\t@Override\n\tpublic int getPartitionNum() {\n\t\tint count = this.nCount;\n\t\treturn count > 0 ? count : -1;\n\t}\n\n\tpublic void setsBeginDate(String sBeginDate) {\n\t\tthis.sBeginDate = sBeginDate;\n\t}\n\n\tpublic void setsPartionDay(String sPartionDay) {\n\t\tthis.sPartionDay = sPartionDay;\n\t}\n\n\tpublic void setDateFormat(String dateFormat) {\n\t\tthis.dateFormat = dateFormat;\n\t}\n\tpublic String getsEndDate() {\n\t\treturn this.sEndDate;\n\t}\n\tpublic void setsEndDate(String sEndDate) {\n\t\tthis.sEndDate = sEndDate;\n\t}\n\n\tpublic String getsNaturalDay() {\n\t\treturn sNaturalDay;\n\t}\n\n\tpublic void setsNaturalDay(String sNaturalDay) {\n\t\tthis.sNaturalDay = sNaturalDay;\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByFileMap.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.route.function;\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.InputStream;\r\nimport java.io.InputStreamReader;\r\nimport java.util.HashMap;\r\nimport java.util.HashSet;\r\nimport java.util.Map;\r\nimport java.util.Set;\r\n\r\nimport io.mycat.config.model.rule.RuleAlgorithm;\r\n\r\n/**\r\n * \r\n * @author mycat\r\n */\r\npublic class PartitionByFileMap extends AbstractPartitionAlgorithm implements RuleAlgorithm {\r\n\r\n\tprivate String mapFile;\r\n\tprivate Map<Object, Integer> app2Partition;\r\n\t/**\r\n\t * Map<Object, Integer> app2Partition中key值的类型：默认值为0，0表示Integer，非零表示String\r\n\t */\r\n\tprivate int type;\r\n\t\r\n\t/**\r\n\t * 默认节点在map中的key\r\n\t */\r\n\tprivate static final String DEFAULT_NODE = \"DEFAULT_NODE\";\r\n\t\r\n\t/**\r\n\t * 默认节点:小于0表示不设置默认节点，大于等于0表示设置默认节点\r\n\t * \r\n\t * 默认节点的作用：枚举分片时，如果碰到不识别的枚举值，就让它路由到默认节点\r\n\t *                如果不配置默认节点（defaultNode值小于0表示不配置默认节点），碰到\r\n\t *                不识别的枚举值就会报错，\r\n\t *                like this：can't find datanode for sharding column:column_name val:ffffffff    \r\n\t */\r\n\tprivate int defaultNode = -1;\r\n\r\n\t@Override\r\n\tpublic void init() {\r\n\r\n\t\tinitialize();\r\n\t}\r\n\r\n\tpublic void setMapFile(String mapFile) {\r\n\t\tthis.mapFile = mapFile;\r\n\t}\r\n\t\r\n\tpublic void setType(int type) {\r\n\t\tthis.type = type;\r\n\t}\r\n\r\n\tpublic void setDefaultNode(int defaultNode) {\r\n\t\tthis.defaultNode = defaultNode;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Integer calculate(String columnValue)  {\r\n\t\ttry {\r\n\t\t\tObject value = columnValue;\r\n\t\t\tif (type == 0) {\r\n\t\t\t\tvalue = Integer.valueOf(columnValue);\r\n\t\t\t}\r\n\t\t\tInteger rst = null;\r\n\t\t\tInteger pid = app2Partition.get(value);\r\n\t\t\tif (pid != null) {\r\n\t\t\t\trst = pid;\r\n\t\t\t} else {\r\n\t\t\t\trst = app2Partition.get(DEFAULT_NODE);\r\n\t\t\t}\r\n\t\t\treturn rst;\r\n\t\t} catch (NumberFormatException e){\r\n\t\t\tthrow new IllegalArgumentException(new StringBuilder().append(\"columnValue:\").append(columnValue).append(\" Please check if the format satisfied.\").toString(),e);\r\n\t\t}\r\n\t}\r\n\t\r\n\t@Override\r\n\tpublic int getPartitionNum() {\r\n\t\tSet<Integer> set = new HashSet<Integer>(app2Partition.values());\r\n\t\tint count = set.size();\r\n\t\treturn count;\r\n\t}\r\n\r\n\tprivate void initialize() {\r\n\t\tBufferedReader in = null;\r\n\t\ttry {\r\n\t\t\t// FileInputStream fin = new FileInputStream(new File(fileMapPath));\r\n\t\t\tInputStream fin = this.getClass().getClassLoader()\r\n\t\t\t\t\t.getResourceAsStream(mapFile);\r\n\t\t\tif (fin == null) {\r\n\t\t\t\tthrow new RuntimeException(\"can't find class resource file \"\r\n\t\t\t\t\t\t+ mapFile);\r\n\t\t\t}\r\n\t\t\tin = new BufferedReader(new InputStreamReader(fin));\r\n\t\t\t\r\n\t\t\tapp2Partition = new HashMap<Object, Integer>();\r\n\t\t\t\r\n\t\t\tfor (String line = null; (line = in.readLine()) != null;) {\r\n\t\t\t\tline = line.trim();\r\n\t\t\t\tif (line.startsWith(\"#\") || line.startsWith(\"//\")) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\tint ind = line.indexOf('=');\r\n\t\t\t\tif (ind < 0) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\ttry {\r\n\t\t\t\t\tString key = line.substring(0, ind).trim();\r\n\t\t\t\t\tint pid = Integer.parseInt(line.substring(ind + 1).trim());\r\n\t\t\t\t\tif(type == 0) {\r\n\t\t\t\t\t\tapp2Partition.put(Integer.parseInt(key), pid);\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tapp2Partition.put(key, pid);\r\n\t\t\t\t\t}\r\n\t\t\t\t} catch (Exception e) {\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t//设置默认节点\r\n\t\t\tif(defaultNode >= 0) {\r\n\t\t\t\tapp2Partition.put(DEFAULT_NODE, defaultNode);\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\tif (e instanceof RuntimeException) {\r\n\t\t\t\tthrow (RuntimeException) e;\r\n\t\t\t} else {\r\n\t\t\t\tthrow new RuntimeException(e);\r\n\t\t\t}\r\n\r\n\t\t} finally {\r\n\t\t\ttry {\r\n\t\t\t\tin.close();\r\n\t\t\t} catch (Exception e2) {\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByHashMod.java",
    "content": "package io.mycat.route.function;\n\nimport io.mycat.config.model.rule.RuleAlgorithm;\n\nimport java.math.BigInteger;\n\n/**\n * 哈希值取模\n * 根据分片列的哈希值对分片个数取模，哈希算法为Wang/Jenkins\n * 用法和简单取模相似，规定分片个数和分片列即可。\n *\n * @author Hash Zhang\n */\npublic class PartitionByHashMod extends AbstractPartitionAlgorithm implements RuleAlgorithm {\n    private boolean watch = false;\n    private int count;\n\n    public void setCount(int count) {\n        this.count = count;\n        if ((count & (count - 1)) == 0) {\n            watch = true;\n        }\n    }\n\n    /**\n     * Using Wang/Jenkins Hash\n     *\n     * @param key\n     * @return hash value\n     */\n    protected int hash(int key) {\n        key = (~key) + (key << 21); // key = (key << 21) - key - 1;\n        key = key ^ (key >> 24);\n        key = (key + (key << 3)) + (key << 8); // key * 265\n        key = key ^ (key >> 14);\n        key = (key + (key << 2)) + (key << 4); // key * 21\n        key = key ^ (key >> 28);\n        key = key + (key << 31);\n        return key;\n    }\n\n    @Override\n    public Integer calculate(String columnValue) {\n//        columnValue = columnValue.replace(\"\\'\", \" \");\n//        columnValue = columnValue.trim();\n        BigInteger bigNum = new BigInteger(hash(columnValue.hashCode()) + \"\").abs();\n        // if count==2^n, then m%count == m&(count-1)\n        if (watch) {\n            return bigNum.intValue() & (count - 1);\n        }\n        return (bigNum.mod(BigInteger.valueOf(count))).intValue();\n    }\n\n    @Override\n    public void init() {\n        super.init();\n    }\n\n\t@Override\n\tpublic int getPartitionNum() {\n\t\tint count = this.count;\n\t\treturn count;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByHotDate.java",
    "content": "package io.mycat.route.function;\n\nimport io.mycat.util.StringUtil;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.model.rule.RuleAlgorithm;\n\n/**\n * 根据日期查询日志数据 冷热数据分布 ，最近n个月的到实时交易库查询，超过n个月的按照m天分片\n * \n * @author sw\n * \n * <tableRule name=\"sharding-by-date\">\n      <rule>\n        <columns>create_time</columns>\n        <algorithm>sharding-by-hotdate</algorithm>\n      </rule>\n   </tableRule>  \n<function name=\"sharding-by-hotdate\" class=\"org.opencloudb.route.function.PartitionByHotDate\">\n    <property name=\"dateFormat\">yyyy-MM-dd</property>\n    <property name=\"sLastDay\">10</property>\n    <property name=\"sPartionDay\">30</property>\n  </function>\n */\npublic class PartitionByHotDate extends AbstractPartitionAlgorithm implements RuleAlgorithm {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(PartitionByHotDate.class);\n\n\tprivate String dateFormat;\n\tprivate String sLastDay;\n\tprivate String sPartionDay;\n\n\tprivate long sLastTime;\n\tprivate long partionTime;\n\tprivate ThreadLocal<SimpleDateFormat> formatter;\n\t\n\tprivate long beginDate;\n\n\tprivate static final long oneDay = 86400000;\n\n\t@Override\n\tpublic void init() {\n\t\ttry {\n\t\t\tformatter = new ThreadLocal<SimpleDateFormat>() {\n\t\t\t\t@Override\n\t\t\t\tprotected SimpleDateFormat initialValue() {\n\t\t\t\t\treturn new SimpleDateFormat(dateFormat);\n\t\t\t\t}\n\t\t\t};\n\t\t\tsLastTime = Integer.valueOf(sLastDay);\n            partionTime = Integer.parseInt(sPartionDay) * oneDay;\n\t\t} catch (Exception e) {\n\t\t\tthrow new java.lang.IllegalArgumentException(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Integer calculate(String columnValue)  {\n\t\tInteger targetPartition = -1;\n\t\ttry {\n\t\t\tlong targetTime = formatter.get().parse(columnValue).getTime();\n\t\t\tCalendar now = Calendar.getInstance();\n\t\t\tlong nowTime = now.getTimeInMillis();\n\t\t\t\n\t\t\tbeginDate = nowTime - sLastTime * oneDay;\n\t\t\t\n\t\t\tlong diffDays = (nowTime - targetTime) / (1000 * 60 * 60 * 24) + 1;\n\t\t\tif(diffDays-sLastTime <= 0 || diffDays<0 ){\n\t\t\t\ttargetPartition = 0;\n\t\t\t}else{\n\t\t\t\ttargetPartition = (int) ((beginDate - targetTime) / partionTime) + 1;\n\t\t\t}\n\t\t\t\n\t\t    LOGGER.debug(\"PartitionByHotDate calculate for \" + columnValue + \" return \" + targetPartition);\n\t\t\treturn targetPartition;\n\t\t} catch (ParseException e) {\n\t\t\tthrow new IllegalArgumentException(new StringBuilder().append(\"columnValue:\").append(columnValue).append(\" Please check if the format satisfied.\").toString(),e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Integer[] calculateRange(String beginValue, String endValue)  {\n\t\tInteger[] targetPartition = null;\n\t\ttry {\n\t\t\tlong startTime = formatter.get().parse(beginValue).getTime();\n\t\t\tlong endTime = formatter.get().parse(endValue).getTime();\n\t\t\tCalendar now = Calendar.getInstance();\n\t\t\tlong nowTime = now.getTimeInMillis();\n\t\t\t\n\t\t\tlong limitDate = nowTime - sLastTime * oneDay;\n\t\t\tlong diffDays = (nowTime - startTime) / (1000 * 60 * 60 * 24) + 1;\n\t\t\tif(diffDays-sLastTime <= 0 || diffDays<0 ){\n\t\t\t\tInteger [] re = new Integer[1];\n\t\t\t\tre[0] = 0;\n\t\t\t\ttargetPartition = re ;\n\t\t\t}else{\n\t\t\t\tInteger [] re = null;\n\t\t\t\tInteger begin = 0, end = 0;\n\t\t\t\tend = this.calculate(StringUtil.removeBackquote(beginValue));\n\t\t\t\tboolean hasLimit = false;\n\t\t\t\tif(endTime-limitDate > 0){\n\t\t\t\t\tendTime = limitDate;\n\t\t\t\t\thasLimit = true;\n\t\t\t\t}\n\t\t\t\tbegin = this.calculate(StringUtil.removeBackquote(formatter.get().format(endTime)));\n\t\t\t\tif(begin == null || end == null){\n\t\t\t\t\treturn re;\n\t\t\t\t}\n\t\t\t\tif (end >= begin) {\n\t\t\t\t\tint len = end-begin+1;\n\t\t\t\t\tif(hasLimit){\n\t\t\t\t\t\tre = new Integer[len+1];\n\t\t\t\t\t\tre[0] = 0;\n\t\t\t\t\t\tfor(int i =0;i<len;i++){\n\t\t\t\t\t\t\tre[i+1]=begin+i;\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\tre = new Integer[len];\n\t\t\t\t\t\tfor(int i=0;i<len;i++){\n\t\t\t\t\t\t\tre[i]=begin+i;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn re;\n\t\t\t\t}else{\n\t\t\t\t\treturn re;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (ParseException e) {\n\t\t\tthrow new IllegalArgumentException(new StringBuilder().append(\"endValue:\").append(endValue).append(\" Please check if the format satisfied.\").toString(),e);\n\t\t}\n\t\treturn targetPartition;\n\t}\n\n\tpublic void setsPartionDay(String sPartionDay) {\n\t\tthis.sPartionDay = sPartionDay;\n\t}\n\tpublic void setDateFormat(String dateFormat) {\n\t\tthis.dateFormat = dateFormat;\n\t}\n\tpublic String getsLastDay() {\n\t\treturn sLastDay;\n\t}\n\tpublic void setsLastDay(String sLastDay) {\n\t\tthis.sLastDay = sLastDay;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByJumpConsistentHash.java",
    "content": "package io.mycat.route.function;\n\nimport io.mycat.config.model.rule.RuleAlgorithm;\n\n/**\n * 跳增一致性哈希分片\n * 思想源自Google公开论文，比传统一致性哈希更省资源速度更快数据迁移量更少\n *\n * @author XiaoSK\n */\npublic final class PartitionByJumpConsistentHash extends AbstractPartitionAlgorithm\n        implements RuleAlgorithm  {\n\n    private static final long UNSIGNED_MASK = 0x7fffffffffffffffL;\n    private static final long JUMP = 1L << 31;\n    // If JDK >= 1.8, just use Long.parseUnsignedLong(\"2862933555777941757\") instead.\n    private static final long CONSTANT = Long.parseLong(\"286293355577794175\", 10) * 10 + 7;\n\n    private int totalBuckets;\n\n    @Override\n    public Integer calculate(String columnValue) {\n        return jumpConsistentHash(columnValue.hashCode(), totalBuckets);\n    }\n    \n\t@Override\n\tpublic int getPartitionNum() {\n\t\tint nPartition = this.totalBuckets;\n\t\treturn nPartition;\n\t}\n\n\tpublic static int jumpConsistentHash(final long key, final int buckets) {\n        checkBuckets(buckets);\n        long k = key;\n        long b = -1;\n        long j = 0;\n\n        while (j < buckets) {\n            b = j;\n            k = k * CONSTANT + 1L;\n\n            j = (long) ((b + 1L) * (JUMP / toDouble((k >>> 33) + 1L)));\n        }\n        return (int) b;\n    }\n\n    private static void checkBuckets(final int buckets) {\n        if (buckets < 0) {\n            throw new IllegalArgumentException(\"Buckets cannot be less than 0\");\n        }\n    }\n\n    private static double toDouble(final long n) {\n        double d = n & UNSIGNED_MASK;\n        if (n < 0) {\n            d += 0x1.0p63;\n        }\n        return d;\n    }\n\n    public void setTotalBuckets(int totalBuckets) {\n        this.totalBuckets = totalBuckets;\n    }\n\n    public int getTotalBuckets() {\n        return totalBuckets;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByLong.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.route.function;\r\n\r\nimport io.mycat.config.model.rule.RuleAlgorithm;\r\nimport io.mycat.route.util.PartitionUtil;\r\n\r\npublic final class PartitionByLong extends AbstractPartitionAlgorithm implements RuleAlgorithm {\r\n\tprotected int[] count;\r\n\tprotected int[] length;\r\n\tprotected PartitionUtil partitionUtil;\r\n\r\n\tprivate static int[] toIntArray(String string) {\r\n\t\tString[] strs = io.mycat.util.SplitUtil.split(string, ',', true);\r\n\t\tint[] ints = new int[strs.length];\r\n\t\tfor (int i = 0; i < strs.length; ++i) {\r\n\t\t\tints[i] = Integer.parseInt(strs[i]);\r\n\t\t}\r\n\t\treturn ints;\r\n\t}\r\n\r\n\tpublic void setPartitionCount(String partitionCount) {\r\n\t\tthis.count = toIntArray(partitionCount);\r\n\t}\r\n\r\n\tpublic void setPartitionLength(String partitionLength) {\r\n\t\tthis.length = toIntArray(partitionLength);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void init() {\r\n\t\tpartitionUtil = new PartitionUtil(count, length);\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Integer calculate(String columnValue)  {\r\n//\t\tcolumnValue = NumberParseUtil.eliminateQoute(columnValue);\r\n\t\ttry {\r\n\t\t\tlong key = Long.parseLong(columnValue);\r\n\t\t\tkey = (key >>> 32) ^ key;\r\n\t\t\treturn partitionUtil.partition(key);\r\n\t\t} catch (NumberFormatException e){\r\n\t\t\tthrow new IllegalArgumentException(new StringBuilder().append(\"columnValue:\").append(columnValue).append(\" Please eliminate any quote and non number within it.\").toString(),e);\r\n\t\t}\r\n\t}\r\n\t\r\n\t@Override\r\n\tpublic Integer[] calculateRange(String beginValue, String endValue)  {\r\n\t\treturn AbstractPartitionAlgorithm.calculateSequenceRange(this, beginValue, endValue);\r\n\t}\r\n\r\n//\t@Override\r\n//\tpublic int getPartitionCount() {\r\n//\t\tint nPartition = 0;\r\n//\t\tfor(int i = 0; i < count.length; i++) {\r\n//\t\t\tnPartition += count[i];\r\n//\t\t}\r\n//\t\treturn nPartition;\r\n//\t}\r\n\t\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByMod.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\nimport io.mycat.util.StringUtil;\nimport java.math.BigInteger;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.config.model.rule.RuleAlgorithm;\n\n/**\n * number column partion by Mod operator\n * if count is 10 then 0 to 0,21 to 1 (21 % 10 =1)\n * @author wuzhih\n *\n */\npublic class PartitionByMod extends AbstractPartitionAlgorithm implements RuleAlgorithm  {\n\n\tprivate int count;\n\t@Override\n\tpublic void init() {\n\t\n\t\t\n\t}\n\n\n\n\tpublic void setCount(int count) {\n\t\tthis.count = count;\n\t}\n\n\t@Override\n\tpublic Integer calculate(String columnValue)  {\n//\t\tcolumnValue = NumberParseUtil.eliminateQoute(columnValue);\n\t\ttry {\n\t\t\tBigInteger bigNum = new BigInteger(columnValue).abs();\n\t\t\treturn (bigNum.mod(BigInteger.valueOf(count))).intValue();\n\t\t} catch (NumberFormatException e){\n            // throw new IllegalArgumentException(new\n            // StringBuilder().append(\"columnValue:\").append(columnValue).append(\" Please\n            // eliminate any quote and non number within it.\").toString(),e);\n //           int value = Math.abs((columnValue).hashCode());\n//            return value % count;\n\t\t\t/**\n \t\t\t* hashcode后的值使用int类型超出数值范围时，返回负数，报数组下标越界\n \t\t\t* date 2020/12/21\n \t\t\t*/\n\t\t\tlong value = Math.abs(Long.valueOf((columnValue).hashCode()));\n\t\t\treturn (int)(value%count);\n\t\t}\n\n\t}\n\t\n\n\t@Override\n\tpublic int getPartitionNum() {\n\t\tint nPartition = this.count;\n\t\treturn nPartition;\n\t}\n\n\tprivate static void hashTest()  {\n\t\tPartitionByMod hash=new PartitionByMod();\n\t\thash.setCount(11);\n\t\thash.init();\n\t\t\n\t\tint[] bucket=new int[hash.count];\n\t\t\n\t\tMap<Integer,List<Integer>> hashed=new HashMap<>();\n\t\t\n\t\tint total=1000_0000;//数据量\n\t\tint c=0;\n\t\tfor(int i=100_0000;i<total+100_0000;i++){//假设分片键从100万开始\n\t\t\tc++;\n\t\t\tint h=hash.calculate(StringUtil.removeBackquote(Integer.toString(i)));\n\t\t\tbucket[h]++;\n\t\t\tList<Integer> list=hashed.get(h);\n\t\t\tif(list==null){\n\t\t\t\tlist=new ArrayList<>();\n\t\t\t\thashed.put(h, list);\n\t\t\t}\n\t\t\tlist.add(i);\n\t\t}\n\t\tSystem.out.println(c+\"   \"+total);\n\t\tdouble d=0;\n\t\tc=0;\n\t\tint idx=0;\n\t\tSystem.out.println(\"index    bucket   ratio\");\n\t\tfor(int i:bucket){\n\t\t\td+=i/(double)total;\n\t\t\tc+=i;\n\t\t\tSystem.out.println(idx+++\"  \"+i+\"   \"+(i/(double)total));\n\t\t}\n\t\tSystem.out.println(d+\"  \"+c);\n\t\t\n\t\tSystem.out.println(\"****************************************************\");\n\t\trehashTest(hashed.get(0));\n\t}\n\tprivate static void rehashTest(List<Integer> partition)  {\n\t\tPartitionByMod hash=new PartitionByMod();\n\t\thash.count=110;//分片数\n\t\thash.init();\n\t\t\n\t\tint[] bucket=new int[hash.count];\n\t\t\n\t\tint total=partition.size();//数据量\n\t\tint c=0;\n\t\tfor(int i:partition){//假设分片键从100万开始\n\t\t\tc++;\n\t\t\tint h=hash.calculate(StringUtil.removeBackquote(Integer.toString(i)));\n\t\t\tbucket[h]++;\n\t\t}\n\t\tSystem.out.println(c+\"   \"+total);\n\t\tc=0;\n\t\tint idx=0;\n\t\tSystem.out.println(\"index    bucket   ratio\");\n\t\tfor(int i:bucket){\n\t\t\tc+=i;\n\t\t\tSystem.out.println(idx+++\"  \"+i+\"   \"+(i/(double)total));\n\t\t}\n\t}\n\tpublic static void main(String[] args)  {\n//\t\thashTest();\n\t\tPartitionByMod partitionByMod = new PartitionByMod();\n\t\tpartitionByMod.count=8;\n\t\tpartitionByMod.calculate(\"\\\"6\\\"\");\n\t\tpartitionByMod.calculate(\"\\'6\\'\");\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByMonth.java",
    "content": "package io.mycat.route.function;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.apache.log4j.Logger;\n\nimport io.mycat.config.model.rule.RuleAlgorithm;\nimport io.mycat.util.StringUtil;\n\n/**\n * 例子 按月份列分区 ，每个自然月一个分片，格式 between操作解析的范例\n *\n * @author wzh\n *\n */\npublic class PartitionByMonth extends AbstractPartitionAlgorithm implements\n    RuleAlgorithm {\n\n    private static final Logger LOGGER = Logger.getLogger(PartitionByDate.class);\n    private String sBeginDate;\n    /** 默认格式 */\n    private String dateFormat = \"yyyy-MM-dd\";\n    /** 场景 */\n    private int scene = -1;\n    private String sEndDate;\n    private Calendar beginDate;\n    private Calendar endDate;\n    private int nPartition;\n\n    private ThreadLocal<SimpleDateFormat> formatter;\n\n    @Override\n    public void init() {\n        try {\n            if (StringUtil.isEmpty(sBeginDate) && StringUtil.isEmpty(sEndDate)) {\n                nPartition = 12;\n                scene = 1;\n                initFormatter();\n                beginDate = Calendar.getInstance();\n                beginDate.set(Calendar.MONTH, 0);\n                endDate = Calendar.getInstance();\n                endDate.set(Calendar.MONTH, 11);\n                return;\n            }\n            beginDate = Calendar.getInstance();\n            beginDate.setTime(new SimpleDateFormat(dateFormat)\n                .parse(sBeginDate));\n            initFormatter();\n            if (sEndDate != null && !sEndDate.equals(\"\")) {\n                endDate = Calendar.getInstance();\n                endDate.setTime(new SimpleDateFormat(dateFormat).parse(sEndDate));\n                nPartition = ((endDate.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR)) * 12\n                    + endDate.get(Calendar.MONTH) - beginDate.get(Calendar.MONTH)) + 1;\n\n                if (nPartition <= 0) {\n                    throw new java.lang.IllegalArgumentException(\"Incorrect time range for month partitioning!\");\n                }\n            } else {\n                nPartition = -1;\n            }\n        } catch (ParseException e) {\n            throw new java.lang.IllegalArgumentException(e);\n        }\n    }\n\n    private void initFormatter() {\n        formatter = new ThreadLocal<SimpleDateFormat>() {\n            @Override\n            protected SimpleDateFormat initialValue() {\n                return new SimpleDateFormat(dateFormat);\n            }\n        };\n    }\n\n    /**\n     * For circulatory partition, calculated value of target partition needs to be\n     * rotated to fit the partition range\n     */\n    private int reCalculatePartition(int targetPartition) {\n        // 没有指定end_date，不是循环使用的情况，直接返回对应的targetPartition\n        if (nPartition == -1) {\n            return targetPartition;\n        }\n        /**\n         * If target date is previous of start time of partition setting, shift\n         * the delta range between target and start date to be positive value\n         */\n        if (targetPartition < 0) {\n            targetPartition = nPartition - (-targetPartition) % nPartition;\n        }\n\n        if (targetPartition >= nPartition) {\n            targetPartition = targetPartition % nPartition;\n        }\n\n        return targetPartition;\n    }\n\n    @Override\n    public Integer calculate(String columnValue) {\n        try {\n            if (scene == 1) {\n                Calendar curTime = Calendar.getInstance();\n                curTime.setTime(formatter.get().parse(columnValue));\n                return curTime.get(Calendar.MONTH);\n            }\n            int targetPartition;\n            Calendar curTime = Calendar.getInstance();\n            curTime.setTime(formatter.get().parse(columnValue));\n            targetPartition = ((curTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR))\n                * 12 + curTime.get(Calendar.MONTH)\n                - beginDate.get(Calendar.MONTH));\n\n            /**\n             * For circulatory partition, calculated value of target partition needs to be\n             * rotated to fit the partition range\n             */\n            if (nPartition > 0) {\n                targetPartition = reCalculatePartition(targetPartition);\n            }\n            // 防止越界的情况\n            if (targetPartition < 0) {\n                targetPartition = 0;\n            }\n            return targetPartition;\n\n        } catch (ParseException e) {\n            throw new IllegalArgumentException(new StringBuilder().append(\"columnValue:\").append(columnValue)\n                .append(\" Please check if the format satisfied.\").toString(), e);\n        }\n    }\n\n    @Override\n    public Integer[] calculateRange(String beginValue, String endValue) {\n        try {\n            return doCalculateRange(beginValue, endValue, beginDate);\n        } catch (ParseException e) {\n            LOGGER.error(\"error\", e);\n            return new Integer[0];\n        }\n    }\n\n    private Integer[] doCalculateRange(String beginValue, String endValue, Calendar beginDate) throws ParseException {\n        int startPartition, endPartition;\n        Calendar partitionTime = Calendar.getInstance();\n        SimpleDateFormat format = new SimpleDateFormat(dateFormat);\n        partitionTime.setTime(format.parse(beginValue));\n        startPartition = ((partitionTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR))\n            * 12 + partitionTime.get(Calendar.MONTH)\n            - beginDate.get(Calendar.MONTH));\n        partitionTime.setTime(format.parse(endValue));\n        endPartition = ((partitionTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR))\n            * 12 + partitionTime.get(Calendar.MONTH)\n            - beginDate.get(Calendar.MONTH));\n\n        List<Integer> list = new ArrayList<>();\n\n        while (startPartition <= endPartition) {\n            Integer nodeValue = reCalculatePartition(startPartition);\n            if (nodeValue < 0) {\n                nodeValue = 0;\n            }\n            if (Collections.frequency(list, nodeValue) < 1) {\n                list.add(nodeValue);\n            }\n            startPartition++;\n        }\n        int size = list.size();\n        // 当在场景1： \"2015-01-01\", \"2014-04-03\" 范围出现的时候\n        // 是应该返回null 还是返回 [] ?\n        return (list.toArray(new Integer[size]));\n    }\n\n    @Override\n    public int getPartitionNum() {\n        int nPartition = this.nPartition;\n        return nPartition;\n    }\n\n    public void setsBeginDate(String sBeginDate) {\n        this.sBeginDate = sBeginDate;\n    }\n\n    public void setDateFormat(String dateFormat) {\n        this.dateFormat = dateFormat;\n    }\n\n    public void setsEndDate(String sEndDate) {\n        this.sEndDate = sEndDate;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByMonthAndHistory.java",
    "content": "package io.mycat.route.function;\n\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.apache.log4j.Logger;\n\nimport io.mycat.config.model.rule.RuleAlgorithm;\n\n/**\n * 例子 按月份列分区 ，每个自然月一个分片，格式 between操作解析的范例\n * \n * @author wzh\n * \n */\npublic class PartitionByMonthAndHistory extends AbstractPartitionAlgorithm implements\n        RuleAlgorithm {\n    private static final Logger LOGGER = Logger.getLogger(PartitionByMonth.class);\n    private String sBeginDate;\n    private String dateFormat;\n    private String sEndDate;\n    private Calendar beginDate;\n    private Calendar endDate;\n    private int nPartition;\n\n    private ThreadLocal<SimpleDateFormat> formatter;\n\n    @Override\n    public void init() {\n        try {\n            beginDate = Calendar.getInstance();\n            beginDate.setTime(new SimpleDateFormat(dateFormat)\n                    .parse(sBeginDate));\n            formatter = new ThreadLocal<SimpleDateFormat>() {\n                @Override\n                protected SimpleDateFormat initialValue() {\n                    return new SimpleDateFormat(dateFormat);\n                }\n            };\n            if(sEndDate!=null&&!sEndDate.equals(\"\")) {\n                endDate = Calendar.getInstance();\n                endDate.setTime(new SimpleDateFormat(dateFormat).parse(sEndDate));\n                nPartition = ((endDate.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR)) * 12\n                                + endDate.get(Calendar.MONTH) - beginDate.get(Calendar.MONTH)) + 1;\n\n                if (nPartition <= 0) {\n                    throw new java.lang.IllegalArgumentException(\"Incorrect time range for month partitioning!\");\n                }\n            } else {\n                nPartition = -1;\n            }\n        } catch (ParseException e) {\n            throw new java.lang.IllegalArgumentException(e);\n        }\n    }\n\n    /**\n     * For circulatory partition, calculated value of target partition needs to be\n     * rotated to fit the partition range\n     */\n    private int reCalculatePartition(int targetPartition) {\n        /**\n         * If target date is previous of start time of partition setting, shift\n         * the delta range between target and start date to be positive value\n         */\n        if (targetPartition < 0) {\n            targetPartition = nPartition - (-targetPartition) % nPartition;\n        }\n\n        if (targetPartition >= nPartition) {\n            targetPartition =  targetPartition % nPartition;\n        }\n        LOGGER.debug(\"partition is:\" + targetPartition);\n        return targetPartition;\n    }\n\n    @Override\n    public Integer calculate(String columnValue)  {\n        try {\n            int targetPartition;\n            Calendar curTime = Calendar.getInstance();\n            curTime.setTime(formatter.get().parse(columnValue));\n            targetPartition = ((curTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR))\n                    * 12 + curTime.get(Calendar.MONTH)\n                    - beginDate.get(Calendar.MONTH));\n\n            /**\n             * For circulatory partition, calculated value of target partition needs to be\n             * rotated to fit the partition range\n             */\n            if (nPartition > 0) {\n                targetPartition = reCalculatePartition(targetPartition);\n            }\n            return targetPartition;\n\n        } catch (ParseException e) {\n            throw new IllegalArgumentException(new StringBuilder().append(\"columnValue:\").append(columnValue).append(\" Please check if the format satisfied.\").toString(),e);\n        }\n    }\n\n    @Override\n    public Integer[] calculateRange(String beginValue, String endValue) {\n        try {\n            int startPartition, endPartition;\n            Calendar partitionTime = Calendar.getInstance();\n            SimpleDateFormat format = new SimpleDateFormat(dateFormat);\n            partitionTime.setTime(format.parse(beginValue));\n            startPartition = ((partitionTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR))\n                    * 12 + partitionTime.get(Calendar.MONTH)\n                    - beginDate.get(Calendar.MONTH));\n            partitionTime.setTime(format.parse(endValue));\n            endPartition = ((partitionTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR))\n                    * 12 + partitionTime.get(Calendar.MONTH)\n                    - beginDate.get(Calendar.MONTH));\n\n            List<Integer> list = new ArrayList<>();\n\n            while (startPartition <= endPartition) {\n                Integer nodeValue = reCalculatePartition(startPartition);\n                if (Collections.frequency(list, nodeValue) < 1)\n                    list.add(nodeValue);\n                startPartition++;\n            }\n            int size = list.size();\n            return (list.toArray(new Integer[size]));\n        } catch (ParseException e) {\n            LOGGER.error(e);\n            return new Integer[0];\n        }\n    }\n\n    public void setsBeginDate(String sBeginDate) {\n        this.sBeginDate = sBeginDate;\n    }\n\n    public void setDateFormat(String dateFormat) {\n        this.dateFormat = dateFormat;\n    }\n\n    public void setsEndDate(String sEndDate) {\n        this.sEndDate = sEndDate;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByMurmurHash.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\nimport io.mycat.util.StringUtil;\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.SortedMap;\nimport java.util.TreeMap;\n\nimport com.google.common.hash.HashFunction;\nimport com.google.common.hash.Hashing;\n\nimport io.mycat.config.model.rule.RuleAlgorithm;\nimport io.mycat.util.exception.MurmurHashException;\n\n/**\n * consistancy hash, murmur hash\n * implemented by Guava\n * @author wuzhih\n *\n */\npublic class PartitionByMurmurHash extends AbstractPartitionAlgorithm implements RuleAlgorithm  {\n\tprivate static final int DEFAULT_VIRTUAL_BUCKET_TIMES=160;\n\tprivate static final int DEFAULT_WEIGHT=1;\n\tprivate static final Charset DEFAULT_CHARSET=Charset.forName(\"UTF-8\");\n\t\n\tprivate int seed;\n\tprivate int count;\n\tprivate int virtualBucketTimes=DEFAULT_VIRTUAL_BUCKET_TIMES;\n\tprivate Map<Integer,Integer> weightMap=new HashMap<>();\n//\tprivate String bucketMapPath;\n\t\n\tprivate HashFunction hash;\n\t\n\tprivate SortedMap<Integer,Integer> bucketMap;\n\t@Override\n\tpublic void init()  {\n\t\ttry{\n\t\t\tbucketMap=new TreeMap<>();\n//\t\t\tboolean serializableBucketMap=bucketMapPath!=null && bucketMapPath.length()>0;\n//\t\t\tif(serializableBucketMap){\n//\t\t\t\tFile bucketMapFile=new File(bucketMapPath);\n//\t\t\t\tif(bucketMapFile.exists() && bucketMapFile.length()>0){\n//\t\t\t\t\tloadBucketMapFile();\n//\t\t\t\t\treturn;\n//\t\t\t\t}\n//\t\t\t}\n\t\t\tgenerateBucketMap();\n//\t\t\tif(serializableBucketMap){\n//\t\t\t\tstoreBucketMap();\n//\t\t\t}\n\t\t}catch(Exception e){\n\t\t\tthrow new MurmurHashException(e);\n\t\t}\n\t}\n\n\tprivate void generateBucketMap(){\n\t\thash=Hashing.murmur3_32(seed);//计算一致性哈希的对象\n\t\tfor(int i=0;i<count;i++){//构造一致性哈希环，用TreeMap表示\n\t\t\tStringBuilder hashName=new StringBuilder(\"SHARD-\").append(i);\n\t\t\tfor(int n=0,shard=virtualBucketTimes*getWeight(i);n<shard;n++){\n\t\t\t\tbucketMap.put(hash.hashUnencodedChars(hashName.append(\"-NODE-\").append(n)).asInt(),i);\n\t\t\t}\n\t\t}\n\t\tweightMap=null;\n\t}\n//\tprivate void storeBucketMap() throws IOException{\n//\t\ttry(OutputStream store=new FileOutputStream(bucketMapPath)){\n//\t\t\tProperties props=new Properties();\n//\t\t\tfor(Map.Entry entry:bucketMap.entrySet()){\n//\t\t\t\tprops.setProperty(entry.getKey().toString(), entry.getValue().toString());\n//\t\t\t}\n//\t\t\tprops.store(store,null);\n//\t\t}\n//\t}\n//\tprivate void loadBucketMapFile() throws FileNotFoundException, IOException{\n//\t\ttry(InputStream in=new FileInputStream(bucketMapPath)){\n//\t\t\tProperties props=new Properties();\n//\t\t\tprops.load(in);\n//\t\t\tfor(Map.Entry entry:props.entrySet()){\n//\t\t\t\tbucketMap.put(Integer.parseInt(entry.getKey().toString()), Integer.parseInt(entry.getValue().toString()));\n//\t\t\t}\n//\t\t}\n//\t}\n\t/**\n\t * 得到桶的权重，桶就是实际存储数据的DB实例\n\t * 从0开始的桶编号为key，权重为值，权重默认为1。\n\t * 键值必须都是整数\n\t * @param bucket\n\t * @return\n\t */\n\tprivate int getWeight(int bucket){\n\t\tInteger w=weightMap.get(bucket);\n\t\tif(w==null){\n\t\t\tw=DEFAULT_WEIGHT;\n\t\t}\n\t\treturn w;\n\t}\n\t/**\n\t * 创建murmur_hash对象的种子，默认0\n\t * @param seed\n\t */\n\tpublic void setSeed(int seed){\n\t\tthis.seed=seed;\n\t}\n\t/**\n\t * 节点的数量\n\t * @param count\n\t */\n\tpublic void setCount(int count) {\n\t\tthis.count = count;\n\t}\n\t/**\n\t * 虚拟节点倍数，virtualBucketTimes*count就是虚拟结点数量\n\t * @param virtualBucketTimes\n\t */\n\tpublic void setVirtualBucketTimes(int virtualBucketTimes){\n\t\tthis.virtualBucketTimes=virtualBucketTimes;\n\t}\n\t/**\n\t * 节点的权重，没有指定权重的节点默认是1。以properties文件的格式填写，以从0开始到count-1的整数值也就是节点索引为key，以节点权重值为值。\n\t * 所有权重值必须是正整数，否则以1代替\n\t * @param weightMapPath\n\t * @throws IOException\n\t * @throws\n\t */\n\tpublic void setWeightMapFile(String weightMapPath) throws IOException{\n\t\tProperties props=new Properties();\n\t\ttry(BufferedReader reader=new BufferedReader(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream(weightMapPath), DEFAULT_CHARSET))){\n\t\t\tprops.load(reader);\n\t\t\tfor(Map.Entry entry:props.entrySet()){\n\t\t\t\tint weight=Integer.parseInt(entry.getValue().toString());\n\t\t\t\tweightMap.put(Integer.parseInt(entry.getKey().toString()), weight>0?weight:1);\n\t\t\t}\n\t\t}\n\t}\n//\t/**\n//\t * 保存一致性hash的虚拟节点文件路径。\n//\t * 如果这个文件不存在或是空文件就按照指定的count, weightMapFile等构造新的MurmurHash数据结构并保存到这个路径的文件里。\n//\t * 如果这个文件已存在且不是空文件就加载这个文件里的内容作为MurmurHash数据结构，此时其它参数都忽略。\n//\t * 除第一次以外在之后增加节点时可以直接修改这个文件，不过不推荐这么做。如果节点数量变化了，推荐删除这个文件。\n//\t * 可以不指定这个路径，不指定路径时不会保存murmur hash\n//\t * @param bucketMapPath\n//\t */\n//\tpublic void setBucketMapPath(String bucketMapPath){\n//\t\tthis.bucketMapPath=bucketMapPath;\n//\t}\n\t@Override\n\tpublic Integer calculate(String columnValue) {\n\t\tSortedMap<Integer, Integer> tail = bucketMap.tailMap(hash.hashUnencodedChars(columnValue).asInt());\n\t\tif (tail.isEmpty()) {\n\t\t    return bucketMap.get(bucketMap.firstKey());\n\t\t}\n\t\treturn tail.get(tail.firstKey());\n\t}\n\n\t@Override\n\tpublic int getPartitionNum() {\n\t\tint nPartition = this.count;\n\t\treturn nPartition;\n\t}\n\n\tprivate static void hashTest() throws IOException{\n\t\tPartitionByMurmurHash hash=new PartitionByMurmurHash();\n\t\thash.count=10;//分片数\n\t\thash.init();\n\n\t\tint[] bucket=new int[hash.count];\n\n\t\tMap<Integer,List<Integer>> hashed=new HashMap<>();\n\n\t\tint total=1000_0000;//数据量\n\t\tint c=0;\n\t\tfor(int i=100_0000;i<total+100_0000;i++){//假设分片键从100万开始\n\t\t\tc++;\n\t\t\tint h=hash.calculate(StringUtil.removeBackquote(Integer.toString(i)));\n\t\t\tbucket[h]++;\n\t\t\tList<Integer> list=hashed.get(h);\n\t\t\tif(list==null){\n\t\t\t\tlist=new ArrayList<>();\n\t\t\t\thashed.put(h, list);\n\t\t\t}\n\t\t\tlist.add(i);\n\t\t}\n\t\tSystem.out.println(c+\"   \"+total);\n\t\tdouble d=0;\n\t\tc=0;\n\t\tint idx=0;\n\t\tSystem.out.println(\"index    bucket   ratio\");\n\t\tfor(int i:bucket){\n\t\t\td+=i/(double)total;\n\t\t\tc+=i;\n\t\t\tSystem.out.println(idx+++\"  \"+i+\"   \"+(i/(double)total));\n\t\t}\n\t\tSystem.out.println(d+\"  \"+c);\n\n\t\tProperties props=new Properties();\n\t\tfor(Map.Entry entry:hash.bucketMap.entrySet()){\n\t\t\tprops.setProperty(entry.getKey().toString(), entry.getValue().toString());\n\t\t}\n\t\tByteArrayOutputStream out=new ByteArrayOutputStream();\n\t\tprops.store(out, null);\n\n\t\tprops.clear();\n\t\tprops.load(new ByteArrayInputStream(out.toByteArray()));\n\t\tSystem.out.println(props);\n\t\tSystem.out.println(\"****************************************************\");\n//\t\trehashTest(hashed.get(0));\n\t}\n\tprivate static void rehashTest(List<Integer> partition){\n\t\tPartitionByMurmurHash hash=new PartitionByMurmurHash();\n\t\thash.count=12;//分片数\n\t\thash.init();\n\t\t\n\t\tint[] bucket=new int[hash.count];\n\t\t\n\t\tint total=partition.size();//数据量\n\t\tint c=0;\n\t\tfor(int i:partition){//假设分片键从100万开始\n\t\t\tc++;\n\t\t\tint h=hash.calculate(StringUtil.removeBackquote(Integer.toString(i)));\n\t\t\tbucket[h]++;\n\t\t}\n\t\tSystem.out.println(c+\"   \"+total);\n\t\tc=0;\n\t\tint idx=0;\n\t\tSystem.out.println(\"index    bucket   ratio\");\n\t\tfor(int i:bucket){\n\t\t\tc+=i;\n\t\t\tSystem.out.println(idx+++\"  \"+i+\"   \"+(i/(double)total));\n\t\t}\n\t}\n\tpublic static void main(String[] args) throws IOException {\n\t\thashTest();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByPattern.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.route.function;\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.InputStream;\r\nimport java.io.InputStreamReader;\r\nimport java.util.HashSet;\r\nimport java.util.LinkedList;\r\nimport java.util.Set;\r\nimport java.util.regex.Pattern;\r\n\r\nimport io.mycat.config.model.rule.RuleAlgorithm;\r\nimport io.mycat.route.function.PartitionByPrefixPattern.LongRange;\r\n\r\n/**\r\n * auto partition by Long\r\n * \r\n * @author hexiaobin\r\n */\r\npublic class PartitionByPattern extends AbstractPartitionAlgorithm implements RuleAlgorithm {\r\n\tprivate static final int PARTITION_LENGTH = 1024;\r\n\tprivate int patternValue = PARTITION_LENGTH;// 分区长度，取模数值\r\n\tprivate String mapFile;\r\n\tprivate LongRange[] longRongs;\r\n\tprivate int defaultNode = 0;// 包含非数值字符，默认存储节点\r\n    private static final  Pattern pattern = Pattern.compile(\"[0-9]*\");;\r\n\r\n\t@Override\r\n\tpublic void init() {\r\n\r\n\t\tinitialize();\r\n\t}\r\n\r\n\tpublic void setMapFile(String mapFile) {\r\n\t\tthis.mapFile = mapFile;\r\n\t}\r\n\r\n\tpublic void setPatternValue(int patternValue) {\r\n\t\tthis.patternValue = patternValue;\r\n\t}\r\n\r\n\tpublic void setDefaultNode(int defaultNode) {\r\n\t\tthis.defaultNode = defaultNode;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Integer calculate(String columnValue) {\r\n\t\tif (!isNumeric(columnValue)) {\r\n\t\t\treturn defaultNode;\r\n\t\t}\r\n\t\tlong value = Long.parseLong(columnValue);\r\n\t\tInteger rst = null;\r\n\t\tfor (LongRange longRang : this.longRongs) {\r\n\t\t\tlong hash = value % patternValue;\r\n\t\t\tif (hash <= longRang.valueEnd && hash >= longRang.valueStart) {\r\n\t\t\t\treturn longRang.nodeIndx;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn rst;\r\n\t}\r\n\t\r\n\t@Override\r\n\tpublic int getPartitionNum() {\r\n//\t\tint nPartition = this.longRongs.length;\r\n\t\t/*\r\n\t\t * fix #1284 这里的统计应该统计Range的nodeIndex的distinct总数\r\n\t\t */\r\n\t\tSet<Integer> distNodeIdxSet = new HashSet<Integer>();\r\n\t\tfor(LongRange range : longRongs) {\r\n\t\t\tdistNodeIdxSet.add(range.nodeIndx);\r\n\t\t}\r\n\t\tint nPartition = distNodeIdxSet.size();\r\n\t\treturn nPartition;\r\n\t}\r\n\r\n\tpublic static boolean isNumeric(String str) {\r\n\t\treturn pattern.matcher(str).matches();\r\n\t}\r\n\r\n\tprivate void initialize() {\r\n\t\tBufferedReader in = null;\r\n\t\ttry {\r\n\t\t\t// FileInputStream fin = new FileInputStream(new File(fileMapPath));\r\n\t\t\tInputStream fin = this.getClass().getClassLoader()\r\n\t\t\t\t\t.getResourceAsStream(mapFile);\r\n\t\t\tif (fin == null) {\r\n\t\t\t\tthrow new RuntimeException(\"can't find class resource file \"\r\n\t\t\t\t\t\t+ mapFile);\r\n\t\t\t}\r\n\t\t\tin = new BufferedReader(new InputStreamReader(fin));\r\n\t\t\tLinkedList<LongRange> longRangeList = new LinkedList<LongRange>();\r\n\r\n\t\t\tfor (String line = null; (line = in.readLine()) != null;) {\r\n\t\t\t\tline = line.trim();\r\n\t\t\t\tif (line.startsWith(\"#\") || line.startsWith(\"//\")) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\tint ind = line.indexOf('=');\r\n\t\t\t\tif (ind < 0) {\r\n\t\t\t\t\tSystem.out.println(\" warn: bad line int \" + mapFile + \" :\"\r\n\t\t\t\t\t\t\t+ line);\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\t\tString pairs[] = line.substring(0, ind).trim().split(\"-\");\r\n\t\t\t\t\tlong longStart = Long.parseLong(pairs[0].trim());\r\n\t\t\t\t\tlong longEnd = Long.parseLong(pairs[1].trim());\r\n\t\t\t\t\tint nodeId = Integer.parseInt(line.substring(ind + 1)\r\n\t\t\t\t\t\t\t.trim());\r\n\t\t\t\t\tlongRangeList\r\n\t\t\t\t\t\t\t.add(new LongRange(nodeId, longStart, longEnd));\r\n\r\n\t\t\t}\r\n\t\t\tlongRongs = longRangeList.toArray(new LongRange[longRangeList\r\n\t\t\t\t\t.size()]);\r\n\t\t} catch (Exception e) {\r\n\t\t\tif (e instanceof RuntimeException) {\r\n\t\t\t\tthrow (RuntimeException) e;\r\n\t\t\t} else {\r\n\t\t\t\tthrow new RuntimeException(e);\r\n\t\t\t}\r\n\r\n\t\t} finally {\r\n\t\t\ttry {\r\n\t\t\t\tin.close();\r\n\t\t\t} catch (Exception e2) {\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tstatic class LongRange {\r\n\t\tpublic final int nodeIndx;\r\n\t\tpublic final long valueStart;\r\n\t\tpublic final long valueEnd;\r\n\r\n\t\tpublic LongRange(int nodeIndx, long valueStart, long valueEnd) {\r\n\t\t\tsuper();\r\n\t\t\tthis.nodeIndx = nodeIndx;\r\n\t\t\tthis.valueStart = valueStart;\r\n\t\t\tthis.valueEnd = valueEnd;\r\n\t\t}\r\n\r\n\t}\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByPrefixPattern.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.route.function;\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.InputStream;\r\nimport java.io.InputStreamReader;\r\nimport java.util.HashSet;\r\nimport java.util.LinkedList;\r\nimport java.util.Set;\r\n\r\nimport io.mycat.config.model.rule.RuleAlgorithm;\r\nimport io.mycat.route.function.AutoPartitionByLong.LongRange;\r\n\r\n/**\r\n * partition by Prefix length ,can be used in String partition\r\n * \r\n * @author hexiaobin\r\n */\r\npublic class PartitionByPrefixPattern extends AbstractPartitionAlgorithm implements RuleAlgorithm {\r\n\tprivate static final int PARTITION_LENGTH = 1024;\r\n\tprivate int patternValue = PARTITION_LENGTH;// 分区长度，取模数值(默认为1024)\r\n\tprivate int prefixLength;// 字符前几位进行ASCII码取和\r\n\tprivate String mapFile;\r\n\tprivate LongRange[] longRongs;\r\n\r\n\t@Override\r\n\tpublic void init() {\r\n\r\n\t\tinitialize();\r\n\t}\r\n\r\n\tpublic void setMapFile(String mapFile) {\r\n\t\tthis.mapFile = mapFile;\r\n\t}\r\n\r\n\tpublic void setPatternValue(int patternValue) {\r\n\t\tthis.patternValue = patternValue;\r\n\t}\r\n\r\n\tpublic void setPrefixLength(int prefixLength) {\r\n\t\tthis.prefixLength = prefixLength;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Integer calculate(String columnValue)  {\r\n\t\ttry {\r\n\t\t\tint Length = Integer.valueOf(prefixLength);\r\n\r\n\t\t\tLength = columnValue.length() < Length ? columnValue.length() : Length;\r\n\t\t\tint sum = 0;\r\n\t\t\tfor (int i = 0; i < Length; i++) {\r\n\t\t\t\tsum = sum + columnValue.charAt(i);\r\n\t\t\t}\r\n\t\t\tInteger rst = null;\r\n\t\t\tfor (LongRange longRang : this.longRongs) {\r\n\t\t\t\tlong hash = sum % patternValue;\r\n\t\t\t\tif (hash <= longRang.valueEnd && hash >= longRang.valueStart) {\r\n\t\t\t\t\treturn longRang.nodeIndx;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn rst;\r\n\t\t} catch (NumberFormatException e){\r\n\t\t\tthrow new IllegalArgumentException(new StringBuilder().append(\"columnValue:\").append(columnValue).append(\" Please eliminate any quote and non number within it.\").toString(),e);\r\n\t\t}\r\n\t}\r\n\t\r\n\t@Override\r\n\tpublic int getPartitionNum() {\r\n//\t\tint nPartition = this.longRongs.length;\r\n\t\t/*\r\n\t\t * fix #1284 这里的统计应该统计Range的nodeIndex的distinct总数\r\n\t\t */\r\n\t\tSet<Integer> distNodeIdxSet = new HashSet<Integer>();\r\n\t\tfor(LongRange range : longRongs) {\r\n\t\t\tdistNodeIdxSet.add(range.nodeIndx);\r\n\t\t}\r\n\t\tint nPartition = distNodeIdxSet.size();\r\n\t\treturn nPartition;\r\n\t}\r\n\r\n\tprivate void initialize() {\r\n\t\tBufferedReader in = null;\r\n\t\ttry {\r\n\t\t\t// FileInputStream fin = new FileInputStream(new File(fileMapPath));\r\n\t\t\tInputStream fin = this.getClass().getClassLoader()\r\n\t\t\t\t\t.getResourceAsStream(mapFile);\r\n\t\t\tif (fin == null) {\r\n\t\t\t\tthrow new RuntimeException(\"can't find class resource file \"\r\n\t\t\t\t\t\t+ mapFile);\r\n\t\t\t}\r\n\t\t\tin = new BufferedReader(new InputStreamReader(fin));\r\n\t\t\tLinkedList<LongRange> longRangeList = new LinkedList<LongRange>();\r\n\r\n\t\t\tfor (String line = null; (line = in.readLine()) != null;) {\r\n\t\t\t\tline = line.trim();\r\n\t\t\t\tif (line.startsWith(\"#\") || line.startsWith(\"//\")) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\tint ind = line.indexOf('=');\r\n\t\t\t\tif (ind < 0) {\r\n\t\t\t\t\tSystem.out.println(\" warn: bad line int \" + mapFile + \" :\"\r\n\t\t\t\t\t\t\t+ line);\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\t\tString pairs[] = line.substring(0, ind).trim().split(\"-\");\r\n\t\t\t\t\tlong longStart = NumberParseUtil.parseLong(pairs[0].trim());\r\n\t\t\t\t\tlong longEnd = NumberParseUtil.parseLong(pairs[1].trim());\r\n\t\t\t\t\tint nodeId = Integer.parseInt(line.substring(ind + 1)\r\n\t\t\t\t\t\t\t.trim());\r\n\t\t\t\t\tlongRangeList\r\n\t\t\t\t\t\t\t.add(new LongRange(nodeId, longStart, longEnd));\r\n\r\n\t\t\t}\r\n\t\t\tlongRongs = longRangeList.toArray(new LongRange[longRangeList\r\n\t\t\t\t\t.size()]);\r\n\t\t} catch (Exception e) {\r\n\t\t\tif (e instanceof RuntimeException) {\r\n\t\t\t\tthrow (RuntimeException) e;\r\n\t\t\t} else {\r\n\t\t\t\tthrow new RuntimeException(e);\r\n\t\t\t}\r\n\r\n\t\t} finally {\r\n\t\t\ttry {\r\n\t\t\t\tin.close();\r\n\t\t\t} catch (Exception e2) {\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tstatic class LongRange {\r\n\t\tpublic final int nodeIndx;\r\n\t\tpublic final long valueStart;\r\n\t\tpublic final long valueEnd;\r\n\r\n\t\tpublic LongRange(int nodeIndx, long valueStart, long valueEnd) {\r\n\t\t\tsuper();\r\n\t\t\tthis.nodeIndx = nodeIndx;\r\n\t\t\tthis.valueStart = valueStart;\r\n\t\t\tthis.valueEnd = valueEnd;\r\n\t\t}\r\n\r\n\t}\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByRangeDateHash.java",
    "content": "package io.mycat.route.function;\n\nimport com.google.common.hash.Hashing;\n\nimport io.mycat.config.model.rule.RuleAlgorithm;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\n\n/**\n * 先根据日期分组，再根据时间hash使得短期内数据分布的更均匀\n * 优点可以避免扩容时的数据迁移，又可以一定程度上避免范围分片的热点问题\n * 要求日期格式尽量精确些，不然达不到局部均匀的目的\n *\n *\n */\npublic class PartitionByRangeDateHash extends AbstractPartitionAlgorithm implements RuleAlgorithm\n{\n    private static final Logger LOGGER = LoggerFactory\n            .getLogger(PartitionByRangeDateHash.class);\n\n    private String sBeginDate;\n    private String sPartionDay;\n    private String dateFormat;\n\n    private long beginDate;\n    private long partionTime;\n\n    private static final long oneDay = 86400000;\n\n    private String groupPartionSize;\n    private int intGroupPartionSize;\n\n    private ThreadLocal<SimpleDateFormat> formatter;\n\n    @Override\n    public void init()\n    {\n        try\n        {\n            beginDate = new SimpleDateFormat(dateFormat).parse(sBeginDate)\n                    .getTime();\n            intGroupPartionSize = Integer.parseInt(groupPartionSize);\n            formatter = new ThreadLocal<SimpleDateFormat>() {\n                @Override\n                protected SimpleDateFormat initialValue() {\n                    return new SimpleDateFormat(dateFormat);\n                }\n            };\n            if (intGroupPartionSize <= 0)\n            {\n                throw new RuntimeException(\"groupPartionSize must >0,but cur is \" + intGroupPartionSize);\n            }\n        } catch (ParseException e)\n        {\n            throw new IllegalArgumentException(e);\n        }\n        partionTime = Integer.parseInt(sPartionDay) * oneDay;\n    }\n\n    @Override\n    public Integer calculate(String columnValue)  {\n        try\n        {\n            long targetTime = formatter.get().parse(\n                    columnValue).getTime();\n            int targetPartition = (int) ((targetTime - beginDate) / partionTime);\n            int innerIndex =  Hashing.consistentHash(targetTime,intGroupPartionSize);\n            return targetPartition * intGroupPartionSize + innerIndex;\n\n        } catch (ParseException e)\n        {\n            throw new IllegalArgumentException(new StringBuilder().append(\"columnValue:\").append(columnValue).append(\" Please check if the format satisfied.\").toString(),e);\n        }\n    }\n\n    public Integer calculateStart(String columnValue)\n    {\n        try\n        {\n            long targetTime = new SimpleDateFormat(dateFormat).parse(\n                    columnValue).getTime();\n            int targetPartition = (int) ((targetTime - beginDate) / partionTime);\n            return targetPartition * intGroupPartionSize;\n\n        } catch (ParseException e)\n        {\n            throw new IllegalArgumentException(e);\n\n        }\n    }\n\n    public Integer calculateEnd(String columnValue)\n    {\n        try\n        {\n            long targetTime = new SimpleDateFormat(dateFormat).parse(\n                    columnValue).getTime();\n            int targetPartition = (int) ((targetTime - beginDate) / partionTime);\n            return (targetPartition+1) * intGroupPartionSize  - 1;\n\n        } catch (ParseException e)\n        {\n            throw new IllegalArgumentException(e);\n\n        }\n    }\n\n    @Override\n    public Integer[] calculateRange(String beginValue, String endValue)\n    {\n        Integer begin = 0, end = 0;\n        begin = calculateStart(beginValue);\n        end = calculateEnd(endValue);\n\n        if (begin == null || end == null)\n        {\n            return new Integer[0];\n        }\n\n        if (end >= begin)\n        {\n            int len = end - begin + 1;\n            Integer[] re = new Integer[len];\n\n            for (int i = 0; i < len; i++)\n            {\n                re[i] = begin + i;\n            }\n\n            return re;\n        } else\n        {\n            return null;\n        }\n    }\n\n    public long getBeginDate()\n    {\n        return beginDate;\n    }\n\n    public void setsBeginDate(String sBeginDate)\n    {\n        this.sBeginDate = sBeginDate;\n    }\n\n    public void setsPartionDay(String sPartionDay)\n    {\n        this.sPartionDay = sPartionDay;\n    }\n\n    public void setDateFormat(String dateFormat)\n    {\n        this.dateFormat = dateFormat;\n    }\n\n    public String getGroupPartionSize()\n    {\n        return groupPartionSize;\n    }\n\n    public void setGroupPartionSize(String groupPartionSize)\n    {\n        this.groupPartionSize = groupPartionSize;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByRangeMod.java",
    "content": "/*\n * Copyright (c) 2015, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://github.com/MyCATApache/Mycat-Server.\n *\n */\npackage io.mycat.route.function;\n\nimport java.io.BufferedReader;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.math.BigInteger;\nimport java.util.LinkedList;\n\nimport io.mycat.config.model.rule.RuleAlgorithm;\n\n/**\n * 先进行范围分片计算出分片组，组内再取模\n * 优点可以避免扩容时的数据迁移，又可以一定程度上避免范围分片的热点问题\n * \n * @author wuzhi\n */\npublic class PartitionByRangeMod extends AbstractPartitionAlgorithm implements RuleAlgorithm{\n\n\tprivate String mapFile;\n\tprivate LongRange[] longRanges;\n\t\n\tprivate int defaultNode = -1;\n\t@Override\n\tpublic void init() {\n\n\t\tinitialize();\n\t}\n\n\tpublic void setMapFile(String mapFile) {\n\t\tthis.mapFile = mapFile;\n\t}\n\n\t@Override\n\tpublic Integer calculate(String columnValue)  {\n//\t\tcolumnValue = NumberParseUtil.eliminateQoute(columnValue);\n\t\ttry {\n\t\t\tlong value = Long.parseLong(columnValue);\n\t\t\tInteger rst = null;\n\t\t\tint nodeIndex = 0;\n\t\t\tfor (LongRange longRang : this.longRanges) {\n\t\t\t\tif (value <= longRang.valueEnd && value >= longRang.valueStart) {\n\t\t\t\t\tBigInteger bigNum = new BigInteger(columnValue).abs();\n\t\t\t\t\tint innerIndex = (bigNum.mod(BigInteger.valueOf(longRang.groupSize))).intValue();\n\t\t\t\t\treturn nodeIndex + innerIndex;\n\t\t\t\t} else {\n\t\t\t\t\tnodeIndex += longRang.groupSize;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//数据超过范围，暂时使用配置的默认节点\n\t\t\tif (rst == null && defaultNode >= 0) {\n\t\t\t\treturn defaultNode;\n\t\t\t}\n\t\t\treturn rst;\n\t\t} catch (NumberFormatException e) {\n\t\t\tthrow new IllegalArgumentException(new StringBuilder().append(\"columnValue:\").append(columnValue).append(\" Please eliminate any quote and non number within it.\").toString(), e);\n\t\t}\n\t}\n    \n\t@Override\n\tpublic int getPartitionNum() {\n\t\tint nPartition = 0;\n\t\tfor(LongRange longRange : this.longRanges) {\n\t\t\tnPartition += longRange.groupSize;\n\t\t}\n\t\treturn nPartition;\n\t}\n\n\tpublic Integer calculateStart(String columnValue) {\n        long value = Long.parseLong(columnValue);\n        Integer rst = null;\n        int nodeIndex=0;\n        for (LongRange longRang : this.longRanges) {\n            if (value <= longRang.valueEnd && value >= longRang.valueStart) {\n\n                return nodeIndex;\n            }    else\n            {\n                nodeIndex+= longRang.groupSize;\n            }\n        }\n        // 数据超过范围，暂时使用配置的默认节点\n        if(rst ==null && defaultNode>=0){\n            return defaultNode ;\n        }\n        return rst;\n    }\n    public Integer calculateEnd(String columnValue) {\n        long value = Long.parseLong(columnValue);\n        Integer rst = null;\n        int nodeIndex=0;\n        for (LongRange longRang : this.longRanges) {\n            if (value <= longRang.valueEnd && value >= longRang.valueStart) {\n\n                return nodeIndex+longRang.groupSize -1;\n            }    else\n            {\n                nodeIndex+= longRang.groupSize;\n            }\n        }\n        // 数据超过范围，暂时使用配置的默认节点\n        if(rst ==null && defaultNode>=0){\n            return defaultNode ;\n        }\n        return rst;\n    }\n\t\n\t@Override\n\tpublic Integer[] calculateRange(String beginValue, String endValue) {\n        Integer begin = 0, end = 0;\n        begin = calculateStart(beginValue);\n        end = calculateEnd(endValue);\n\n        if(begin == null || end == null){\n            return new Integer[0];\n        }\n\n        if (end >= begin) {\n            int len = end-begin+1;\n            Integer [] re = new Integer[len];\n\n            for(int i =0;i<len;i++){\n                re[i]=begin+i;\n            }\n\n            return re;\n        }else{\n            return null;\n        }\n\t}\n\n\n\n\tprivate void initialize() {\n\t\tBufferedReader in = null;\n\t\ttry {\n\t\t\tInputStream fin = this.getClass().getClassLoader()\n\t\t\t\t\t.getResourceAsStream(mapFile);\n\t\t\tif (fin == null) {\n\t\t\t\tthrow new RuntimeException(\"can't find class resource file \"\n\t\t\t\t\t\t+ mapFile);\n\t\t\t}\n\t\t\tin = new BufferedReader(new InputStreamReader(fin));\n\t\t\tLinkedList<LongRange> longRangeList = new LinkedList<LongRange>();\n\n\t\t\tfor (String line = null; (line = in.readLine()) != null;) {\n\t\t\t\tline = line.trim();\n\t\t\t\tif (line.startsWith(\"#\") || line.startsWith(\"//\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tint ind = line.indexOf('=');\n\t\t\t\tif (ind < 0) {\n\t\t\t\t\tSystem.out.println(\" warn: bad line int \" + mapFile + \" :\"\n\t\t\t\t\t\t\t+ line);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\tString pairs[] = line.substring(0, ind).trim().split(\"-\");\n\t\t\t\t\tlong longStart = NumberParseUtil.parseLong(pairs[0].trim());\n\t\t\t\t\tlong longEnd = NumberParseUtil.parseLong(pairs[1].trim());\n\t\t\t\t\tint nodeId = Integer.parseInt(line.substring(ind + 1)\n\t\t\t\t\t\t\t.trim());\n\t\t\t\t\tlongRangeList\n\t\t\t\t\t\t\t.add(new LongRange(nodeId, longStart, longEnd));\n\n\t\t\t}\n\t\t\tlongRanges = longRangeList.toArray(new LongRange[longRangeList.size()]);\n\t\t} catch (Exception e) {\n\t\t\tif (e instanceof RuntimeException) {\n\t\t\t\tthrow (RuntimeException) e;\n\t\t\t} else {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tin.close();\n\t\t\t} catch (Exception e2) {\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic int getDefaultNode() {\n\t\treturn defaultNode;\n\t}\n\n\tpublic void setDefaultNode(int defaultNode) {\n\t\tthis.defaultNode = defaultNode;\n\t}\n\n\tstatic class LongRange {\n\t\tpublic final int groupSize;\n\t\tpublic final long valueStart;\n\t\tpublic final long valueEnd;\n\n\t\tpublic LongRange(int groupSize, long valueStart, long valueEnd) {\n\t\t\tsuper();\n\t\t\tthis.groupSize = groupSize;\n\t\t\tthis.valueStart = valueStart;\n\t\t\tthis.valueEnd = valueEnd;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionByString.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.route.function;\r\n\r\nimport io.mycat.config.model.rule.RuleAlgorithm;\r\nimport io.mycat.route.parser.util.Pair;\r\nimport io.mycat.route.util.PartitionUtil;\r\nimport io.mycat.util.StringUtil;\r\n\r\n/**\r\n * @author <a href=\"mailto:daasadmin@hp.com\">yangwenx</a>\r\n */\r\npublic final class PartitionByString extends AbstractPartitionAlgorithm implements RuleAlgorithm  {\r\n  \r\n    private int hashSliceStart = 0;\r\n    /** 0 means str.length(), -1 means str.length()-1 */\r\n    private int hashSliceEnd = 8;\r\n    protected int[] count;\r\n    protected int[] length;\r\n    protected PartitionUtil partitionUtil;\r\n\r\n    public void setPartitionCount(String partitionCount) {\r\n        this.count = toIntArray(partitionCount);\r\n    }\r\n\r\n    public void setPartitionLength(String partitionLength) {\r\n        this.length = toIntArray(partitionLength);\r\n    }\r\n\r\n\r\n\tpublic void setHashLength(int hashLength) {\r\n        setHashSlice(String.valueOf(hashLength));\r\n    }\r\n\r\n    public void setHashSlice(String hashSlice) {\r\n        Pair<Integer, Integer> p = sequenceSlicing(hashSlice);\r\n        hashSliceStart = p.getKey();\r\n        hashSliceEnd = p.getValue();\r\n    }\r\n\r\n\r\n    /**\r\n     * \"2\" -&gt; (0,2)<br/>\r\n     * \"1:2\" -&gt; (1,2)<br/>\r\n     * \"1:\" -&gt; (1,0)<br/>\r\n     * \"-1:\" -&gt; (-1,0)<br/>\r\n     * \":-1\" -&gt; (0,-1)<br/>\r\n     * \":\" -&gt; (0,0)<br/>\r\n     */\r\n    public static Pair<Integer, Integer> sequenceSlicing(String slice) {\r\n        int ind = slice.indexOf(':');\r\n        if (ind < 0) {\r\n            int i = Integer.parseInt(slice.trim());\r\n            if (i >= 0) {\r\n                return new Pair<Integer, Integer>(0, i);\r\n            } else {\r\n                return new Pair<Integer, Integer>(i, 0);\r\n            }\r\n        }\r\n        String left = slice.substring(0, ind).trim();\r\n        String right = slice.substring(1 + ind).trim();\r\n        int start, end;\r\n        if (left.length() <= 0) {\r\n            start = 0;\r\n        } else {\r\n            start = Integer.parseInt(left);\r\n        }\r\n        if (right.length() <= 0) {\r\n            end = 0;\r\n        } else {\r\n            end = Integer.parseInt(right);\r\n        }\r\n        return new Pair<Integer, Integer>(start, end);\r\n    }\r\n\r\n\t@Override\r\n\tpublic void init() {\r\n\t\tpartitionUtil = new PartitionUtil(count,length);\r\n\t\t\r\n\t}\r\n\tprivate static int[] toIntArray(String string) {\r\n\t\tString[] strs = io.mycat.util.SplitUtil.split(string, ',', true);\r\n\t\tint[] ints = new int[strs.length];\r\n\t\tfor (int i = 0; i < strs.length; ++i) {\r\n\t\t\tints[i] = Integer.parseInt(strs[i]);\r\n\t\t}\r\n\t\treturn ints;\r\n\t}\r\n\t@Override\r\n\tpublic Integer calculate(String key) {\r\n        int start = hashSliceStart >= 0 ? hashSliceStart : key.length() + hashSliceStart;\r\n        int end = hashSliceEnd > 0 ? hashSliceEnd : key.length() + hashSliceEnd;\r\n        long hash = StringUtil.hash(key, start, end);\r\n        return partitionUtil.partition(hash);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int getPartitionNum() {\r\n\t\tint nPartition = 0;\r\n\t\tfor(int i = 0; i < count.length; i++) {\r\n\t\t\tnPartition += count[i];\r\n\t\t}\r\n\t\treturn nPartition;\r\n\t}\r\n\t\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PartitionDirectBySubString.java",
    "content": "package io.mycat.route.function;\r\n\r\nimport io.mycat.config.model.rule.RuleAlgorithm;\r\n\r\n/**\r\n * 直接根据字符子串（必须是数字）计算分区号（由应用传递参数，显式指定分区号）。\r\n * <function name=\"sub\" class=\"org.opencloudb.route.function.PartitionDirectBySubString\">\r\n * <property name=\"startIndex\">9</property> <!-- zero-based -->\r\n * <property name=\"size\">2</property>\r\n * <property name=\"partitionCount\">8</property>\r\n * <property name=\"defaultPartition\">0</property>\r\n * </function>\r\n */\r\npublic class PartitionDirectBySubString extends AbstractPartitionAlgorithm implements RuleAlgorithm {\r\n    // 字符子串起始索引（zero-based)\r\n    private int startIndex;\r\n    // 字串长度\r\n    private int size;\r\n    // 分区数量\r\n    private int partitionCount;\r\n    // 默认分区（在分区数量定义时，字串标示的分区编号不在分区数量内时，使用默认分区）\r\n    private int defaultPartition;\r\n\r\n    public void setStartIndex(String str) {\r\n        startIndex = Integer.parseInt(str);\r\n    }\r\n\r\n    public void setSize(String str) {\r\n        size = Integer.parseInt(str);\r\n    }\r\n\r\n    public void setPartitionCount(String partitionCount) {\r\n        this.partitionCount = Integer.parseInt(partitionCount);\r\n    }\r\n\r\n    public void setDefaultPartition(String defaultPartition) {\r\n        this.defaultPartition = Integer.parseInt(defaultPartition);\r\n    }\r\n\r\n    @Override\r\n    public void init() {\r\n\r\n    }\r\n\r\n    @Override\r\n    public Integer calculate(String columnValue) {\r\n        String partitionSubString = columnValue.substring(startIndex, startIndex + size);\r\n        int partition = Integer.parseInt(partitionSubString, 10);\r\n        return partitionCount > 0 && partition >= partitionCount\r\n                ? defaultPartition : partition;\r\n    }\r\n\r\n\t@Override\r\n\tpublic int getPartitionNum() {\r\n\t\tint nPartition = this.partitionCount;\r\n\t\treturn nPartition;\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/PureJavaCrc32.java",
    "content": "package io.mycat.route.function;\n\n/**\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n        import java.util.HashSet;\n        import java.util.Set;\n        import java.util.zip.Checksum;\n\n/**\n * A pure-java implementation of the CRC32 checksum that uses\n * the same polynomial as the built-in native CRC32.\n *\n * This is to avoid the JNI overhead for certain uses of Checksumming\n * where many small pieces of data are checksummed in succession.\n *\n * The current version is ~10x to 1.8x as fast as Sun's native\n * java.util.zip.CRC32 in Java 1.6\n *\n * @see java.util.zip.CRC32\n *\n * This class is copied from hadoop-commons project.\n * (The initial patch added PureJavaCrc32 was HADOOP-6148)\n */\npublic class PureJavaCrc32 implements Checksum {\n\n    /** the current CRC value, bit-flipped */\n    private int crc;\n\n    /** Create a new PureJavaCrc32 object. */\n    public PureJavaCrc32() {\n        reset();\n    }\n\n    @Override\n    public long getValue() {\n        return (~crc) & 0xffffffffL;\n    }\n\n    @Override\n    public void reset() {\n        crc = 0xffffffff;\n    }\n\n    @Override\n    public void update(byte[] b, int off, int len) {\n        int localCrc = crc;\n\n        while(len > 7) {\n            final int c0 =(b[off+0] ^ localCrc) & 0xff;\n            final int c1 =(b[off+1] ^ (localCrc >>>= 8)) & 0xff;\n            final int c2 =(b[off+2] ^ (localCrc >>>= 8)) & 0xff;\n            final int c3 =(b[off+3] ^ (localCrc >>>= 8)) & 0xff;\n            localCrc = (T[T8_7_start + c0] ^ T[T8_6_start + c1])\n                    ^ (T[T8_5_start + c2] ^ T[T8_4_start + c3]);\n\n            final int c4 = b[off+4] & 0xff;\n            final int c5 = b[off+5] & 0xff;\n            final int c6 = b[off+6] & 0xff;\n            final int c7 = b[off+7] & 0xff;\n\n            localCrc ^= (T[T8_3_start + c4] ^ T[T8_2_start + c5])\n                    ^ (T[T8_1_start + c6] ^ T[T8_0_start + c7]);\n\n            off += 8;\n            len -= 8;\n        }\n\n    /* loop unroll - duff's device style */\n        switch(len) {\n            case 7: localCrc = (localCrc >>> 8) ^ T[T8_0_start + ((localCrc ^ b[off++]) & 0xff)];\n            case 6: localCrc = (localCrc >>> 8) ^ T[T8_0_start + ((localCrc ^ b[off++]) & 0xff)];\n            case 5: localCrc = (localCrc >>> 8) ^ T[T8_0_start + ((localCrc ^ b[off++]) & 0xff)];\n            case 4: localCrc = (localCrc >>> 8) ^ T[T8_0_start + ((localCrc ^ b[off++]) & 0xff)];\n            case 3: localCrc = (localCrc >>> 8) ^ T[T8_0_start + ((localCrc ^ b[off++]) & 0xff)];\n            case 2: localCrc = (localCrc >>> 8) ^ T[T8_0_start + ((localCrc ^ b[off++]) & 0xff)];\n            case 1: localCrc = (localCrc >>> 8) ^ T[T8_0_start + ((localCrc ^ b[off++]) & 0xff)];\n            default:\n        /* nothing */\n        }\n\n        // Publish crc out to object\n        crc = localCrc;\n    }\n\n    @Override\n    final public void update(int b) {\n        crc = (crc >>> 8) ^ T[T8_0_start + ((crc ^ b) & 0xff)];\n    }\n\n    /*\n     * CRC-32 lookup tables generated by the polynomial 0xEDB88320.\n     * See also TestPureJavaCrc32.Table.\n     */\n    private static final int T8_0_start = 0*256;\n    private static final int T8_1_start = 1*256;\n    private static final int T8_2_start = 2*256;\n    private static final int T8_3_start = 3*256;\n    private static final int T8_4_start = 4*256;\n    private static final int T8_5_start = 5*256;\n    private static final int T8_6_start = 6*256;\n    private static final int T8_7_start = 7*256;\n\n    private static final int[] T = new int[] {\n    /* T8_0 */\n            0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,\n            0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,\n            0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,\n            0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,\n            0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,\n            0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,\n            0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,\n            0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,\n            0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,\n            0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,\n            0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,\n            0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,\n            0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,\n            0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,\n            0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,\n            0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,\n            0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,\n            0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,\n            0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,\n            0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,\n            0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,\n            0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,\n            0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,\n            0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,\n            0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,\n            0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,\n            0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,\n            0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,\n            0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,\n            0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,\n            0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,\n            0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,\n            0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,\n            0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,\n            0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,\n            0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,\n            0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,\n            0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,\n            0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,\n            0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,\n            0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,\n            0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,\n            0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,\n            0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,\n            0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,\n            0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,\n            0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,\n            0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,\n            0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,\n            0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,\n            0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,\n            0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,\n            0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,\n            0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,\n            0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,\n            0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,\n            0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,\n            0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,\n            0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,\n            0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,\n            0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,\n            0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,\n            0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,\n            0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,\n    /* T8_1 */\n            0x00000000, 0x191B3141, 0x32366282, 0x2B2D53C3,\n            0x646CC504, 0x7D77F445, 0x565AA786, 0x4F4196C7,\n            0xC8D98A08, 0xD1C2BB49, 0xFAEFE88A, 0xE3F4D9CB,\n            0xACB54F0C, 0xB5AE7E4D, 0x9E832D8E, 0x87981CCF,\n            0x4AC21251, 0x53D92310, 0x78F470D3, 0x61EF4192,\n            0x2EAED755, 0x37B5E614, 0x1C98B5D7, 0x05838496,\n            0x821B9859, 0x9B00A918, 0xB02DFADB, 0xA936CB9A,\n            0xE6775D5D, 0xFF6C6C1C, 0xD4413FDF, 0xCD5A0E9E,\n            0x958424A2, 0x8C9F15E3, 0xA7B24620, 0xBEA97761,\n            0xF1E8E1A6, 0xE8F3D0E7, 0xC3DE8324, 0xDAC5B265,\n            0x5D5DAEAA, 0x44469FEB, 0x6F6BCC28, 0x7670FD69,\n            0x39316BAE, 0x202A5AEF, 0x0B07092C, 0x121C386D,\n            0xDF4636F3, 0xC65D07B2, 0xED705471, 0xF46B6530,\n            0xBB2AF3F7, 0xA231C2B6, 0x891C9175, 0x9007A034,\n            0x179FBCFB, 0x0E848DBA, 0x25A9DE79, 0x3CB2EF38,\n            0x73F379FF, 0x6AE848BE, 0x41C51B7D, 0x58DE2A3C,\n            0xF0794F05, 0xE9627E44, 0xC24F2D87, 0xDB541CC6,\n            0x94158A01, 0x8D0EBB40, 0xA623E883, 0xBF38D9C2,\n            0x38A0C50D, 0x21BBF44C, 0x0A96A78F, 0x138D96CE,\n            0x5CCC0009, 0x45D73148, 0x6EFA628B, 0x77E153CA,\n            0xBABB5D54, 0xA3A06C15, 0x888D3FD6, 0x91960E97,\n            0xDED79850, 0xC7CCA911, 0xECE1FAD2, 0xF5FACB93,\n            0x7262D75C, 0x6B79E61D, 0x4054B5DE, 0x594F849F,\n            0x160E1258, 0x0F152319, 0x243870DA, 0x3D23419B,\n            0x65FD6BA7, 0x7CE65AE6, 0x57CB0925, 0x4ED03864,\n            0x0191AEA3, 0x188A9FE2, 0x33A7CC21, 0x2ABCFD60,\n            0xAD24E1AF, 0xB43FD0EE, 0x9F12832D, 0x8609B26C,\n            0xC94824AB, 0xD05315EA, 0xFB7E4629, 0xE2657768,\n            0x2F3F79F6, 0x362448B7, 0x1D091B74, 0x04122A35,\n            0x4B53BCF2, 0x52488DB3, 0x7965DE70, 0x607EEF31,\n            0xE7E6F3FE, 0xFEFDC2BF, 0xD5D0917C, 0xCCCBA03D,\n            0x838A36FA, 0x9A9107BB, 0xB1BC5478, 0xA8A76539,\n            0x3B83984B, 0x2298A90A, 0x09B5FAC9, 0x10AECB88,\n            0x5FEF5D4F, 0x46F46C0E, 0x6DD93FCD, 0x74C20E8C,\n            0xF35A1243, 0xEA412302, 0xC16C70C1, 0xD8774180,\n            0x9736D747, 0x8E2DE606, 0xA500B5C5, 0xBC1B8484,\n            0x71418A1A, 0x685ABB5B, 0x4377E898, 0x5A6CD9D9,\n            0x152D4F1E, 0x0C367E5F, 0x271B2D9C, 0x3E001CDD,\n            0xB9980012, 0xA0833153, 0x8BAE6290, 0x92B553D1,\n            0xDDF4C516, 0xC4EFF457, 0xEFC2A794, 0xF6D996D5,\n            0xAE07BCE9, 0xB71C8DA8, 0x9C31DE6B, 0x852AEF2A,\n            0xCA6B79ED, 0xD37048AC, 0xF85D1B6F, 0xE1462A2E,\n            0x66DE36E1, 0x7FC507A0, 0x54E85463, 0x4DF36522,\n            0x02B2F3E5, 0x1BA9C2A4, 0x30849167, 0x299FA026,\n            0xE4C5AEB8, 0xFDDE9FF9, 0xD6F3CC3A, 0xCFE8FD7B,\n            0x80A96BBC, 0x99B25AFD, 0xB29F093E, 0xAB84387F,\n            0x2C1C24B0, 0x350715F1, 0x1E2A4632, 0x07317773,\n            0x4870E1B4, 0x516BD0F5, 0x7A468336, 0x635DB277,\n            0xCBFAD74E, 0xD2E1E60F, 0xF9CCB5CC, 0xE0D7848D,\n            0xAF96124A, 0xB68D230B, 0x9DA070C8, 0x84BB4189,\n            0x03235D46, 0x1A386C07, 0x31153FC4, 0x280E0E85,\n            0x674F9842, 0x7E54A903, 0x5579FAC0, 0x4C62CB81,\n            0x8138C51F, 0x9823F45E, 0xB30EA79D, 0xAA1596DC,\n            0xE554001B, 0xFC4F315A, 0xD7626299, 0xCE7953D8,\n            0x49E14F17, 0x50FA7E56, 0x7BD72D95, 0x62CC1CD4,\n            0x2D8D8A13, 0x3496BB52, 0x1FBBE891, 0x06A0D9D0,\n            0x5E7EF3EC, 0x4765C2AD, 0x6C48916E, 0x7553A02F,\n            0x3A1236E8, 0x230907A9, 0x0824546A, 0x113F652B,\n            0x96A779E4, 0x8FBC48A5, 0xA4911B66, 0xBD8A2A27,\n            0xF2CBBCE0, 0xEBD08DA1, 0xC0FDDE62, 0xD9E6EF23,\n            0x14BCE1BD, 0x0DA7D0FC, 0x268A833F, 0x3F91B27E,\n            0x70D024B9, 0x69CB15F8, 0x42E6463B, 0x5BFD777A,\n            0xDC656BB5, 0xC57E5AF4, 0xEE530937, 0xF7483876,\n            0xB809AEB1, 0xA1129FF0, 0x8A3FCC33, 0x9324FD72,\n    /* T8_2 */\n            0x00000000, 0x01C26A37, 0x0384D46E, 0x0246BE59,\n            0x0709A8DC, 0x06CBC2EB, 0x048D7CB2, 0x054F1685,\n            0x0E1351B8, 0x0FD13B8F, 0x0D9785D6, 0x0C55EFE1,\n            0x091AF964, 0x08D89353, 0x0A9E2D0A, 0x0B5C473D,\n            0x1C26A370, 0x1DE4C947, 0x1FA2771E, 0x1E601D29,\n            0x1B2F0BAC, 0x1AED619B, 0x18ABDFC2, 0x1969B5F5,\n            0x1235F2C8, 0x13F798FF, 0x11B126A6, 0x10734C91,\n            0x153C5A14, 0x14FE3023, 0x16B88E7A, 0x177AE44D,\n            0x384D46E0, 0x398F2CD7, 0x3BC9928E, 0x3A0BF8B9,\n            0x3F44EE3C, 0x3E86840B, 0x3CC03A52, 0x3D025065,\n            0x365E1758, 0x379C7D6F, 0x35DAC336, 0x3418A901,\n            0x3157BF84, 0x3095D5B3, 0x32D36BEA, 0x331101DD,\n            0x246BE590, 0x25A98FA7, 0x27EF31FE, 0x262D5BC9,\n            0x23624D4C, 0x22A0277B, 0x20E69922, 0x2124F315,\n            0x2A78B428, 0x2BBADE1F, 0x29FC6046, 0x283E0A71,\n            0x2D711CF4, 0x2CB376C3, 0x2EF5C89A, 0x2F37A2AD,\n            0x709A8DC0, 0x7158E7F7, 0x731E59AE, 0x72DC3399,\n            0x7793251C, 0x76514F2B, 0x7417F172, 0x75D59B45,\n            0x7E89DC78, 0x7F4BB64F, 0x7D0D0816, 0x7CCF6221,\n            0x798074A4, 0x78421E93, 0x7A04A0CA, 0x7BC6CAFD,\n            0x6CBC2EB0, 0x6D7E4487, 0x6F38FADE, 0x6EFA90E9,\n            0x6BB5866C, 0x6A77EC5B, 0x68315202, 0x69F33835,\n            0x62AF7F08, 0x636D153F, 0x612BAB66, 0x60E9C151,\n            0x65A6D7D4, 0x6464BDE3, 0x662203BA, 0x67E0698D,\n            0x48D7CB20, 0x4915A117, 0x4B531F4E, 0x4A917579,\n            0x4FDE63FC, 0x4E1C09CB, 0x4C5AB792, 0x4D98DDA5,\n            0x46C49A98, 0x4706F0AF, 0x45404EF6, 0x448224C1,\n            0x41CD3244, 0x400F5873, 0x4249E62A, 0x438B8C1D,\n            0x54F16850, 0x55330267, 0x5775BC3E, 0x56B7D609,\n            0x53F8C08C, 0x523AAABB, 0x507C14E2, 0x51BE7ED5,\n            0x5AE239E8, 0x5B2053DF, 0x5966ED86, 0x58A487B1,\n            0x5DEB9134, 0x5C29FB03, 0x5E6F455A, 0x5FAD2F6D,\n            0xE1351B80, 0xE0F771B7, 0xE2B1CFEE, 0xE373A5D9,\n            0xE63CB35C, 0xE7FED96B, 0xE5B86732, 0xE47A0D05,\n            0xEF264A38, 0xEEE4200F, 0xECA29E56, 0xED60F461,\n            0xE82FE2E4, 0xE9ED88D3, 0xEBAB368A, 0xEA695CBD,\n            0xFD13B8F0, 0xFCD1D2C7, 0xFE976C9E, 0xFF5506A9,\n            0xFA1A102C, 0xFBD87A1B, 0xF99EC442, 0xF85CAE75,\n            0xF300E948, 0xF2C2837F, 0xF0843D26, 0xF1465711,\n            0xF4094194, 0xF5CB2BA3, 0xF78D95FA, 0xF64FFFCD,\n            0xD9785D60, 0xD8BA3757, 0xDAFC890E, 0xDB3EE339,\n            0xDE71F5BC, 0xDFB39F8B, 0xDDF521D2, 0xDC374BE5,\n            0xD76B0CD8, 0xD6A966EF, 0xD4EFD8B6, 0xD52DB281,\n            0xD062A404, 0xD1A0CE33, 0xD3E6706A, 0xD2241A5D,\n            0xC55EFE10, 0xC49C9427, 0xC6DA2A7E, 0xC7184049,\n            0xC25756CC, 0xC3953CFB, 0xC1D382A2, 0xC011E895,\n            0xCB4DAFA8, 0xCA8FC59F, 0xC8C97BC6, 0xC90B11F1,\n            0xCC440774, 0xCD866D43, 0xCFC0D31A, 0xCE02B92D,\n            0x91AF9640, 0x906DFC77, 0x922B422E, 0x93E92819,\n            0x96A63E9C, 0x976454AB, 0x9522EAF2, 0x94E080C5,\n            0x9FBCC7F8, 0x9E7EADCF, 0x9C381396, 0x9DFA79A1,\n            0x98B56F24, 0x99770513, 0x9B31BB4A, 0x9AF3D17D,\n            0x8D893530, 0x8C4B5F07, 0x8E0DE15E, 0x8FCF8B69,\n            0x8A809DEC, 0x8B42F7DB, 0x89044982, 0x88C623B5,\n            0x839A6488, 0x82580EBF, 0x801EB0E6, 0x81DCDAD1,\n            0x8493CC54, 0x8551A663, 0x8717183A, 0x86D5720D,\n            0xA9E2D0A0, 0xA820BA97, 0xAA6604CE, 0xABA46EF9,\n            0xAEEB787C, 0xAF29124B, 0xAD6FAC12, 0xACADC625,\n            0xA7F18118, 0xA633EB2F, 0xA4755576, 0xA5B73F41,\n            0xA0F829C4, 0xA13A43F3, 0xA37CFDAA, 0xA2BE979D,\n            0xB5C473D0, 0xB40619E7, 0xB640A7BE, 0xB782CD89,\n            0xB2CDDB0C, 0xB30FB13B, 0xB1490F62, 0xB08B6555,\n            0xBBD72268, 0xBA15485F, 0xB853F606, 0xB9919C31,\n            0xBCDE8AB4, 0xBD1CE083, 0xBF5A5EDA, 0xBE9834ED,\n    /* T8_3 */\n            0x00000000, 0xB8BC6765, 0xAA09C88B, 0x12B5AFEE,\n            0x8F629757, 0x37DEF032, 0x256B5FDC, 0x9DD738B9,\n            0xC5B428EF, 0x7D084F8A, 0x6FBDE064, 0xD7018701,\n            0x4AD6BFB8, 0xF26AD8DD, 0xE0DF7733, 0x58631056,\n            0x5019579F, 0xE8A530FA, 0xFA109F14, 0x42ACF871,\n            0xDF7BC0C8, 0x67C7A7AD, 0x75720843, 0xCDCE6F26,\n            0x95AD7F70, 0x2D111815, 0x3FA4B7FB, 0x8718D09E,\n            0x1ACFE827, 0xA2738F42, 0xB0C620AC, 0x087A47C9,\n            0xA032AF3E, 0x188EC85B, 0x0A3B67B5, 0xB28700D0,\n            0x2F503869, 0x97EC5F0C, 0x8559F0E2, 0x3DE59787,\n            0x658687D1, 0xDD3AE0B4, 0xCF8F4F5A, 0x7733283F,\n            0xEAE41086, 0x525877E3, 0x40EDD80D, 0xF851BF68,\n            0xF02BF8A1, 0x48979FC4, 0x5A22302A, 0xE29E574F,\n            0x7F496FF6, 0xC7F50893, 0xD540A77D, 0x6DFCC018,\n            0x359FD04E, 0x8D23B72B, 0x9F9618C5, 0x272A7FA0,\n            0xBAFD4719, 0x0241207C, 0x10F48F92, 0xA848E8F7,\n            0x9B14583D, 0x23A83F58, 0x311D90B6, 0x89A1F7D3,\n            0x1476CF6A, 0xACCAA80F, 0xBE7F07E1, 0x06C36084,\n            0x5EA070D2, 0xE61C17B7, 0xF4A9B859, 0x4C15DF3C,\n            0xD1C2E785, 0x697E80E0, 0x7BCB2F0E, 0xC377486B,\n            0xCB0D0FA2, 0x73B168C7, 0x6104C729, 0xD9B8A04C,\n            0x446F98F5, 0xFCD3FF90, 0xEE66507E, 0x56DA371B,\n            0x0EB9274D, 0xB6054028, 0xA4B0EFC6, 0x1C0C88A3,\n            0x81DBB01A, 0x3967D77F, 0x2BD27891, 0x936E1FF4,\n            0x3B26F703, 0x839A9066, 0x912F3F88, 0x299358ED,\n            0xB4446054, 0x0CF80731, 0x1E4DA8DF, 0xA6F1CFBA,\n            0xFE92DFEC, 0x462EB889, 0x549B1767, 0xEC277002,\n            0x71F048BB, 0xC94C2FDE, 0xDBF98030, 0x6345E755,\n            0x6B3FA09C, 0xD383C7F9, 0xC1366817, 0x798A0F72,\n            0xE45D37CB, 0x5CE150AE, 0x4E54FF40, 0xF6E89825,\n            0xAE8B8873, 0x1637EF16, 0x048240F8, 0xBC3E279D,\n            0x21E91F24, 0x99557841, 0x8BE0D7AF, 0x335CB0CA,\n            0xED59B63B, 0x55E5D15E, 0x47507EB0, 0xFFEC19D5,\n            0x623B216C, 0xDA874609, 0xC832E9E7, 0x708E8E82,\n            0x28ED9ED4, 0x9051F9B1, 0x82E4565F, 0x3A58313A,\n            0xA78F0983, 0x1F336EE6, 0x0D86C108, 0xB53AA66D,\n            0xBD40E1A4, 0x05FC86C1, 0x1749292F, 0xAFF54E4A,\n            0x322276F3, 0x8A9E1196, 0x982BBE78, 0x2097D91D,\n            0x78F4C94B, 0xC048AE2E, 0xD2FD01C0, 0x6A4166A5,\n            0xF7965E1C, 0x4F2A3979, 0x5D9F9697, 0xE523F1F2,\n            0x4D6B1905, 0xF5D77E60, 0xE762D18E, 0x5FDEB6EB,\n            0xC2098E52, 0x7AB5E937, 0x680046D9, 0xD0BC21BC,\n            0x88DF31EA, 0x3063568F, 0x22D6F961, 0x9A6A9E04,\n            0x07BDA6BD, 0xBF01C1D8, 0xADB46E36, 0x15080953,\n            0x1D724E9A, 0xA5CE29FF, 0xB77B8611, 0x0FC7E174,\n            0x9210D9CD, 0x2AACBEA8, 0x38191146, 0x80A57623,\n            0xD8C66675, 0x607A0110, 0x72CFAEFE, 0xCA73C99B,\n            0x57A4F122, 0xEF189647, 0xFDAD39A9, 0x45115ECC,\n            0x764DEE06, 0xCEF18963, 0xDC44268D, 0x64F841E8,\n            0xF92F7951, 0x41931E34, 0x5326B1DA, 0xEB9AD6BF,\n            0xB3F9C6E9, 0x0B45A18C, 0x19F00E62, 0xA14C6907,\n            0x3C9B51BE, 0x842736DB, 0x96929935, 0x2E2EFE50,\n            0x2654B999, 0x9EE8DEFC, 0x8C5D7112, 0x34E11677,\n            0xA9362ECE, 0x118A49AB, 0x033FE645, 0xBB838120,\n            0xE3E09176, 0x5B5CF613, 0x49E959FD, 0xF1553E98,\n            0x6C820621, 0xD43E6144, 0xC68BCEAA, 0x7E37A9CF,\n            0xD67F4138, 0x6EC3265D, 0x7C7689B3, 0xC4CAEED6,\n            0x591DD66F, 0xE1A1B10A, 0xF3141EE4, 0x4BA87981,\n            0x13CB69D7, 0xAB770EB2, 0xB9C2A15C, 0x017EC639,\n            0x9CA9FE80, 0x241599E5, 0x36A0360B, 0x8E1C516E,\n            0x866616A7, 0x3EDA71C2, 0x2C6FDE2C, 0x94D3B949,\n            0x090481F0, 0xB1B8E695, 0xA30D497B, 0x1BB12E1E,\n            0x43D23E48, 0xFB6E592D, 0xE9DBF6C3, 0x516791A6,\n            0xCCB0A91F, 0x740CCE7A, 0x66B96194, 0xDE0506F1,\n    /* T8_4 */\n            0x00000000, 0x3D6029B0, 0x7AC05360, 0x47A07AD0,\n            0xF580A6C0, 0xC8E08F70, 0x8F40F5A0, 0xB220DC10,\n            0x30704BC1, 0x0D106271, 0x4AB018A1, 0x77D03111,\n            0xC5F0ED01, 0xF890C4B1, 0xBF30BE61, 0x825097D1,\n            0x60E09782, 0x5D80BE32, 0x1A20C4E2, 0x2740ED52,\n            0x95603142, 0xA80018F2, 0xEFA06222, 0xD2C04B92,\n            0x5090DC43, 0x6DF0F5F3, 0x2A508F23, 0x1730A693,\n            0xA5107A83, 0x98705333, 0xDFD029E3, 0xE2B00053,\n            0xC1C12F04, 0xFCA106B4, 0xBB017C64, 0x866155D4,\n            0x344189C4, 0x0921A074, 0x4E81DAA4, 0x73E1F314,\n            0xF1B164C5, 0xCCD14D75, 0x8B7137A5, 0xB6111E15,\n            0x0431C205, 0x3951EBB5, 0x7EF19165, 0x4391B8D5,\n            0xA121B886, 0x9C419136, 0xDBE1EBE6, 0xE681C256,\n            0x54A11E46, 0x69C137F6, 0x2E614D26, 0x13016496,\n            0x9151F347, 0xAC31DAF7, 0xEB91A027, 0xD6F18997,\n            0x64D15587, 0x59B17C37, 0x1E1106E7, 0x23712F57,\n            0x58F35849, 0x659371F9, 0x22330B29, 0x1F532299,\n            0xAD73FE89, 0x9013D739, 0xD7B3ADE9, 0xEAD38459,\n            0x68831388, 0x55E33A38, 0x124340E8, 0x2F236958,\n            0x9D03B548, 0xA0639CF8, 0xE7C3E628, 0xDAA3CF98,\n            0x3813CFCB, 0x0573E67B, 0x42D39CAB, 0x7FB3B51B,\n            0xCD93690B, 0xF0F340BB, 0xB7533A6B, 0x8A3313DB,\n            0x0863840A, 0x3503ADBA, 0x72A3D76A, 0x4FC3FEDA,\n            0xFDE322CA, 0xC0830B7A, 0x872371AA, 0xBA43581A,\n            0x9932774D, 0xA4525EFD, 0xE3F2242D, 0xDE920D9D,\n            0x6CB2D18D, 0x51D2F83D, 0x167282ED, 0x2B12AB5D,\n            0xA9423C8C, 0x9422153C, 0xD3826FEC, 0xEEE2465C,\n            0x5CC29A4C, 0x61A2B3FC, 0x2602C92C, 0x1B62E09C,\n            0xF9D2E0CF, 0xC4B2C97F, 0x8312B3AF, 0xBE729A1F,\n            0x0C52460F, 0x31326FBF, 0x7692156F, 0x4BF23CDF,\n            0xC9A2AB0E, 0xF4C282BE, 0xB362F86E, 0x8E02D1DE,\n            0x3C220DCE, 0x0142247E, 0x46E25EAE, 0x7B82771E,\n            0xB1E6B092, 0x8C869922, 0xCB26E3F2, 0xF646CA42,\n            0x44661652, 0x79063FE2, 0x3EA64532, 0x03C66C82,\n            0x8196FB53, 0xBCF6D2E3, 0xFB56A833, 0xC6368183,\n            0x74165D93, 0x49767423, 0x0ED60EF3, 0x33B62743,\n            0xD1062710, 0xEC660EA0, 0xABC67470, 0x96A65DC0,\n            0x248681D0, 0x19E6A860, 0x5E46D2B0, 0x6326FB00,\n            0xE1766CD1, 0xDC164561, 0x9BB63FB1, 0xA6D61601,\n            0x14F6CA11, 0x2996E3A1, 0x6E369971, 0x5356B0C1,\n            0x70279F96, 0x4D47B626, 0x0AE7CCF6, 0x3787E546,\n            0x85A73956, 0xB8C710E6, 0xFF676A36, 0xC2074386,\n            0x4057D457, 0x7D37FDE7, 0x3A978737, 0x07F7AE87,\n            0xB5D77297, 0x88B75B27, 0xCF1721F7, 0xF2770847,\n            0x10C70814, 0x2DA721A4, 0x6A075B74, 0x576772C4,\n            0xE547AED4, 0xD8278764, 0x9F87FDB4, 0xA2E7D404,\n            0x20B743D5, 0x1DD76A65, 0x5A7710B5, 0x67173905,\n            0xD537E515, 0xE857CCA5, 0xAFF7B675, 0x92979FC5,\n            0xE915E8DB, 0xD475C16B, 0x93D5BBBB, 0xAEB5920B,\n            0x1C954E1B, 0x21F567AB, 0x66551D7B, 0x5B3534CB,\n            0xD965A31A, 0xE4058AAA, 0xA3A5F07A, 0x9EC5D9CA,\n            0x2CE505DA, 0x11852C6A, 0x562556BA, 0x6B457F0A,\n            0x89F57F59, 0xB49556E9, 0xF3352C39, 0xCE550589,\n            0x7C75D999, 0x4115F029, 0x06B58AF9, 0x3BD5A349,\n            0xB9853498, 0x84E51D28, 0xC34567F8, 0xFE254E48,\n            0x4C059258, 0x7165BBE8, 0x36C5C138, 0x0BA5E888,\n            0x28D4C7DF, 0x15B4EE6F, 0x521494BF, 0x6F74BD0F,\n            0xDD54611F, 0xE03448AF, 0xA794327F, 0x9AF41BCF,\n            0x18A48C1E, 0x25C4A5AE, 0x6264DF7E, 0x5F04F6CE,\n            0xED242ADE, 0xD044036E, 0x97E479BE, 0xAA84500E,\n            0x4834505D, 0x755479ED, 0x32F4033D, 0x0F942A8D,\n            0xBDB4F69D, 0x80D4DF2D, 0xC774A5FD, 0xFA148C4D,\n            0x78441B9C, 0x4524322C, 0x028448FC, 0x3FE4614C,\n            0x8DC4BD5C, 0xB0A494EC, 0xF704EE3C, 0xCA64C78C,\n    /* T8_5 */\n            0x00000000, 0xCB5CD3A5, 0x4DC8A10B, 0x869472AE,\n            0x9B914216, 0x50CD91B3, 0xD659E31D, 0x1D0530B8,\n            0xEC53826D, 0x270F51C8, 0xA19B2366, 0x6AC7F0C3,\n            0x77C2C07B, 0xBC9E13DE, 0x3A0A6170, 0xF156B2D5,\n            0x03D6029B, 0xC88AD13E, 0x4E1EA390, 0x85427035,\n            0x9847408D, 0x531B9328, 0xD58FE186, 0x1ED33223,\n            0xEF8580F6, 0x24D95353, 0xA24D21FD, 0x6911F258,\n            0x7414C2E0, 0xBF481145, 0x39DC63EB, 0xF280B04E,\n            0x07AC0536, 0xCCF0D693, 0x4A64A43D, 0x81387798,\n            0x9C3D4720, 0x57619485, 0xD1F5E62B, 0x1AA9358E,\n            0xEBFF875B, 0x20A354FE, 0xA6372650, 0x6D6BF5F5,\n            0x706EC54D, 0xBB3216E8, 0x3DA66446, 0xF6FAB7E3,\n            0x047A07AD, 0xCF26D408, 0x49B2A6A6, 0x82EE7503,\n            0x9FEB45BB, 0x54B7961E, 0xD223E4B0, 0x197F3715,\n            0xE82985C0, 0x23755665, 0xA5E124CB, 0x6EBDF76E,\n            0x73B8C7D6, 0xB8E41473, 0x3E7066DD, 0xF52CB578,\n            0x0F580A6C, 0xC404D9C9, 0x4290AB67, 0x89CC78C2,\n            0x94C9487A, 0x5F959BDF, 0xD901E971, 0x125D3AD4,\n            0xE30B8801, 0x28575BA4, 0xAEC3290A, 0x659FFAAF,\n            0x789ACA17, 0xB3C619B2, 0x35526B1C, 0xFE0EB8B9,\n            0x0C8E08F7, 0xC7D2DB52, 0x4146A9FC, 0x8A1A7A59,\n            0x971F4AE1, 0x5C439944, 0xDAD7EBEA, 0x118B384F,\n            0xE0DD8A9A, 0x2B81593F, 0xAD152B91, 0x6649F834,\n            0x7B4CC88C, 0xB0101B29, 0x36846987, 0xFDD8BA22,\n            0x08F40F5A, 0xC3A8DCFF, 0x453CAE51, 0x8E607DF4,\n            0x93654D4C, 0x58399EE9, 0xDEADEC47, 0x15F13FE2,\n            0xE4A78D37, 0x2FFB5E92, 0xA96F2C3C, 0x6233FF99,\n            0x7F36CF21, 0xB46A1C84, 0x32FE6E2A, 0xF9A2BD8F,\n            0x0B220DC1, 0xC07EDE64, 0x46EAACCA, 0x8DB67F6F,\n            0x90B34FD7, 0x5BEF9C72, 0xDD7BEEDC, 0x16273D79,\n            0xE7718FAC, 0x2C2D5C09, 0xAAB92EA7, 0x61E5FD02,\n            0x7CE0CDBA, 0xB7BC1E1F, 0x31286CB1, 0xFA74BF14,\n            0x1EB014D8, 0xD5ECC77D, 0x5378B5D3, 0x98246676,\n            0x852156CE, 0x4E7D856B, 0xC8E9F7C5, 0x03B52460,\n            0xF2E396B5, 0x39BF4510, 0xBF2B37BE, 0x7477E41B,\n            0x6972D4A3, 0xA22E0706, 0x24BA75A8, 0xEFE6A60D,\n            0x1D661643, 0xD63AC5E6, 0x50AEB748, 0x9BF264ED,\n            0x86F75455, 0x4DAB87F0, 0xCB3FF55E, 0x006326FB,\n            0xF135942E, 0x3A69478B, 0xBCFD3525, 0x77A1E680,\n            0x6AA4D638, 0xA1F8059D, 0x276C7733, 0xEC30A496,\n            0x191C11EE, 0xD240C24B, 0x54D4B0E5, 0x9F886340,\n            0x828D53F8, 0x49D1805D, 0xCF45F2F3, 0x04192156,\n            0xF54F9383, 0x3E134026, 0xB8873288, 0x73DBE12D,\n            0x6EDED195, 0xA5820230, 0x2316709E, 0xE84AA33B,\n            0x1ACA1375, 0xD196C0D0, 0x5702B27E, 0x9C5E61DB,\n            0x815B5163, 0x4A0782C6, 0xCC93F068, 0x07CF23CD,\n            0xF6999118, 0x3DC542BD, 0xBB513013, 0x700DE3B6,\n            0x6D08D30E, 0xA65400AB, 0x20C07205, 0xEB9CA1A0,\n            0x11E81EB4, 0xDAB4CD11, 0x5C20BFBF, 0x977C6C1A,\n            0x8A795CA2, 0x41258F07, 0xC7B1FDA9, 0x0CED2E0C,\n            0xFDBB9CD9, 0x36E74F7C, 0xB0733DD2, 0x7B2FEE77,\n            0x662ADECF, 0xAD760D6A, 0x2BE27FC4, 0xE0BEAC61,\n            0x123E1C2F, 0xD962CF8A, 0x5FF6BD24, 0x94AA6E81,\n            0x89AF5E39, 0x42F38D9C, 0xC467FF32, 0x0F3B2C97,\n            0xFE6D9E42, 0x35314DE7, 0xB3A53F49, 0x78F9ECEC,\n            0x65FCDC54, 0xAEA00FF1, 0x28347D5F, 0xE368AEFA,\n            0x16441B82, 0xDD18C827, 0x5B8CBA89, 0x90D0692C,\n            0x8DD55994, 0x46898A31, 0xC01DF89F, 0x0B412B3A,\n            0xFA1799EF, 0x314B4A4A, 0xB7DF38E4, 0x7C83EB41,\n            0x6186DBF9, 0xAADA085C, 0x2C4E7AF2, 0xE712A957,\n            0x15921919, 0xDECECABC, 0x585AB812, 0x93066BB7,\n            0x8E035B0F, 0x455F88AA, 0xC3CBFA04, 0x089729A1,\n            0xF9C19B74, 0x329D48D1, 0xB4093A7F, 0x7F55E9DA,\n            0x6250D962, 0xA90C0AC7, 0x2F987869, 0xE4C4ABCC,\n    /* T8_6 */\n            0x00000000, 0xA6770BB4, 0x979F1129, 0x31E81A9D,\n            0xF44F2413, 0x52382FA7, 0x63D0353A, 0xC5A73E8E,\n            0x33EF4E67, 0x959845D3, 0xA4705F4E, 0x020754FA,\n            0xC7A06A74, 0x61D761C0, 0x503F7B5D, 0xF64870E9,\n            0x67DE9CCE, 0xC1A9977A, 0xF0418DE7, 0x56368653,\n            0x9391B8DD, 0x35E6B369, 0x040EA9F4, 0xA279A240,\n            0x5431D2A9, 0xF246D91D, 0xC3AEC380, 0x65D9C834,\n            0xA07EF6BA, 0x0609FD0E, 0x37E1E793, 0x9196EC27,\n            0xCFBD399C, 0x69CA3228, 0x582228B5, 0xFE552301,\n            0x3BF21D8F, 0x9D85163B, 0xAC6D0CA6, 0x0A1A0712,\n            0xFC5277FB, 0x5A257C4F, 0x6BCD66D2, 0xCDBA6D66,\n            0x081D53E8, 0xAE6A585C, 0x9F8242C1, 0x39F54975,\n            0xA863A552, 0x0E14AEE6, 0x3FFCB47B, 0x998BBFCF,\n            0x5C2C8141, 0xFA5B8AF5, 0xCBB39068, 0x6DC49BDC,\n            0x9B8CEB35, 0x3DFBE081, 0x0C13FA1C, 0xAA64F1A8,\n            0x6FC3CF26, 0xC9B4C492, 0xF85CDE0F, 0x5E2BD5BB,\n            0x440B7579, 0xE27C7ECD, 0xD3946450, 0x75E36FE4,\n            0xB044516A, 0x16335ADE, 0x27DB4043, 0x81AC4BF7,\n            0x77E43B1E, 0xD19330AA, 0xE07B2A37, 0x460C2183,\n            0x83AB1F0D, 0x25DC14B9, 0x14340E24, 0xB2430590,\n            0x23D5E9B7, 0x85A2E203, 0xB44AF89E, 0x123DF32A,\n            0xD79ACDA4, 0x71EDC610, 0x4005DC8D, 0xE672D739,\n            0x103AA7D0, 0xB64DAC64, 0x87A5B6F9, 0x21D2BD4D,\n            0xE47583C3, 0x42028877, 0x73EA92EA, 0xD59D995E,\n            0x8BB64CE5, 0x2DC14751, 0x1C295DCC, 0xBA5E5678,\n            0x7FF968F6, 0xD98E6342, 0xE86679DF, 0x4E11726B,\n            0xB8590282, 0x1E2E0936, 0x2FC613AB, 0x89B1181F,\n            0x4C162691, 0xEA612D25, 0xDB8937B8, 0x7DFE3C0C,\n            0xEC68D02B, 0x4A1FDB9F, 0x7BF7C102, 0xDD80CAB6,\n            0x1827F438, 0xBE50FF8C, 0x8FB8E511, 0x29CFEEA5,\n            0xDF879E4C, 0x79F095F8, 0x48188F65, 0xEE6F84D1,\n            0x2BC8BA5F, 0x8DBFB1EB, 0xBC57AB76, 0x1A20A0C2,\n            0x8816EAF2, 0x2E61E146, 0x1F89FBDB, 0xB9FEF06F,\n            0x7C59CEE1, 0xDA2EC555, 0xEBC6DFC8, 0x4DB1D47C,\n            0xBBF9A495, 0x1D8EAF21, 0x2C66B5BC, 0x8A11BE08,\n            0x4FB68086, 0xE9C18B32, 0xD82991AF, 0x7E5E9A1B,\n            0xEFC8763C, 0x49BF7D88, 0x78576715, 0xDE206CA1,\n            0x1B87522F, 0xBDF0599B, 0x8C184306, 0x2A6F48B2,\n            0xDC27385B, 0x7A5033EF, 0x4BB82972, 0xEDCF22C6,\n            0x28681C48, 0x8E1F17FC, 0xBFF70D61, 0x198006D5,\n            0x47ABD36E, 0xE1DCD8DA, 0xD034C247, 0x7643C9F3,\n            0xB3E4F77D, 0x1593FCC9, 0x247BE654, 0x820CEDE0,\n            0x74449D09, 0xD23396BD, 0xE3DB8C20, 0x45AC8794,\n            0x800BB91A, 0x267CB2AE, 0x1794A833, 0xB1E3A387,\n            0x20754FA0, 0x86024414, 0xB7EA5E89, 0x119D553D,\n            0xD43A6BB3, 0x724D6007, 0x43A57A9A, 0xE5D2712E,\n            0x139A01C7, 0xB5ED0A73, 0x840510EE, 0x22721B5A,\n            0xE7D525D4, 0x41A22E60, 0x704A34FD, 0xD63D3F49,\n            0xCC1D9F8B, 0x6A6A943F, 0x5B828EA2, 0xFDF58516,\n            0x3852BB98, 0x9E25B02C, 0xAFCDAAB1, 0x09BAA105,\n            0xFFF2D1EC, 0x5985DA58, 0x686DC0C5, 0xCE1ACB71,\n            0x0BBDF5FF, 0xADCAFE4B, 0x9C22E4D6, 0x3A55EF62,\n            0xABC30345, 0x0DB408F1, 0x3C5C126C, 0x9A2B19D8,\n            0x5F8C2756, 0xF9FB2CE2, 0xC813367F, 0x6E643DCB,\n            0x982C4D22, 0x3E5B4696, 0x0FB35C0B, 0xA9C457BF,\n            0x6C636931, 0xCA146285, 0xFBFC7818, 0x5D8B73AC,\n            0x03A0A617, 0xA5D7ADA3, 0x943FB73E, 0x3248BC8A,\n            0xF7EF8204, 0x519889B0, 0x6070932D, 0xC6079899,\n            0x304FE870, 0x9638E3C4, 0xA7D0F959, 0x01A7F2ED,\n            0xC400CC63, 0x6277C7D7, 0x539FDD4A, 0xF5E8D6FE,\n            0x647E3AD9, 0xC209316D, 0xF3E12BF0, 0x55962044,\n            0x90311ECA, 0x3646157E, 0x07AE0FE3, 0xA1D90457,\n            0x579174BE, 0xF1E67F0A, 0xC00E6597, 0x66796E23,\n            0xA3DE50AD, 0x05A95B19, 0x34414184, 0x92364A30,\n    /* T8_7 */\n            0x00000000, 0xCCAA009E, 0x4225077D, 0x8E8F07E3,\n            0x844A0EFA, 0x48E00E64, 0xC66F0987, 0x0AC50919,\n            0xD3E51BB5, 0x1F4F1B2B, 0x91C01CC8, 0x5D6A1C56,\n            0x57AF154F, 0x9B0515D1, 0x158A1232, 0xD92012AC,\n            0x7CBB312B, 0xB01131B5, 0x3E9E3656, 0xF23436C8,\n            0xF8F13FD1, 0x345B3F4F, 0xBAD438AC, 0x767E3832,\n            0xAF5E2A9E, 0x63F42A00, 0xED7B2DE3, 0x21D12D7D,\n            0x2B142464, 0xE7BE24FA, 0x69312319, 0xA59B2387,\n            0xF9766256, 0x35DC62C8, 0xBB53652B, 0x77F965B5,\n            0x7D3C6CAC, 0xB1966C32, 0x3F196BD1, 0xF3B36B4F,\n            0x2A9379E3, 0xE639797D, 0x68B67E9E, 0xA41C7E00,\n            0xAED97719, 0x62737787, 0xECFC7064, 0x205670FA,\n            0x85CD537D, 0x496753E3, 0xC7E85400, 0x0B42549E,\n            0x01875D87, 0xCD2D5D19, 0x43A25AFA, 0x8F085A64,\n            0x562848C8, 0x9A824856, 0x140D4FB5, 0xD8A74F2B,\n            0xD2624632, 0x1EC846AC, 0x9047414F, 0x5CED41D1,\n            0x299DC2ED, 0xE537C273, 0x6BB8C590, 0xA712C50E,\n            0xADD7CC17, 0x617DCC89, 0xEFF2CB6A, 0x2358CBF4,\n            0xFA78D958, 0x36D2D9C6, 0xB85DDE25, 0x74F7DEBB,\n            0x7E32D7A2, 0xB298D73C, 0x3C17D0DF, 0xF0BDD041,\n            0x5526F3C6, 0x998CF358, 0x1703F4BB, 0xDBA9F425,\n            0xD16CFD3C, 0x1DC6FDA2, 0x9349FA41, 0x5FE3FADF,\n            0x86C3E873, 0x4A69E8ED, 0xC4E6EF0E, 0x084CEF90,\n            0x0289E689, 0xCE23E617, 0x40ACE1F4, 0x8C06E16A,\n            0xD0EBA0BB, 0x1C41A025, 0x92CEA7C6, 0x5E64A758,\n            0x54A1AE41, 0x980BAEDF, 0x1684A93C, 0xDA2EA9A2,\n            0x030EBB0E, 0xCFA4BB90, 0x412BBC73, 0x8D81BCED,\n            0x8744B5F4, 0x4BEEB56A, 0xC561B289, 0x09CBB217,\n            0xAC509190, 0x60FA910E, 0xEE7596ED, 0x22DF9673,\n            0x281A9F6A, 0xE4B09FF4, 0x6A3F9817, 0xA6959889,\n            0x7FB58A25, 0xB31F8ABB, 0x3D908D58, 0xF13A8DC6,\n            0xFBFF84DF, 0x37558441, 0xB9DA83A2, 0x7570833C,\n            0x533B85DA, 0x9F918544, 0x111E82A7, 0xDDB48239,\n            0xD7718B20, 0x1BDB8BBE, 0x95548C5D, 0x59FE8CC3,\n            0x80DE9E6F, 0x4C749EF1, 0xC2FB9912, 0x0E51998C,\n            0x04949095, 0xC83E900B, 0x46B197E8, 0x8A1B9776,\n            0x2F80B4F1, 0xE32AB46F, 0x6DA5B38C, 0xA10FB312,\n            0xABCABA0B, 0x6760BA95, 0xE9EFBD76, 0x2545BDE8,\n            0xFC65AF44, 0x30CFAFDA, 0xBE40A839, 0x72EAA8A7,\n            0x782FA1BE, 0xB485A120, 0x3A0AA6C3, 0xF6A0A65D,\n            0xAA4DE78C, 0x66E7E712, 0xE868E0F1, 0x24C2E06F,\n            0x2E07E976, 0xE2ADE9E8, 0x6C22EE0B, 0xA088EE95,\n            0x79A8FC39, 0xB502FCA7, 0x3B8DFB44, 0xF727FBDA,\n            0xFDE2F2C3, 0x3148F25D, 0xBFC7F5BE, 0x736DF520,\n            0xD6F6D6A7, 0x1A5CD639, 0x94D3D1DA, 0x5879D144,\n            0x52BCD85D, 0x9E16D8C3, 0x1099DF20, 0xDC33DFBE,\n            0x0513CD12, 0xC9B9CD8C, 0x4736CA6F, 0x8B9CCAF1,\n            0x8159C3E8, 0x4DF3C376, 0xC37CC495, 0x0FD6C40B,\n            0x7AA64737, 0xB60C47A9, 0x3883404A, 0xF42940D4,\n            0xFEEC49CD, 0x32464953, 0xBCC94EB0, 0x70634E2E,\n            0xA9435C82, 0x65E95C1C, 0xEB665BFF, 0x27CC5B61,\n            0x2D095278, 0xE1A352E6, 0x6F2C5505, 0xA386559B,\n            0x061D761C, 0xCAB77682, 0x44387161, 0x889271FF,\n            0x825778E6, 0x4EFD7878, 0xC0727F9B, 0x0CD87F05,\n            0xD5F86DA9, 0x19526D37, 0x97DD6AD4, 0x5B776A4A,\n            0x51B26353, 0x9D1863CD, 0x1397642E, 0xDF3D64B0,\n            0x83D02561, 0x4F7A25FF, 0xC1F5221C, 0x0D5F2282,\n            0x079A2B9B, 0xCB302B05, 0x45BF2CE6, 0x89152C78,\n            0x50353ED4, 0x9C9F3E4A, 0x121039A9, 0xDEBA3937,\n            0xD47F302E, 0x18D530B0, 0x965A3753, 0x5AF037CD,\n            0xFF6B144A, 0x33C114D4, 0xBD4E1337, 0x71E413A9,\n            0x7B211AB0, 0xB78B1A2E, 0x39041DCD, 0xF5AE1D53,\n            0x2C8E0FFF, 0xE0240F61, 0x6EAB0882, 0xA201081C,\n            0xA8C40105, 0x646E019B, 0xEAE10678, 0x264B06E6\n    };\n\n    public static void main(String[] args) {\n        Set xx=new HashSet();\n        long start=System.currentTimeMillis();\n        int i1 = 100000000;\n        for (int i = 0; i< i1; i++)\n        {\n            PureJavaCrc32 crc32 = new PureJavaCrc32();\n            byte[] bytes = String.valueOf(i).getBytes();\n            crc32.update(bytes,0,bytes.length);\n            long x = crc32.getValue();\n            x=x%65535;\n           if(xx.contains(x))\n            {\n                System.out.println(x);\n            }\n\n           // xx.add(x);\n\n        }\n        long used=System.currentTimeMillis()-start;\n\n        System.out.println(\"tps \"+i1*1000.0/used);\n        System.out.println(\"..............................\"+xx.size());\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/ReloadFunction.java",
    "content": "package io.mycat.route.function;\n\n/**\n * Created by magicdoom on 2016/9/17.\n */\npublic interface ReloadFunction {\n   void reload();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/SlotFunction.java",
    "content": "package io.mycat.route.function;\n\n/**\n * Created by magicdoom on 2016/9/17.\n */\npublic interface SlotFunction {\n    int slotValue();\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/function/TableRuleAware.java",
    "content": "package io.mycat.route.function;\n\nimport io.mycat.config.model.TableConfig;\n\n/**\n * Created by magicdoom on 2016/9/5.\n * 考虑一类新分片算法     属于有状态算法\n * 比如PartitionByCRC32PreSlot 如果迁移过数据的话，slot映射规则会进行改变\n * 所以必须对应一张表单独一个实例      实现此接口后会根据不同表自动创建新实例\n */\npublic interface TableRuleAware{\n    void setTableConfig(TableConfig tableConfig);\n\n    void setRuleName(String ruleName);\n\n    TableConfig getTableConfig();\n\n    String getRuleName();\n\n    /**\n     * 如果是实例则返回true,不是实例则是false\n     * cjw qq:294712221\n     * @return\n     */\n    boolean isIstance();\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/handler/HintCatletHandler.java",
    "content": "package io.mycat.route.handler;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.catlets.Catlet;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.sqlengine.EngineCtx;\n\n/**\n * 处理注释中类型为catlet 的情况,每个catlet为一个用户自定义Java代码类，用于进行复杂查询SQL（只能是查询SQL）的自定义执行过程，\n * 目前主要用于跨分片Join的人工智能编码\n */\npublic class HintCatletHandler implements HintHandler {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(HintCatletHandler.class);\n\n\t/**\n\t * 从全局的schema列表中查询指定的schema是否存在， 如果存在则替换connection属性中原有的schema，\n\t * 如果不存在，则throws SQLNonTransientException，表示指定的schema 不存在\n\t * \n\t * @param sysConfig\n\t * @param schema\n\t * @param sqlType\n\t * @param realSQL\n\t * @param charset\n\t * @param info\n\t * @param cachePool\n\t * @param hintSQLValue\n\t * @return\n\t * @throws SQLNonTransientException\n\t */\n\t@Override\n\tpublic RouteResultset route(SystemConfig sysConfig, SchemaConfig schema,\n\t\t\tint sqlType, String realSQL, String charset, ServerConnection sc,\n\t\t\tLayerCachePool cachePool, String hintSQLValue,int hintSqlType, Map hintMap)\n\t\t\tthrows SQLNonTransientException {\n\t\t// sc.setEngineCtx ctx\n\t\tString cateletClass = hintSQLValue;\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"load catelet class:\" + hintSQLValue + \" to run sql \"\n\t\t\t\t\t+ realSQL);\n\t\t}\n\t\ttry {\n\t\t\tCatlet catlet = (Catlet) MycatServer.getInstance()\n\t\t\t\t\t.getCatletClassLoader().getInstanceofClass(cateletClass);\n\t\t\tcatlet.route(sysConfig, schema, sqlType, realSQL,charset, sc, cachePool);\n\t\t\tcatlet.processSQL(realSQL, new EngineCtx(sc.getSession2()));\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.warn(\"catlet error \"+e);\n\t\t\tthrow new SQLNonTransientException(e);\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/handler/HintDataNodeHandler.java",
    "content": "package io.mycat.route.handler;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport com.alibaba.druid.sql.ast.expr.SQLTextLiteralExpr;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement;\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\nimport io.mycat.route.parser.druid.MycatStatementParser;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.sqlengine.mpp.LoadData;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.util.RouterUtil;\nimport io.mycat.server.ServerConnection;\n\n/**\n * 处理注释中类型为datanode 的情况\n * \n * @author zhuam\n */\npublic class HintDataNodeHandler implements HintHandler {\n\t\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(HintSchemaHandler.class);\n\n\t@Override\n\tpublic RouteResultset route(SystemConfig sysConfig, SchemaConfig schema, int sqlType, String realSQL,\n\t\t\tString charset, ServerConnection sc, LayerCachePool cachePool, String hintSQLValue,int hintSqlType, Map hintMap)\n\t\t\t\t\tthrows SQLNonTransientException {\n\t\t\n\t\tString stmt = realSQL;\n\t\t\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"route datanode sql hint from \" + stmt);\n\t\t}\n\t\t\n\t\tRouteResultset rrs = new RouteResultset(stmt, sqlType);\t\t\n\t\tPhysicalDBNode dataNode = MycatServer.getInstance().getConfig().getDataNodes().get(hintSQLValue);\n\t\tif (dataNode != null) {\t\t\t\n\t\t\trrs = RouterUtil.routeToSingleNode(rrs, dataNode.getName(), stmt);\n\t\t} else {\n\t\t\tString msg = \"can't find hint datanode:\" + hintSQLValue;\n\t\t\tLOGGER.warn(msg);\n\t\t\tthrow new SQLNonTransientException(msg);\n\t\t}\n\n\t\t// 处理导入参数初始化\n\t\tif(rrs.getSqlType() == ServerParse.LOAD_DATA_INFILE_SQL){\n\t\t\tLOGGER.info(\"load data use annotation datanode\");\n\t\t\trrs.getNodes()[0].setLoadData(parseLoadDataPram(stmt , charset));\n\t\t}\n\t\t\n\t\treturn rrs;\n\t}\n\n\t// 初始化导入参数\n\tprivate LoadData parseLoadDataPram(String sql , String connectionCharset)\n\t{\n\t\tSQLStatementParser parser = new MycatStatementParser(sql);\n\t\tMySqlLoadDataInFileStatement statement = (MySqlLoadDataInFileStatement) parser.parseStatement();\n\n\t\tLoadData loadData = new LoadData();\n\t\tSQLTextLiteralExpr rawLineEnd = (SQLTextLiteralExpr) statement.getLinesTerminatedBy();\n\t\tString lineTerminatedBy = rawLineEnd == null ? \"\\n\" : rawLineEnd.getText();\n\t\tloadData.setLineTerminatedBy(lineTerminatedBy);\n\n\t\tSQLTextLiteralExpr rawFieldEnd = (SQLTextLiteralExpr) statement.getColumnsTerminatedBy();\n\t\tString fieldTerminatedBy = rawFieldEnd == null ? \"\\t\" : rawFieldEnd.getText();\n\t\tloadData.setFieldTerminatedBy(fieldTerminatedBy);\n\n\t\tSQLTextLiteralExpr rawEnclosed = (SQLTextLiteralExpr) statement.getColumnsEnclosedBy();\n\t\tString enclose = rawEnclosed == null ? null : rawEnclosed.getText();\n\t\tloadData.setEnclose(enclose);\n\n\t\tSQLTextLiteralExpr escapseExpr =  (SQLTextLiteralExpr)statement.getColumnsEscaped() ;\n\t\tString escapse=escapseExpr==null?\"\\\\\":escapseExpr.getText();\n\t\tloadData.setEscape(escapse);\n\t\tString charset = statement.getCharset() != null ? statement.getCharset() : connectionCharset;\n\t\tloadData.setCharset(charset);\n\n\t\tString fileName = parseFileName(sql);\n\t\tif(StringUtils.isBlank(fileName)){\n\t\t\tthrow new RuntimeException(\" file name is null !\");\n\t\t}\n\n\t\tloadData.setFileName(fileName);\n\n\t\treturn loadData ;\n\t}\n\n\t// 处理文件名\n\tprivate String parseFileName(String sql)\n\t{\n\t\tif (sql.contains(\"'\"))\n\t\t{\n\t\t\tint beginIndex = sql.indexOf(\"'\");\n\t\t\treturn sql.substring(beginIndex + 1, sql.indexOf(\"'\", beginIndex + 1));\n\t\t} else if (sql.contains(\"\\\"\"))\n\t\t{\n\t\t\tint beginIndex = sql.indexOf(\"\\\"\");\n\t\t\treturn sql.substring(beginIndex + 1, sql.indexOf(\"\\\"\", beginIndex + 1));\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/handler/HintHandler.java",
    "content": "package io.mycat.route.handler;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.server.ServerConnection;\n\n/**\n * 按照注释中包含指定类型的内容做路由解析\n * \n */\npublic interface HintHandler {\n\n\tpublic RouteResultset route(SystemConfig sysConfig, SchemaConfig schema,\n                                int sqlType, String realSQL, String charset, ServerConnection sc,\n                                LayerCachePool cachePool, String hintSQLValue, int hintSqlType, Map hintMap)\n\t\t\tthrows SQLNonTransientException;\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/handler/HintHandlerFactory.java",
    "content": "package io.mycat.route.handler;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.FutureTask;\n\npublic class HintHandlerFactory {\n\t\n\tprivate static volatile boolean isInit = false;\n\t\n\t //sql注释的类型处理handler 集合，现在支持两种类型的处理：sql,schema\n    private static Map<String,HintHandler> hintHandlerMap = new HashMap<String,HintHandler>();\n\n    private HintHandlerFactory() {\n    }\n    \n    private static void init() {\n        hintHandlerMap.put(\"sql\",new HintSQLHandler());\n        hintHandlerMap.put(\"schema\",new HintSchemaHandler());\n        hintHandlerMap.put(\"datanode\",new HintDataNodeHandler());\n        hintHandlerMap.put(\"catlet\",new HintCatletHandler());\n        \n        // 新增sql hint（注解）/*#mycat:db_type=master*/ 和 /*#mycat:db_type=slave*/  和 /*mycat:db_type=slave*/\n        // 该hint可以和 /*balance*/ 一起使用\n        // 实现强制走 master 和 强制走 slave\n        hintHandlerMap.put(\"db_type\", new HintMasterDBHandler());\n        isInit = true;\t// 修复多次初始化的bug\n    }\n    \n    // 双重校验锁 fix 线程安全问题\n    public static HintHandler getHintHandler(String hintType) {\n    \tif(!isInit) {\n    \t\tsynchronized(HintHandlerFactory.class){\n    \t\t\tif(!isInit) {\n                    init();\n                }\n    \t\t}\n    \t}\n    \treturn hintHandlerMap.get(hintType);\n    }\n    \n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/handler/HintMasterDBHandler.java",
    "content": "package io.mycat.route.handler;\r\n\r\n\r\nimport java.sql.SQLNonTransientException;\r\nimport java.util.Map;\r\n\r\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\r\nimport io.mycat.cache.LayerCachePool;\r\nimport io.mycat.config.model.SchemaConfig;\r\nimport io.mycat.config.model.SystemConfig;\r\nimport io.mycat.route.RouteResultset;\r\nimport io.mycat.route.factory.RouteStrategyFactory;\r\nimport io.mycat.server.ServerConnection;\r\nimport io.mycat.server.parser.ServerParse;\r\n\r\n/**\r\n * 处理情况 sql hint: mycat:db_type=master/slave<br/>\r\n * 后期可能会考虑增加 mycat:db_type=slave_newest，实现走延迟最小的slave\r\n * @author digdeep@126.com\r\n */\r\n// /*#mycat:db_type=master*/\r\n// /*#mycat:db_type=slave*/\r\n// /*#mycat:db_type=slave_newest*/\r\n// 强制走 master 和 强制走 slave\r\npublic class HintMasterDBHandler implements HintHandler {\r\n\t\r\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(HintMasterDBHandler.class);\r\n\r\n\t@Override\r\n\tpublic RouteResultset route(SystemConfig sysConfig, SchemaConfig schema, int sqlType, \r\n\t\t\tString realSQL, String charset,\r\n\t\t\tServerConnection sc, LayerCachePool cachePool, String hintSQLValue, int hintSqlType, Map hintMap)\r\n\t\t\tthrows SQLNonTransientException {\r\n\t\t\r\n//\t\tLOGGER.debug(\"realSQL: \" + realSQL); // select * from travelrecord limit 1\r\n//\t\tLOGGER.debug(\"sqlType: \" + sqlType); // 7\r\n//\t\tLOGGER.debug(\"schema.getName(): \" + schema.getName()); // TESTDB\r\n//\t\tLOGGER.debug(\"schema.getName(): \" + schema.getDataNode()); // null\r\n//\t\tLOGGER.debug(\"hintSQLValue: \" + hintSQLValue); // master/slave\r\n\t\t\r\n\t\tRouteResultset rrs = RouteStrategyFactory.getRouteStrategy()\r\n\t\t\t\t\t\t\t\t\t.route(sysConfig, schema, sqlType, \r\n\t\t\t\t\t\t\t\t\t\trealSQL, charset, sc, cachePool);\r\n\t\t\r\n\t\tLOGGER.debug(\"schema.rrs(): \" + rrs); // master\r\n\t\tBoolean isRouteToMaster = null;\t// 默认不施加任何影响\r\n\t\t\r\n\t\tLOGGER.debug(\"hintSQLValue:::::::::\" + hintSQLValue); // slave\r\n\t\t\r\n\t\tif(hintSQLValue != null && !hintSQLValue.trim().equals(\"\")){\r\n\t\t\tif(hintSQLValue.trim().equalsIgnoreCase(\"master\")) {\r\n\t\t\t\tisRouteToMaster = true;\r\n\t\t\t}\r\n\t\t\tif(hintSQLValue.trim().equalsIgnoreCase(\"slave\")){\r\n//\t\t\t\tif(rrs.getCanRunInReadDB() != null && !rrs.getCanRunInReadDB()){\r\n//\t\t\t\t\tisRouteToMaster = null;\r\n//\t\t\t\t\tLOGGER.warn(realSQL + \" can not run in slave.\");\r\n//\t\t\t\t}else{\r\n//\t\t\t\t\tisRouteToMaster = false;\r\n//\t\t\t\t}\r\n\t\t\t\tif(sqlType == ServerParse.DELETE || sqlType == ServerParse.INSERT\r\n\t\t\t\t\t\t||sqlType == ServerParse.REPLACE || sqlType == ServerParse.UPDATE\r\n\t\t\t\t\t\t|| sqlType == ServerParse.DDL){\r\n\t\t\t\t\tLOGGER.error(\"should not use hint 'db_type' to route 'delete', 'insert', 'replace', 'update', 'ddl' to a slave db.\");\r\n\t\t\t\t\tisRouteToMaster = null;\t// 不施加任何影响\r\n\t\t\t\t}else{\r\n\t\t\t\t\tisRouteToMaster = false;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tif(isRouteToMaster == null){\t// 默认不施加任何影响\r\n\t\t\tLOGGER.warn(\" sql hint 'db_type' error, ignore this hint.\");\r\n\t\t\treturn rrs;\r\n\t\t}\r\n\t\t\r\n\t\tif(isRouteToMaster)\t {// 强制走 master\r\n\t\t\trrs.setRunOnSlave(false);\r\n\t\t}\r\n\t\t\r\n\t\tif(!isRouteToMaster) {// 强制走slave\r\n\t\t\trrs.setRunOnSlave(true);\r\n\t\t}\r\n\t\t\r\n\t\tLOGGER.debug(\"rrs.getRunOnSlave():\" + rrs.getRunOnSlaveDebugInfo());\r\n\t\treturn rrs;\r\n\t}\r\n\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/handler/HintSQLHandler.java",
    "content": "package io.mycat.route.handler;\n\nimport java.sql.SQLNonTransientException;\nimport java.sql.Types;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLNumberExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLTextLiteralExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLValuableExpr;\nimport com.alibaba.druid.sql.ast.statement.*;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\nimport com.google.common.base.Splitter;\nimport com.google.common.base.Strings;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.*;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\n\n/**\n * 处理注释中 类型为sql的情况 （按照 注释中的sql做路由解析，而不是实际的sql）\n */\npublic class HintSQLHandler implements HintHandler {\n\t\n\tprivate RouteStrategy routeStrategy;\n\t\n\tpublic HintSQLHandler() {\n\t\tthis.routeStrategy = RouteStrategyFactory.getRouteStrategy();\n\t}\n\n\t@Override\n\tpublic RouteResultset route(SystemConfig sysConfig, SchemaConfig schema,\n\t\t\tint sqlType, String realSQL, String charset, ServerConnection sc,\n\t\t\tLayerCachePool cachePool, String hintSQLValue,int hintSqlType, Map hintMap)\n            throws SQLNonTransientException {\n\t\t\n\t\tRouteResultset rrs = routeStrategy.route(sysConfig, schema, hintSqlType,\n\t\t\t\thintSQLValue, charset, sc, cachePool);\n\t\t\n\t\t// 替换RRS中的SQL执行\n\t\tRouteResultsetNode[] oldRsNodes = rrs.getNodes();\n\t\tRouteResultsetNode[] newRrsNodes = new RouteResultsetNode[oldRsNodes.length];\n\t\tfor (int i = 0; i < newRrsNodes.length; i++) {\n\t\t\tnewRrsNodes[i] = new RouteResultsetNode(oldRsNodes[i].getName(),\n\t\t\t\t\toldRsNodes[i].getSqlType(), realSQL);\n            newRrsNodes[i].setSlot(oldRsNodes[i].getSlot());\n\t\t}\n\t\trrs.setNodes(newRrsNodes);\n\n\t\t// 判断是否为调用存储过程的SQL语句，这里不能用SQL解析器来解析判断是否为CALL语句\n\t\tif (ServerParse.CALL == sqlType) {\n\t\t\trrs.setCallStatement(true);\n\n             Procedure procedure=parseProcedure(realSQL,hintMap);\n            rrs.setProcedure(procedure);\n        //    String sql=procedure.toChangeCallSql(null);\n            String sql=realSQL;\n            for (RouteResultsetNode node : rrs.getNodes())\n            {\n                node.setProcedure(procedure);\n                node.setHintMap(hintMap);\n                node.setStatement(sql);\n            }\n\n\t\t}\n\n\t\treturn rrs;\n\t}\n\n\n\n    private   Procedure parseProcedure(String sql,Map hintMap)\n    {\n        boolean fields = hintMap.containsKey(\"list_fields\");\n        boolean isResultList= hintMap != null && (\"list\".equals(hintMap.get(\"result_type\"))|| fields);\n        Procedure procedure=new Procedure();\n        procedure.setOriginSql(sql);\n        procedure.setResultList(isResultList);\n        List<String> sqls= Splitter.on(\";\").trimResults().splitToList(sql)    ;\n        Set<String> outSet=new HashSet<>();\n        for (int i = sqls.size() - 1; i >= 0; i--)\n        {\n            String s = sqls.get(i);\n            if(Strings.isNullOrEmpty(s)) {\n                continue;\n            }\n            SQLStatementParser parser = new MySqlStatementParser(s);\n            SQLStatement statement = parser.parseStatement();\n            if(statement instanceof SQLSelectStatement)\n            {\n                MySqlSelectQueryBlock selectQuery= (MySqlSelectQueryBlock) ((SQLSelectStatement) statement).getSelect().getQuery();\n                if(selectQuery!=null)\n                {\n                    List<SQLSelectItem> selectItems=   selectQuery.getSelectList();\n                    for (SQLSelectItem selectItem : selectItems)\n                    {\n                        String select = selectItem.toString();\n                        outSet.add(select) ;\n                        procedure.getSelectColumns().add(select);\n                    }\n                }\n               procedure.setSelectSql(s);\n            }  else  if(statement instanceof SQLCallStatement)\n            {\n                SQLCallStatement sqlCallStatement = (SQLCallStatement) statement;\n                procedure.setName(sqlCallStatement.getProcedureName().getSimpleName());\n                List<SQLExpr> paramterList= sqlCallStatement.getParameters();\n                for (int i1 = 0; i1 < paramterList.size(); i1++)\n                {\n                    SQLExpr sqlExpr = paramterList.get(i1);\n                    String pName = sqlExpr.toString();\n                    String pType=outSet.contains(pName)? ProcedureParameter.OUT:ProcedureParameter.IN;\n                    ProcedureParameter parameter=new ProcedureParameter();\n                    parameter.setIndex(i1+1);\n                    parameter.setName(pName);\n                    parameter.setParameterType(pType);\n                    if(pName.startsWith(\"@\"))\n                    {\n                        procedure.getParamterMap().put(pName, parameter);\n                    }   else\n                    {\n                        procedure.getParamterMap().put(String.valueOf(i1+1), parameter);\n                    }\n\n\n                }\n                procedure.setCallSql(s);\n            }   else  if(statement instanceof SQLSetStatement)\n            {\n                procedure.setSetSql(s);\n                SQLSetStatement setStatement= (SQLSetStatement) statement;\n                List<SQLAssignItem> sets= setStatement.getItems();\n                for (SQLAssignItem set : sets)\n                {\n                    String name=set.getTarget().toString();\n                     SQLExpr value=set.getValue();\n                    ProcedureParameter parameter = procedure.getParamterMap().get(name);\n                    if(parameter!=null)\n                    {\n                        if (value instanceof SQLIntegerExpr)\n                        {\n                           parameter.setValue(((SQLIntegerExpr) value).getNumber());\n                            parameter.setJdbcType(Types.INTEGER);\n                        }  else   if(value instanceof SQLNumberExpr)\n                        {\n                            parameter.setValue(((SQLNumberExpr) value).getNumber());\n                            parameter.setJdbcType(Types.NUMERIC);\n                        }\n                        else if(value instanceof SQLTextLiteralExpr)\n                        {\n                            parameter.setValue(((SQLTextLiteralExpr) value).getText());\n                            parameter.setJdbcType(Types.VARCHAR);\n                        }\n                        else\n                        if (value instanceof SQLValuableExpr)\n                        {\n                            parameter.setValue(((SQLValuableExpr) value).getValue());\n                            parameter.setJdbcType(Types.VARCHAR);\n                        }\n                    }\n                }\n            }\n\n        }\n        if(fields)\n        {\n            String list_fields =(String) hintMap.get(\"list_fields\");\n            List<String> listFields = Splitter.on(\",\").trimResults().splitToList( list_fields);\n            for (String field : listFields)\n            {\n                if(!procedure.getParamterMap().containsKey(field))\n                {\n                    ProcedureParameter parameter=new ProcedureParameter();\n                    parameter.setParameterType(ProcedureParameter.OUT);\n                    parameter.setName(field);\n                    parameter.setJdbcType(-10);\n                    parameter.setIndex(procedure.getParamterMap().size()+1);\n                    procedure.getParamterMap().put(field,parameter);\n                }\n            }\n            procedure.getListFields().addAll(listFields);\n        }\n        return procedure;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/handler/HintSchemaHandler.java",
    "content": "package io.mycat.route.handler;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteStrategy;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.server.ServerConnection;\n\n/**\n * 处理注释中类型为schema 的情况（按照指定schema做路由解析）\n */\npublic class HintSchemaHandler implements HintHandler {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(HintSchemaHandler.class);\n\n\tprivate RouteStrategy routeStrategy;\n    \n    public HintSchemaHandler() {\n\t\tthis.routeStrategy = RouteStrategyFactory.getRouteStrategy();\n\t}\n\t/**\n\t * 从全局的schema列表中查询指定的schema是否存在， 如果存在则替换connection属性中原有的schema，\n\t * 如果不存在，则throws SQLNonTransientException，表示指定的schema 不存在\n\t * \n\t * @param sysConfig\n\t * @param schema\n\t * @param sqlType\n\t * @param realSQL\n\t * @param charset\n\t * @param info\n\t * @param cachePool\n\t * @param hintSQLValue\n\t * @return\n\t * @throws SQLNonTransientException\n\t */\n\t@Override\n\tpublic RouteResultset route(SystemConfig sysConfig, SchemaConfig schema,\n\t\t\tint sqlType, String realSQL, String charset, ServerConnection sc,\n\t\t\tLayerCachePool cachePool, String hintSQLValue,int hintSqlType, Map hintMap)\n\t\t\tthrows SQLNonTransientException {\n\t    SchemaConfig tempSchema = MycatServer.getInstance().getConfig().getSchemas().get(hintSQLValue);\n\t\tif (tempSchema != null) {\n\t\t\treturn routeStrategy.route(sysConfig, tempSchema, sqlType, realSQL, charset, sc, cachePool);\n\t\t} else {\n\t\t\tString msg = \"can't find hint schema:\" + hintSQLValue;\n\t\t\tLOGGER.warn(msg);\n\t\t\tthrow new SQLNonTransientException(msg);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/impl/AbstractRouteStrategy.java",
    "content": "package io.mycat.route.impl;\n\nimport java.sql.SQLNonTransientException;\nimport java.sql.SQLSyntaxErrorException;\nimport java.util.List;\n\nimport io.mycat.config.model.TableConfig;\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteStrategy;\nimport io.mycat.route.util.RouterUtil;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.sqlengine.mpp.LoadData;\n\npublic abstract class AbstractRouteStrategy implements RouteStrategy {\n\t\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(AbstractRouteStrategy.class);\n\n\t@Override\n\tpublic RouteResultset route(SystemConfig sysConfig, SchemaConfig schema, int sqlType, String origSQL,\n\t\t\tString charset, ServerConnection sc, LayerCachePool cachePool) throws SQLNonTransientException {\n\n\t\t//对应schema标签checkSQLschema属性，把表示schema的字符去掉\n\t\tif (schema.isCheckSQLSchema()) {\n\t\t\torigSQL = RouterUtil.removeSchema(origSQL, schema.getName());\n\t\t}\n\n\t\t/**\n     * 处理一些路由之前的逻辑\n     * 全局序列号，父子表插入\n     */\n\t\tif ( beforeRouteProcess(schema, sqlType, origSQL, sc) ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t/**\n\t\t * SQL 语句拦截\n\t\t */\n\t\tString stmt = MycatServer.getInstance().getSqlInterceptor().interceptSQL(origSQL, sqlType);\n\t\tif (!origSQL.equals(stmt) && LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"sql intercepted to \" + stmt + \" from \" + origSQL);\n\t\t}\n\n\n\t\tRouteResultset rrs = new RouteResultset(stmt, sqlType);\n\n\t\t/**\n\t\t * 优化debug loaddata输出cache的日志会极大降低性能\n\t\t */\n\t\tif (LOGGER.isDebugEnabled() && origSQL.startsWith(LoadData.loadDataHint)) {\n\t\t\trrs.setCacheAble(false);\n\t\t}\n\n        /**\n         * rrs携带ServerConnection的autocommit状态用于在sql解析的时候遇到\n         * select ... for update的时候动态设定RouteResultsetNode的canRunInReadDB属性\n         */\n\t\tif (sc != null ) {\n\t\t\trrs.setAutocommit(sc.isAutocommit());\n\t\t}\n\n\t\t/**\n\t\t * DDL 语句的路由\n\t\t */\n\t\tif (ServerParse.DDL == sqlType) {\n\t\t\treturn RouterUtil.routeToDDLNode(rrs, sqlType, stmt, schema);\n\t\t}\n\n\t\t/**\n\t\t * 检查是否有分片\n\t\t */\n\t\tif (schema.isNoSharding() && ServerParse.SHOW != sqlType) {\n\t\t\trrs = RouterUtil.routeToSingleNode(rrs, schema.getDataNode(), stmt);\n\t\t} else {\n\t\t\tRouteResultset returnedSet = routeSystemInfo(schema, sqlType, stmt, rrs);\n\t\t\tif (returnedSet == null) {\n\t\t\t\trrs = routeNormalSqlWithAST(schema, stmt, rrs, charset, cachePool,sqlType,sc);\n\t\t\t}\n\t\t}\n\n\t\tif (rrs.getSqlType()==ServerParse.INSERT && rrs.getTables()!=null && rrs.getTables().size()!=0) {\n\t\t\tList<String> tables = rrs.getTables();\n\t\t\tboolean isAutoIncrement = false;\n\t\t\tfor (String tableName: tables) {\n\t\t\t\tif (schema.getTables()!=null && schema.getTables().get(tableName)!=null) {\n\t\t\t\t\tTableConfig tableConfig = schema.getTables().get(tableName);\n\t\t\t\t\tif (tableConfig.isAutoIncrement()) {\n\t\t\t\t\t\tisAutoIncrement = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\trrs.setAutoIncrement(isAutoIncrement);\n\t\t}\n\n\t\treturn rrs;\n\t}\n\n\t/**\n\t * 路由之前必要的处理\n\t * 主要是全局序列号插入，还有子表插入\n\t */\n\tprivate boolean beforeRouteProcess(SchemaConfig schema, int sqlType, String origSQL, ServerConnection sc)\n\t\t\tthrows SQLNonTransientException {\n\t\t\n\t\treturn RouterUtil.processWithMycatSeq(schema, sqlType, origSQL, sc)\n\t\t\t\t|| (sqlType == ServerParse.INSERT && RouterUtil.processERChildTable(schema, origSQL, sc))\n\t\t\t\t|| (sqlType == ServerParse.INSERT && RouterUtil.processInsert(schema, sqlType, origSQL, sc));\n\t}\n\n\t/**\n\t * 通过解析AST语法树类来寻找路由\n\t */\n\tpublic abstract RouteResultset routeNormalSqlWithAST(SchemaConfig schema, String stmt, RouteResultset rrs,\n\t\t\tString charset, LayerCachePool cachePool,int sqlType,ServerConnection sc) throws SQLNonTransientException;\n\n\t/**\n\t * 路由信息指令, 如 SHOW、SELECT@@、DESCRIBE\n\t */\n\tpublic abstract RouteResultset routeSystemInfo(SchemaConfig schema, int sqlType, String stmt, RouteResultset rrs)\n\t\t\tthrows SQLSyntaxErrorException;\n\n\t/**\n\t * 解析 Show 之类的语句\n\t */\n\tpublic abstract RouteResultset analyseShowSQL(SchemaConfig schema, RouteResultset rrs, String stmt)\n\t\t\tthrows SQLNonTransientException;\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/impl/DruidMycatRouteStrategy.java",
    "content": "package io.mycat.route.impl;\n\nimport java.sql.SQLNonTransientException;\nimport java.sql.SQLSyntaxErrorException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.sql.SQLUtils;\nimport com.alibaba.druid.sql.ast.SQLObject;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLAllExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLExistsExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLQueryExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLExprTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLInsertStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLReplaceStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQuery;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\nimport com.alibaba.druid.stat.TableStat.Relationship;\nimport com.google.common.base.Strings;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.mysql.nio.handler.MiddlerQueryResultHandler;\nimport io.mycat.backend.mysql.nio.handler.MiddlerResultHandler;\nimport io.mycat.backend.mysql.nio.handler.SecondHandler;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.config.model.rule.RuleConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.route.function.SlotFunction;\nimport io.mycat.route.impl.middlerResultStrategy.BinaryOpResultHandler;\nimport io.mycat.route.impl.middlerResultStrategy.InSubQueryResultHandler;\nimport io.mycat.route.impl.middlerResultStrategy.RouteMiddlerReaultHandler;\nimport io.mycat.route.impl.middlerResultStrategy.SQLAllResultHandler;\nimport io.mycat.route.impl.middlerResultStrategy.SQLExistsResultHandler;\nimport io.mycat.route.impl.middlerResultStrategy.SQLQueryResultHandler;\nimport io.mycat.route.parser.druid.DruidParser;\nimport io.mycat.route.parser.druid.DruidParserFactory;\nimport io.mycat.route.parser.druid.DruidShardingParseInfo;\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\nimport io.mycat.route.parser.druid.MycatStatementParser;\nimport io.mycat.route.parser.druid.RouteCalculateUnit;\nimport io.mycat.route.parser.util.ParseUtil;\nimport io.mycat.route.util.RouterUtil;\nimport io.mycat.server.NonBlockingSession;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.util.SchemaUtil;\n\npublic class DruidMycatRouteStrategy extends AbstractRouteStrategy {\n\n\tpublic static final Logger LOGGER = LoggerFactory.getLogger(DruidMycatRouteStrategy.class);\n\n\tprivate static Map<Class<?>,RouteMiddlerReaultHandler> middlerResultHandler = new HashMap<>();\n\n\tstatic{\n\t\tmiddlerResultHandler.put(SQLQueryExpr.class, new SQLQueryResultHandler());\n\t\tmiddlerResultHandler.put(SQLBinaryOpExpr.class, new BinaryOpResultHandler());\n\t\tmiddlerResultHandler.put(SQLInSubQueryExpr.class, new InSubQueryResultHandler());\n\t\tmiddlerResultHandler.put(SQLExistsExpr.class, new SQLExistsResultHandler());\n\t\tmiddlerResultHandler.put(SQLAllExpr.class, new SQLAllResultHandler());\n\t}\n\n\t@Override\n\tpublic RouteResultset routeNormalSqlWithAST(SchemaConfig schema, String stmt, RouteResultset rrs, String charset,\n\t\t\tLayerCachePool cachePool, int sqlType, ServerConnection sc) throws SQLNonTransientException {\n\t\t// 如果是批量的update,delete走特别的流程\n\t\tif (sc != null && sc.isAllowMultiStatements()\n\t\t\t\t&& (sqlType == ServerParse.DELETE || sqlType == ServerParse.UPDATE)) {\n\t\t\treturn routeMultiSqlWithAST(schema, stmt, rrs, charset, cachePool, sqlType, sc);\n\n\t\t} else {\n\t\t\treturn routeNormalSqlWithAST0(schema, stmt, rrs, charset, cachePool, sqlType, sc);\n\t\t}\n\t}\n\n\tprivate RouteResultset routeNormalSqlWithAST0(SchemaConfig schema, String stmt, RouteResultset rrs, String charset,\n\t\t\tLayerCachePool cachePool, int sqlType, ServerConnection sc) throws SQLNonTransientException {\n\n\t\t/**\n\t\t *  只有mysql时只支持mysql语法\n\t\t */\n\t\tSQLStatementParser parser = null;\n\t\tif (schema.isNeedSupportMultiDBType()) {\n\t\t\tparser = new MycatStatementParser(stmt);\n\t\t} else {\n\t\t\tparser = new MySqlStatementParser(stmt);\n\t\t}\n\n\t\tMycatSchemaStatVisitor visitor = null;\n\t\tSQLStatement statement;\n\n\t\t/**\n\t\t * 解析出现问题统一抛SQL语法错误\n\t\t */\n\t\ttry {\n            if (parser instanceof MycatStatementParser || sqlType == ServerParse.LOCK) {\n                /**\n                 * 说明： 1)非mysql数据库因为是jdbc驱动支持多语句，所以无需判断是否为多语句；\n                 * 2)lock类型语句，因为druid自身问题且升级后mycat代码大量编译报错，只能沿用当前逻辑，不判断是否为多语句\n                 */\n                statement = parser.parseStatement();\n            } else {\n                // 因不支持多语句，添加判断是否为多语句\n                List<SQLStatement> statementList = new ArrayList<SQLStatement>();\n\n                /** 最多就解析2条，用于判断是否为批量 **/\n                parser.parseStatementList(statementList, 2);\n                if (statementList.size() > 1) {\n                    throw new SQLSyntaxErrorException(\n                            \"Multi statements is not supported,use single statement instead \");\n                } else {\n                    statement = statementList.get(0);\n                }\n            }\n\n\t\t\tvisitor = new MycatSchemaStatVisitor();\n\t\t} catch (Exception t) {\n\t\t\tLOGGER.error(\"DruidMycatRouteStrategyError\", t);\n\t\t\tthrow new SQLSyntaxErrorException(t);\n\t\t}\n\n\t\t/**\n\t\t * 检验unsupported statement\n\t\t */\n\t\tcheckUnSupportedStatement(statement);\n\n\t\tDruidParser druidParser = DruidParserFactory.create(schema, statement, visitor);\n\t\tdruidParser.parser(schema, rrs, statement, stmt,cachePool,visitor);\n\t\tDruidShardingParseInfo ctx=  druidParser.getCtx() ;\n\t\trrs.setTables(ctx.getTables());\n\n\t\tif(visitor.isSubqueryRelationOr()){\n\t\t\tString err = \"In subQuery,the or condition is not supported.\";\n\t\t\tLOGGER.error(err);\n\t\t\tthrow new SQLSyntaxErrorException(err);\n\t\t}\n\n\t\t/* 按照以下情况路由\n\t\t\t1.2.1 可以直接路由.\n       \t\t1.2.2 两个表夸库join的sql.调用calat\n       \t\t1.2.3 需要先执行subquery 的sql.把subquery拆分出来.获取结果后,与outerquery\n\t\t */\n\n\t\t//add huangyiming 分片规则不一样的且表中带查询条件的则走Catlet\n\t\tList<String> tables = ctx.getTables();\n\t\tSchemaConfig schemaConf = MycatServer.getInstance().getConfig().getSchemas().get(schema.getName());\n\t\tint index = 0;\n\t\tRuleConfig firstRule = null;\n\t\tboolean directRoute = true;\n\t\tSet<String> firstDataNodes = new HashSet<String>();\n\t\tMap<String, TableConfig> tconfigs = schemaConf==null?null:schemaConf.getTables();\n\n\t\tMap<String,RuleConfig> rulemap = new HashMap<>();\n\t\tif(tconfigs!=null){\n\t\t\tfor(String tableName : tables){\n\t\t\t\tTableConfig tc =  tconfigs.get(tableName);\n\t\t\t\tif(tc == null){\n\t\t\t\t\t//add 别名中取\n\t\t\t\t\tMap<String, String> tableAliasMap = ctx.getTableAliasMap();\n\t\t\t\t\tif(tableAliasMap !=null && tableAliasMap.get(tableName) !=null){\n\t\t\t\t\t\ttc = schemaConf.getTables().get(tableAliasMap.get(tableName));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif(index == 0){\n\t\t\t\t\tif(tc !=null){\n\t\t\t\t\t\tfirstRule=  tc.getRule();\n\t\t\t\t\t\t//没有指定分片规则时,不做处理\n\t\t\t\t\t\tif(firstRule==null){\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfirstDataNodes.addAll(tc.getDataNodes());\n\t\t\t\t\t\trulemap.put(tc.getName(), firstRule);\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\tif(tc !=null){\n\t\t\t\t\t\t//ER关系表的时候是可能存在字表中没有tablerule的情况,所以加上判断\n\t\t\t\t\t\tRuleConfig ruleCfg = tc.getRule();\n\t\t\t\t\t\tif(ruleCfg==null){  //没有指定分片规则时,不做处理\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tSet<String> dataNodes = new HashSet<>(tc.getDataNodes());\n\t\t\t\t\t\trulemap.put(tc.getName(), ruleCfg);\n\t\t\t\t\t\t//如果匹配规则不相同或者分片的datanode不相同则需要走子查询处理\n\t\t\t\t\t\tif(firstRule!=null&&((ruleCfg !=null && !ruleCfg.getRuleAlgorithm().equals(firstRule.getRuleAlgorithm()) )||( !dataNodes.equals(firstDataNodes)))){\n\t\t\t\t\t\t\tdirectRoute = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tindex++;\n\t\t\t}\n\t\t}\n\n\t\tRouteResultset rrsResult = rrs;\n\t\tif(directRoute){ //直接路由\n\t\t\tif(!RouterUtil.isAllGlobalTable(ctx, schemaConf)){\n\t\t\t\tif(rulemap.size()>1&&!checkRuleField(rulemap,visitor)){\n\t\t\t\t\tString err = \"In case of slice table,there is no rule field in the relationship condition!\";\n\t\t\t\t\tLOGGER.error(err);\n\t\t\t\t\tthrow new SQLSyntaxErrorException(err);\n\t\t\t\t}\n\t\t\t}\n\t\t\trrsResult = directRoute(rrs,ctx,schema,druidParser,statement,cachePool);\n\t\t}else{\n\t\t\tint subQuerySize = visitor.getSubQuerys().size();\n\t\t\tif(subQuerySize==0&&ctx.getTables().size()==2){ //两表关联,考虑使用catlet\n\t\t\t\tif(!visitor.getRelationships().isEmpty()){\n\t\t\t\t\trrs.setCacheAble(false);\n\t\t\t\t\trrs.setFinishedRoute(true);\n\t\t\t\t\trrsResult = catletRoute(schema,ctx.getSql(),charset,sc);\n\t\t\t\t}else{\n\t\t\t\t\trrsResult = directRoute(rrs,ctx,schema,druidParser,statement,cachePool);\n\t\t\t\t}\n\t\t\t}else if(subQuerySize==1){     //只涉及一张表的子查询,使用  MiddlerResultHandler 获取中间结果后,改写原有 sql 继续执行 TODO 后期可能会考虑多个子查询的情况.\n\t\t\t\tSQLSelect sqlselect = visitor.getSubQuerys().iterator().next();\n\t\t\t\tif(!visitor.getRelationships().isEmpty()){     // 当 inner query  和 outer  query  有关联条件时,暂不支持\n\t\t\t\t\tString err = \"In case of slice table,sql have different rules,the relationship condition is not supported.\";\n\t\t\t\t\tLOGGER.error(err);\n\t\t\t\t\tthrow new SQLSyntaxErrorException(err);\n\t\t\t\t}else{\n\t\t\t\t\tSQLSelectQuery sqlSelectQuery = sqlselect.getQuery();\n\t\t\t\t\tif(((MySqlSelectQueryBlock)sqlSelectQuery).getFrom() instanceof SQLExprTableSource) {\n\t\t\t\t\t\trrs.setCacheAble(false);\n\t\t\t\t\t\trrs.setFinishedRoute(true);\n\t\t\t\t\t\trrsResult = middlerResultRoute(schema,charset,sqlselect,sqlType,statement,sc);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}else if(subQuerySize >=2){\n\t\t\t\tString err = \"In case of slice table,sql has different rules,currently only one subQuery is supported.\";\n\t\t\t\tLOGGER.error(err);\n\t\t\t\tthrow new SQLSyntaxErrorException(err);\n\t\t\t}\n\t\t}\n\t\treturn rrsResult;\n\t}\n\n\t// 批量update,delete路由方法\n\tprivate RouteResultset routeMultiSqlWithAST(SchemaConfig schema, String stmt, RouteResultset rrs, String charset,\n\t\t\tLayerCachePool cachePool, int sqlType, ServerConnection sc) throws SQLNonTransientException {\n\t\tList<RouteResultsetNode> allNodes = new ArrayList<>(64);\n\t\t// 拆分出一个个SQL解析路由\n\t\tString remingSql = stmt;\n\t\tString eachSqlItem = null;\n\t\tdo {\n\t\t\tint index = ParseUtil.findNextBreak(remingSql);\n\t\t\tif (index + 1 < remingSql.length() && !ParseUtil.isEOF(remingSql, index)) {\n\t\t\t\teachSqlItem = remingSql.substring(0, index);\n\t\t\t\tremingSql = remingSql.substring(index + 1, remingSql.length());\n\t\t\t\tRouteResultset rrsTemp = new RouteResultset(eachSqlItem, sqlType);\n\t\t\t\tRouteResultset tempRrs = routeNormalSqlWithAST0(schema, eachSqlItem, rrsTemp, charset, cachePool,\n\t\t\t\t\t\tsqlType, sc);\n\t\t\t\tallNodes.addAll(Arrays.asList(tempRrs.getNodes()));\n\t\t\t} else {\n\t\t\t\t// the last one\n\t\t\t\tRouteResultset rrsTemp = new RouteResultset(remingSql, sqlType);\n\t\t\t\tRouteResultset tempRrs = routeNormalSqlWithAST0(schema, remingSql, rrsTemp, charset, cachePool, sqlType,\n\t\t\t\t\t\tsc);\n\t\t\t\tallNodes.addAll(Arrays.asList(tempRrs.getNodes()));\n\t\t\t\trrs = rrsTemp;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} while (true);\n\n\t\tif (allNodes.size() >= 1) {\n\t\t\t// merge\n\t\t\trrs.setStatement(stmt);\n\t\t\trrs.setNodes(allNodes.toArray(new RouteResultsetNode[0]));\n\t\t\trrs.mergeSameNode();\n\t\t\treturn rrs;\n\t\t} else {\n\t\t\tthrow new SQLNonTransientException(\"mycat parse error on sql:\" + stmt);\n\t\t}\n\t}\n\t/**\n\t * 子查询中存在关联查询的情况下,检查关联字段是否是分片字段\n\t * @param rulemap\n\t * @param ships\n\t * @return\n\t */\n\tprivate boolean checkRuleField(Map<String,RuleConfig> rulemap,MycatSchemaStatVisitor visitor){\n\n\t\tif(!MycatServer.getInstance().getConfig().getSystem().isSubqueryRelationshipCheck()){\n\t\t\treturn true;\n\t\t}\n\n\t\tSet<Relationship> ships = visitor.getRelationships();\n\t\tIterator<Relationship> iter = ships.iterator();\n\t\twhile(iter.hasNext()){\n\t\t\tRelationship ship = iter.next();\n\t\t\tString lefttable = ship.getLeft().getTable().toUpperCase();\n\t\t\tString righttable = ship.getRight().getTable().toUpperCase();\n\t\t\t// 如果是同一个表中的关联条件,不做处理\n\t\t\tif(lefttable.equals(righttable)){\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tRuleConfig leftconfig = rulemap.get(lefttable);\n\t\t\tRuleConfig rightconfig = rulemap.get(righttable);\n\n\t\t\tif(null!=leftconfig&&null!=rightconfig\n\t\t\t\t\t&&leftconfig.equals(rightconfig)\n\t\t\t\t\t&&leftconfig.getColumn().equals(ship.getLeft().getName().toUpperCase())\n\t\t\t\t\t&&rightconfig.getColumn().equals(ship.getRight().getName().toUpperCase())){\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate RouteResultset middlerResultRoute(final SchemaConfig schema,final String charset,final SQLSelect sqlselect,\n\t\t\tfinal int sqlType,final SQLStatement statement,final ServerConnection sc){\n\n\t\tfinal String middlesql = SQLUtils.toMySqlString(sqlselect);\n\n\t\tMiddlerResultHandler<String> middlerResultHandler =  new MiddlerQueryResultHandler<>(new SecondHandler() {\n\t\t\t@Override\n\t\t\tpublic void doExecute(List param) {\n\t\t\t\tsc.getSession2().setMiddlerResultHandler(null);\n\t\t\t\tString sqls = null;\n\t\t\t\t// 路由计算\n\t\t\t\tRouteResultset rrs = null;\n\t\t\t\ttry {\n\n\t\t\t\t\tsqls = buildSql(statement,sqlselect,param);\n\t\t\t\t\trrs = MycatServer\n\t\t\t\t\t\t\t.getInstance()\n\t\t\t\t\t\t\t.getRouterservice()\n\t\t\t\t\t\t\t.route(MycatServer.getInstance().getConfig().getSystem(),\n\t\t\t\t\t\t\t\t\tschema, sqlType,sqls.toLowerCase(), charset,sc );\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tStringBuilder s = new StringBuilder();\n\t\t\t\t\tLOGGER.warn(s.append(this).append(sqls).toString() + \" err:\" + e.toString(),e);\n\t\t\t\t\tString msg = e.getMessage();\n\t\t\t\t\tsc.writeErrMessage(ErrorCode.ER_PARSE_ERROR, msg == null ? e.getClass().getSimpleName() : msg);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tNonBlockingSession noBlockSession =  new NonBlockingSession(sc.getSession2().getSource());\n\t\t\t\tnoBlockSession.setMiddlerResultHandler(null);\n\t\t\t\t//session的预编译标示传递\n\t\t\t\tnoBlockSession.setPrepared(sc.getSession2().isPrepared());\n\t\t\t\tif (rrs != null) {\n\t\t\t\t\tnoBlockSession.setCanClose(false);\n\t\t\t\t\tnoBlockSession.execute(rrs, ServerParse.SELECT);\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t\tsc.getSession2().setMiddlerResultHandler(middlerResultHandler);\n\t\tsc.getSession2().setCanClose(false);\n\n\t\t// 路由计算\n\t\tRouteResultset rrs = null;\n\t\ttry {\n\t\t\trrs = MycatServer\n\t\t\t\t\t.getInstance()\n\t\t\t\t\t.getRouterservice()\n\t\t\t\t\t.route(MycatServer.getInstance().getConfig().getSystem(),\n\t\t\t\t\t\t\tschema, ServerParse.SELECT, middlesql, charset, sc);\n\n\t\t} catch (Exception e) {\n\t\t\tStringBuilder s = new StringBuilder();\n\t\t\tLOGGER.warn(s.append(this).append(middlesql).toString() + \" err:\" + e.toString(),e);\n\t\t\tString msg = e.getMessage();\n\t\t\tsc.writeErrMessage(ErrorCode.ER_PARSE_ERROR, msg == null ? e.getClass().getSimpleName() : msg);\n\t\t\treturn null;\n\t\t}\n\n\t\tif(rrs!=null){\n\t\t\trrs.setCacheAble(false);\n\t\t}\n\t\treturn rrs;\n\t}\n\n\t/**\n\t * 获取子查询执行结果后,改写原始sql 继续执行.\n\t * @param statement\n\t * @param sqlselect\n\t * @param param\n\t * @return\n\t */\n\tprivate String buildSql(SQLStatement statement,SQLSelect sqlselect,List param){\n\n\t\tSQLObject parent = sqlselect.getParent();\n\t\tRouteMiddlerReaultHandler handler = middlerResultHandler.get(parent.getClass());\n\t\tif(handler==null){\n\t\t\tthrow new UnsupportedOperationException(parent.getClass()+\" current is not supported \");\n\t\t}\n\t\treturn handler.dohandler(statement, sqlselect, parent, param);\n\t}\n\n\t/**\n\t * 两个表的情况，catlet\n\t * @param schema\n\t * @param stmt\n\t * @param charset\n\t * @param sc\n\t * @return\n\t */\n\tprivate RouteResultset catletRoute(SchemaConfig schema,String stmt,String charset,ServerConnection sc){\n\t\tRouteResultset rrs = null;\n\t\ttry {\n\t\t\trrs = MycatServer\n\t\t\t\t\t.getInstance()\n\t\t\t\t\t.getRouterservice()\n\t\t\t\t\t.route(MycatServer.getInstance().getConfig().getSystem(),\n\t\t\t\t\t\t\tschema, ServerParse.SELECT, \"/*!mycat:catlet=io.mycat.catlets.ShareJoin */ \"+stmt, charset, sc);\n\n\t\t}catch(Exception e){\n\n\t\t}\n\t\treturn rrs;\n\t}\n\n\t/**\n\t *  直接结果路由\n\t * @param rrs\n\t * @param ctx\n\t * @param schema\n\t * @param druidParser\n\t * @param statement\n\t * @param cachePool\n\t * @return\n\t * @throws SQLNonTransientException\n\t */\n\tprivate RouteResultset directRoute(RouteResultset rrs,DruidShardingParseInfo ctx,SchemaConfig schema,\n\t\t\tDruidParser druidParser,SQLStatement statement,LayerCachePool cachePool) throws SQLNonTransientException{\n\n\t\t//改写sql：如insert语句主键自增长, 在直接结果路由的情况下,进行sql 改写处理\n\t\tdruidParser.changeSql(schema, rrs, statement,cachePool);\n\n\t\t/**\n\t\t * DruidParser 解析过程中已完成了路由的直接返回\n\t\t */\n\t\tif ( rrs.isFinishedRoute() ) {\n\t\t\treturn rrs;\n\t\t}\n\n\t\t/**\n\t\t * 没有from的select语句或其他\n\t\t */\n\t\tif((ctx.getTables() == null || ctx.getTables().size() == 0)&&(ctx.getTableAliasMap()==null||ctx.getTableAliasMap().isEmpty()))\n\t\t{\n\t\t\treturn RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), druidParser.getCtx().getSql());\n\t\t}\n\n\t\tif(druidParser.getCtx().getRouteCalculateUnits().size() == 0) {\n\t\t\tRouteCalculateUnit routeCalculateUnit = new RouteCalculateUnit();\n\t\t\tdruidParser.getCtx().addRouteCalculateUnit(routeCalculateUnit);\n\t\t}\n\n\t\tSortedSet<RouteResultsetNode> nodeSet = new TreeSet<RouteResultsetNode>();\n\t\tboolean isAllGlobalTable = RouterUtil.isAllGlobalTable(ctx, schema);\n\t\tfor(RouteCalculateUnit unit: druidParser.getCtx().getRouteCalculateUnits()) {\n\t\t\tRouteResultset rrsTmp = RouterUtil.tryRouteForTables(schema, druidParser.getCtx(), unit, rrs, isSelect(statement), cachePool);\n\t\t\tif(rrsTmp != null&&rrsTmp.getNodes()!=null) {\n\t\t\t\tfor(RouteResultsetNode node :rrsTmp.getNodes()) {\n\t\t\t\t\tnodeSet.add(node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(isAllGlobalTable) {//都是全局表时只计算一遍路由\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tRouteResultsetNode[] nodes = new RouteResultsetNode[nodeSet.size()];\n\t\tint i = 0;\n\t\tfor (RouteResultsetNode aNodeSet : nodeSet) {\n\t\t\tnodes[i] = aNodeSet;\n\t\t\tif(statement instanceof MySqlInsertStatement &&ctx.getTables().size()==1&&schema.getTables().containsKey(ctx.getTables().get(0))) {\n\t\t\t\tRuleConfig rule = schema.getTables().get(ctx.getTables().get(0)).getRule();\n\t\t\t\tif(rule!=null&&  rule.getRuleAlgorithm() instanceof SlotFunction){\n\t\t\t\t\taNodeSet.setStatement(ParseUtil.changeInsertAddSlot(aNodeSet.getStatement(),aNodeSet.getSlot()));\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t}\n\t\trrs.setNodes(nodes);\n\n\t\t//分表\n\t\t/**\n\t\t *  subTables=\"t_order$1-2,t_order3\"\n\t\t *目前分表 1.6 开始支持 幵丏 dataNode 在分表条件下只能配置一个，分表条件下不支持join。\n\t\t */\n\t\tif(rrs.isDistTable()){\n\t\t\treturn this.routeDisTable(statement,rrs);\n\t\t}\n\t\treturn rrs;\n\t}\n\n\tprivate SQLExprTableSource getDisTable(SQLTableSource tableSource,RouteResultsetNode node) throws SQLSyntaxErrorException{\n\t\tif(node.getSubTableName()==null){\n\t\t\tString msg = \" sub table not exists for \" + node.getName() + \" on \" + tableSource;\n\t\t\tLOGGER.error(\"DruidMycatRouteStrategyError \" + msg);\n\t\t\tthrow new SQLSyntaxErrorException(msg);\n\t\t}\n\n\t\tSQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr();\n\t\tsqlIdentifierExpr.setParent(tableSource.getParent());\n\t\tsqlIdentifierExpr.setName(node.getSubTableName());\n\t\tSQLExprTableSource from2 = new SQLExprTableSource(sqlIdentifierExpr);\n\t\treturn from2;\n\t}\n\n\tprivate RouteResultset routeDisTable(SQLStatement statement, RouteResultset rrs) throws SQLSyntaxErrorException{\n\t\tSQLTableSource tableSource = null;\n\t\tif(statement instanceof SQLInsertStatement) {\n\t\t\tSQLInsertStatement insertStatement = (SQLInsertStatement) statement;\n\t\t\ttableSource = insertStatement.getTableSource();\n\t\t\tfor (RouteResultsetNode node : rrs.getNodes()) {\n\t\t\t\tSQLExprTableSource from2 = getDisTable(tableSource, node);\n\t\t\t\tinsertStatement.setTableSource(from2);\n\t\t\t\tnode.setStatement(insertStatement.toString());\n\t\t\t}\n\t\t}\n\t\tif(statement instanceof SQLDeleteStatement) {\n\t\t\tSQLDeleteStatement deleteStatement = (SQLDeleteStatement) statement;\n\t\t\ttableSource = deleteStatement.getTableSource();\n\t\t\tSQLTableSource from = deleteStatement.getFrom();\n\t\t\tfor (RouteResultsetNode node : rrs.getNodes()) {\n\t\t\t\tSQLExprTableSource from2 = getDisTable(tableSource, node);\n\n\t\t\t\tif (from == null) {\n\t\t\t\t\tfrom2.setAlias(tableSource.toString());\n\t\t\t\t\tdeleteStatement.setFrom(from2);\n\t\t\t\t} else {\n\t\t\t\t\tString alias = from.getAlias();\n\t\t\t\t\tfrom2.setAlias(alias);\n\t\t\t\t\tdeleteStatement.setFrom(from2);\n\t\t\t\t}\n\n\t\t\t\tnode.setStatement(deleteStatement.toString());\n\t\t\t}\n\t\t}\n\t\tif(statement instanceof SQLUpdateStatement) {\n\t\t\tSQLUpdateStatement updateStatement = (SQLUpdateStatement) statement;\n\t\t\ttableSource = updateStatement.getTableSource();\n\n\t\t\tString alias = tableSource.getAlias();\n\t\t\tSQLExprTableSource exprSource = (SQLExprTableSource) tableSource;\n\t\t\tSQLIdentifierExpr expr = (SQLIdentifierExpr) exprSource.getExpr();\n\t\t\talias = alias == null ? expr.getName() : alias;\n\n\t\t\tfor (RouteResultsetNode node : rrs.getNodes()) {\n\t\t\t\tSQLExprTableSource from2 = getDisTable(tableSource, node);\n\n\t\t\t\t//修复\n\t\t\t\t// EXPLAIN UPDATE travelrecord SET user_id = 'Fred' WHERE id IN 1; => UPDATE travelrecord travelrecord SET user_id = 'Fred' WHERE id IN (1)\n\t\t\t\tif (!from2.toString().equals(alias)){\n\t\t\t\t\tfrom2.setAlias(alias);\n\t\t\t\t}\n\t\t\t\tupdateStatement.setTableSource(from2);\n\t\t\t\tnode.setStatement(updateStatement.toString());\n\t\t\t}\n\t\t}\n\n\t\treturn rrs;\n\t}\n\n\t/**\n\t * SELECT 语句\n\t */\n\tprivate boolean isSelect(SQLStatement statement) {\n\t\tif(statement instanceof SQLSelectStatement) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 检验不支持的SQLStatement类型 ：不支持的类型直接抛SQLSyntaxErrorException异常\n\t * @param statement\n\t * @throws SQLSyntaxErrorException\n\t */\n\tprivate void checkUnSupportedStatement(SQLStatement statement) throws SQLSyntaxErrorException {\n\t\t//不支持replace语句\n\t\tif (statement instanceof SQLReplaceStatement) {\n\t\t\tthrow new SQLSyntaxErrorException(\" ReplaceStatement can't be supported,use insert into ...on duplicate key update... instead \");\n\t\t}\n\t}\n\n\t/**\n\t * 分析 SHOW SQL\n\t */\n\t@Override\n\tpublic RouteResultset analyseShowSQL(SchemaConfig schema,\n\t\t\tRouteResultset rrs, String stmt) throws SQLSyntaxErrorException {\n\n\t\tString[] fields = SchemaUtil.parseShowTable(stmt);\n        if (\"1\".equals(fields[0])) {// show tables\n\t\t\tString relSchema = fields[3];\n\t\t\tString tableName = fields[8];\n            //\n            if (relSchema != null && !relSchema.equalsIgnoreCase(schema.getName())) {\n                schema = MycatServer.getInstance().getConfig().getSchemas().get(relSchema);\n                if (schema == null) {\n                    throw new SQLSyntaxErrorException(\"not found schema : \" + relSchema);\n                }\n            }\n\t\t\t// 2020/03/19 Ken.Li\n\t\t\t// if (StringUtils.isNotBlank(tableName) && tableName.indexOf(\"%\") < 0) {\n\t\t\tif (StringUtils.isNotBlank(tableName) && tableName.indexOf(\"%\") < 0 && !schema.getTables().isEmpty()) {\n                TableConfig tableConfig = schema.getTables().get(tableName.toUpperCase());\n                if (tableConfig != null) {\n                    String dataNode = RouterUtil.getAliveRandomDataNode(tableConfig);\n                    PhysicalDBNode dataNodeObj = MycatServer.getInstance().getConfig().getDataNodes().get(dataNode);\n                    if (StringUtils.isNotBlank(dataNodeObj.getDatabase())) {\n                        stmt = stmt.replaceAll(relSchema, dataNodeObj.getDatabase());\n                    }\n                    return RouterUtil.routeToSingleNode(rrs, dataNode, stmt);\n                }\n            }\n            // remove db\n            if (StringUtils.isNotBlank(relSchema)) {\n\t\t\t\tif(schema.getDataNode() !=null) {\n\t\t\t\t\t// 2020/03/19 Ken.Li\n\t\t\t\t\t// stmt = stmt.replaceAll(relSchema, schema.getDataNode());\n\t\t\t\t\tPhysicalDBNode dataNode = MycatServer.getInstance().getConfig().getDataNodes().get(schema.getDataNode());\n\t\t\t\t\tstmt = stmt.replaceAll(relSchema, dataNode.getDatabase());\n\t\t\t\t} else {\n\t\t\t\t\tif(fields[2] !=null ) {\n\t\t\t\t\t\tstmt = stmt.replaceAll( fields[2] + \"\\\\s*\" + relSchema, \"\");\n\t\t\t\t\t}\n\t\t\t\t}\n            }\n            //\n            String defaultNode = schema.getDataNode();\n            if (!Strings.isNullOrEmpty(defaultNode)) {\n                return RouterUtil.routeToSingleNode(rrs, defaultNode, stmt);\n            }\n\n            return RouterUtil.routeToMultiNode(false, rrs, schema.getMetaDataNodes(), stmt);\n\t\t}\n\t\t\n\t\tString upStmt = stmt.toUpperCase();\n\t\t/**\n\t\t *  show index or column\n\t\t */\n\t\tint[] indx = RouterUtil.getSpecPos(upStmt, 0);\n\t\tif (indx[0] > 0) {\n\t\t\t/**\n\t\t\t *  has table\n\t\t\t */\n\t\t\tint[] repPos = { indx[0] + indx[1], 0 };\n\t\t\tString tableName = RouterUtil.getShowTableName(schema,stmt, repPos);\n\t\t\t/**\n\t\t\t *  IN DB pattern\n\t\t\t */\n\t\t\tint[] indx2 = RouterUtil.getSpecPos(upStmt, indx[0] + indx[1] + 1);\n\t\t\tif (indx2[0] > 0) {// find LIKE OR WHERE\n\t\t\t\trepPos[1] = RouterUtil.getSpecEndPos(upStmt, indx2[0] + indx2[1]);\n\n\t\t\t}\n\t\t\tstmt = stmt.substring(0, indx[0]) + \" FROM \" + tableName + stmt.substring(repPos[1]);\n\t\t\tRouterUtil.routeForTableMeta(rrs, schema, tableName, stmt);\n\t\t\treturn rrs;\n\n\t\t}\n\n\t\t/**\n\t\t *  show create table tableName\n\t\t */\n\t\tint[] createTabInd = RouterUtil.getCreateTablePos(upStmt, 0);\n\t\tif (createTabInd[0] > 0) {\n\t\t\tint tableNameIndex = createTabInd[0] + createTabInd[1];\n\t\t\tif (upStmt.length() > tableNameIndex) {\n\t\t\t\tString tableName = stmt.substring(tableNameIndex).trim();\n\t\t\t\tint ind2 = tableName.indexOf('.');\n\t\t\t\tif (ind2 > 0) {\n\t\t\t\t\ttableName = tableName.substring(ind2 + 1);\n\t\t\t\t}\n\t\t\t\tRouterUtil.routeForTableMeta(rrs, schema, tableName, stmt);\n\t\t\t\treturn rrs;\n\t\t\t}\n\t\t}\n\n\t\treturn RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), stmt);\n\t}\n\n\n//\t/**\n//\t * 为一个表进行条件路由\n//\t * @param schema\n//\t * @param tablesAndConditions\n//\t * @param tablesRouteMap\n//\t * @throws SQLNonTransientException\n//\t */\n//\tprivate static RouteResultset findRouteWithcConditionsForOneTable(SchemaConfig schema, RouteResultset rrs,\n//\t\tMap<String, Set<ColumnRoutePair>> conditions, String tableName, String sql) throws SQLNonTransientException {\n//\t\tboolean cache = rrs.isCacheAble();\n//\t    //为分库表找路由\n//\t\ttableName = tableName.toUpperCase();\n//\t\tTableConfig tableConfig = schema.getTables().get(tableName);\n//\t\t//全局表或者不分库的表略过（全局表后面再计算）\n//\t\tif(tableConfig.isGlobalTable()) {\n//\t\t\treturn null;\n//\t\t} else {//非全局表\n//\t\t\tSet<String> routeSet = new HashSet<String>();\n//\t\t\tString joinKey = tableConfig.getJoinKey();\n//\t\t\tfor(Map.Entry<String, Set<ColumnRoutePair>> condition : conditions.entrySet()) {\n//\t\t\t\tString colName = condition.getKey();\n//\t\t\t\t//条件字段是拆分字段\n//\t\t\t\tif(colName.equals(tableConfig.getPartitionColumn())) {\n//\t\t\t\t\tSet<ColumnRoutePair> columnPairs = condition.getValue();\n//\n//\t\t\t\t\tfor(ColumnRoutePair pair : columnPairs) {\n//\t\t\t\t\t\tif(pair.colValue != null) {\n//\t\t\t\t\t\t\tInteger nodeIndex = tableConfig.getRule().getRuleAlgorithm().calculate(pair.colValue);\n//\t\t\t\t\t\t\tif(nodeIndex == null) {\n//\t\t\t\t\t\t\t\tString msg = \"can't find any valid datanode :\" + tableConfig.getName()\n//\t\t\t\t\t\t\t\t\t\t+ \" -> \" + tableConfig.getPartitionColumn() + \" -> \" + pair.colValue;\n//\t\t\t\t\t\t\t\tLOGGER.warn(msg);\n//\t\t\t\t\t\t\t\tthrow new SQLNonTransientException(msg);\n//\t\t\t\t\t\t\t}\n//\t\t\t\t\t\t\tString node = tableConfig.getDataNodes().get(nodeIndex);\n//\t\t\t\t\t\t\tif(node != null) {//找到一个路由节点\n//\t\t\t\t\t\t\t\trouteSet.add(node);\n//\t\t\t\t\t\t\t}\n//\t\t\t\t\t\t}\n//\t\t\t\t\t\tif(pair.rangeValue != null) {\n//\t\t\t\t\t\t\tInteger[] nodeIndexs = tableConfig.getRule().getRuleAlgorithm()\n//\t\t\t\t\t\t\t\t\t.calculateRange(pair.rangeValue.beginValue.toString(), pair.rangeValue.endValue.toString());\n//\t\t\t\t\t\t\tfor(Integer idx : nodeIndexs) {\n//\t\t\t\t\t\t\t\tString node = tableConfig.getDataNodes().get(idx);\n//\t\t\t\t\t\t\t\tif(node != null) {//找到一个路由节点\n//\t\t\t\t\t\t\t\t\trouteSet.add(node);\n//\t\t\t\t\t\t\t\t}\n//\t\t\t\t\t\t\t}\n//\t\t\t\t\t\t}\n//\t\t\t\t\t}\n//\t\t\t\t} else if(joinKey != null && joinKey.equals(colName)) {\n//\t\t\t\t\tSet<String> dataNodeSet = RouterUtil.ruleCalculate(\n//\t\t\t\t\t\t\ttableConfig.getParentTC(), condition.getValue());\n//\t\t\t\t\tif (dataNodeSet.isEmpty()) {\n//\t\t\t\t\t\tthrow new SQLNonTransientException(\n//\t\t\t\t\t\t\t\t\"parent key can't find any valid datanode \");\n//\t\t\t\t\t}\n//\t\t\t\t\tif (LOGGER.isDebugEnabled()) {\n//\t\t\t\t\t\tLOGGER.debug(\"found partion nodes (using parent partion rule directly) for child table to update  \"\n//\t\t\t\t\t\t\t\t+ Arrays.toString(dataNodeSet.toArray()) + \" sql :\" + sql);\n//\t\t\t\t\t}\n//\t\t\t\t\tif (dataNodeSet.size() > 1) {\n//\t\t\t\t\t\treturn RouterUtil.routeToMultiNode(rrs.isCacheAble(), rrs, schema.getAllDataNodes(), sql);\n//\t\t\t\t\t} else {\n//\t\t\t\t\t\trrs.setCacheAble(true);\n//\t\t\t\t\t\treturn RouterUtil.routeToSingleNode(rrs, dataNodeSet.iterator().next(), sql);\n//\t\t\t\t\t}\n//\t\t\t\t} else {//条件字段不是拆分字段也不是join字段,略过\n//\t\t\t\t\tcontinue;\n//\n//\t\t\t\t}\n//\t\t\t}\n//\t\t\treturn RouterUtil.routeToMultiNode(cache, rrs, routeSet, sql);\n//\n//\t\t}\n//\n//\t}\n\n\tpublic RouteResultset routeSystemInfo(SchemaConfig schema, int sqlType,\n\t\t\tString stmt, RouteResultset rrs) throws SQLSyntaxErrorException {\n\t\tswitch(sqlType){\n\t\t\tcase ServerParse.SHOW:// if origSQL is like show tables\n\t\t\t\treturn analyseShowSQL(schema, rrs, stmt);\n\t\t\tcase ServerParse.SELECT://if origSQL is like select @@\n\t\t\t\tint index = stmt.indexOf(\"@@\");\n\t\t\t\tif(index > 0 && \"SELECT\".equals(stmt.substring(0, index).trim().toUpperCase())){\n\t\t\t\t\treturn analyseDoubleAtSgin(schema, rrs, stmt);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ServerParse.DESCRIBE:// if origSQL is meta SQL, such as describe table\n\t\t\t\tint ind = stmt.indexOf(' ');\n\t\t\t\tstmt = stmt.trim();\n\t\t\t\treturn analyseDescrSQL(schema, rrs, stmt, ind + 1);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * 对Desc语句进行分析 返回数据路由集合\n\t * \t *\n\t * @param schema   \t\t\t\t数据库名\n\t * @param rrs    \t\t\t\t数据路由集合\n\t * @param stmt   \t\t\t\t执行语句\n\t * @param ind    \t\t\t\t第一个' '的位置\n\t * @return RouteResultset\t\t(数据路由集合)\n\t * @author mycat\n\t */\n\tprivate static RouteResultset analyseDescrSQL(SchemaConfig schema,\n\t\t\tRouteResultset rrs, String stmt, int ind) {\n\n\t\tfinal String MATCHED_FEATURE = \"DESCRIBE \";\n\t\tfinal String MATCHED2_FEATURE = \"DESC \";\n\t\tint pos = 0;\n\t\twhile (pos < stmt.length()) {\n\t\t\tchar ch = stmt.charAt(pos);\n\t\t\t// 忽略处理注释 /* */ BEN\n\t\t\tif(ch == '/' &&  pos+4 < stmt.length() && stmt.charAt(pos+1) == '*') {\n\t\t\t\tif(stmt.substring(pos+2).indexOf(\"*/\") != -1) {\n\t\t\t\t\tpos += stmt.substring(pos+2).indexOf(\"*/\")+4;\n\t\t\t\t\tcontinue;\n\t\t\t\t} else {\n\t\t\t\t\t// 不应该发生这类情况。\n\t\t\t\t\tthrow new IllegalArgumentException(\"sql 注释 语法错误\");\n\t\t\t\t}\n\t\t\t} else if(ch == 'D'||ch == 'd') {\n\t\t\t\t// 匹配 [describe ]\n\t\t\t\tif(pos+MATCHED_FEATURE.length() < stmt.length() && (stmt.substring(pos).toUpperCase().indexOf(MATCHED_FEATURE) != -1)) {\n\t\t\t\t\tpos = pos + MATCHED_FEATURE.length();\n\t\t\t\t\tbreak;\n\t\t\t\t} else if(pos+MATCHED2_FEATURE.length() < stmt.length() && (stmt.substring(pos).toUpperCase().indexOf(MATCHED2_FEATURE) != -1)) {\n\t\t\t\t\tpos = pos + MATCHED2_FEATURE.length();\n\t\t\t\t\tbreak;\n\t\t\t\t} else {\n\t\t\t\t\tpos++;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// 重置ind坐标。BEN GONG\n\t\tind = pos;\n\t\tint[] repPos = { ind, 0 };\n\t\tString tableName = RouterUtil.getTableName(stmt, repPos);\n\n\t\tstmt = stmt.substring(0, ind) + tableName + stmt.substring(repPos[1]);\n\t\tRouterUtil.routeForTableMeta(rrs, schema, tableName, stmt);\n\t\treturn rrs;\n\t}\n\n\t/**\n\t * 根据执行语句判断数据路由\n\t *\n\t * @param schema     \t\t\t数据库名\n\t * @param rrs\t\t  \t\t \t数据路由集合\n\t * @param stmt\t\t  \t \t\t执行sql\n\t * @return RouteResultset\t\t数据路由集合\n\t * @throws SQLSyntaxErrorException\n\t * @author mycat\n\t */\n\tprivate RouteResultset analyseDoubleAtSgin(SchemaConfig schema,\n\t\t\tRouteResultset rrs, String stmt) throws SQLSyntaxErrorException {\n\t\tString upStmt = stmt.toUpperCase();\n\t\tint atSginInd = upStmt.indexOf(\" @@\");\n\t\tif (atSginInd > 0) {\n\t\t\treturn RouterUtil.routeToMultiNode(false, rrs, schema.getMetaDataNodes(), stmt);\n\t\t}\n\t\treturn RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), stmt);\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/impl/middlerResultStrategy/BinaryOpResultHandler.java",
    "content": "package io.mycat.route.impl.middlerResultStrategy;\r\n\r\nimport java.util.List;\r\n\r\nimport com.alibaba.druid.sql.ast.SQLExprImpl;\r\nimport com.alibaba.druid.sql.ast.SQLObject;\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLInListExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLListExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLNullExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLQueryExpr;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\r\n\r\npublic class BinaryOpResultHandler implements RouteMiddlerReaultHandler {\r\n\r\n\t@Override\r\n\tpublic String dohandler(SQLStatement statement, SQLSelect sqlselect, SQLObject parent, List param) {\r\n\t\t\r\n\t\tSQLBinaryOpExpr pp = (SQLBinaryOpExpr)parent;\r\n\t\tif(pp.getLeft() instanceof SQLQueryExpr){\r\n\t\t\tSQLQueryExpr left = (SQLQueryExpr)pp.getLeft();\r\n\t\t\tif(left.getSubQuery().equals(sqlselect)){\r\n\t\t\t\tSQLExprImpl listExpr = null;\r\n\t\t\t\tif(null==param||param.isEmpty()){\r\n\t\t\t\t\tlistExpr = new SQLNullExpr();\r\n\t\t\t\t}else{\r\n\t\t\t\t\tlistExpr = new SQLListExpr();\r\n\t\t\t\t\tlistExpr.setParent(left.getParent());\r\n\t\t\t\t\t((SQLListExpr)listExpr).getItems().addAll(param);\r\n\t\t\t\t}\r\n\t\t\t\tpp.setLeft(listExpr);\r\n\t\t\t}\r\n\t\t}else if(pp.getRight() instanceof SQLQueryExpr){\r\n\t\t\tSQLQueryExpr right = (SQLQueryExpr)pp.getRight();\r\n\t\t\tif(right.getSubQuery().equals(sqlselect)){\r\n\t\t\t\tSQLExprImpl listExpr = null;\r\n\t\t\t\tif(null==param||param.isEmpty()){\r\n\t\t\t\t\tlistExpr = new SQLNullExpr();\r\n\t\t\t\t}else{\r\n\t\t\t\t\tlistExpr = new SQLListExpr();\r\n\t\t\t\t\tlistExpr.setParent(right.getParent());\r\n\t\t\t\t\t((SQLListExpr)listExpr).getItems().addAll(param);\r\n\t\t\t\t}\r\n\t\t\t\tpp.setRight(listExpr);\r\n\t\t\t\t\r\n\t\t\t}\r\n\t\t}else if(pp.getLeft() instanceof SQLInSubQueryExpr){\r\n\t\t\tSQLInSubQueryExpr left = (SQLInSubQueryExpr)pp.getLeft();\r\n\t\t\tif(left.getSubQuery().equals(sqlselect)){\r\n\t\t\t\tSQLExprImpl inlistExpr = null;\r\n\t\t\t\tif(null==param||param.isEmpty()){\r\n\t\t\t\t\tinlistExpr = new SQLNullExpr();\r\n\t\t\t\t}else{\r\n\t\t\t\t\tinlistExpr = new SQLInListExpr();\r\n\t\t\t\t\t((SQLInListExpr)inlistExpr).setTargetList(param);\r\n\t\t\t\t\t((SQLInListExpr)inlistExpr).setExpr(pp.getRight());\r\n\t\t\t\t\t((SQLInListExpr)inlistExpr).setNot(left.isNot());\r\n\t\t\t\t\t((SQLInListExpr)inlistExpr).setParent(left.getParent());\r\n\t\t\t\t}\r\n\t\t\t\tpp.setLeft(inlistExpr);\r\n\t\t\t}\r\n\t\t}else if(pp.getRight() instanceof SQLInSubQueryExpr){\r\n\t\t\tSQLInSubQueryExpr right = (SQLInSubQueryExpr)pp.getRight();\r\n\t\t\tif(right.getSubQuery().equals(sqlselect)){\r\n\t\t\t\tSQLExprImpl listExpr = null;\r\n\t\t\t\tif(null==param||param.isEmpty()){\r\n\t\t\t\t\tlistExpr = new SQLNullExpr();\r\n\t\t\t\t}else{\r\n\t\t\t\t\tlistExpr = new SQLListExpr();\r\n\t\t\t\t\t((SQLListExpr)listExpr).getItems().addAll(param);\r\n\t\t\t\t}\r\n\t\t\t\tpp.setRight(listExpr);\r\n\t\t\t\t\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn statement.toString();\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/route/impl/middlerResultStrategy/InSubQueryResultHandler.java",
    "content": "package io.mycat.route.impl.middlerResultStrategy;\r\n\r\nimport java.util.List;\r\n\r\nimport com.alibaba.druid.sql.ast.SQLExprImpl;\r\nimport com.alibaba.druid.sql.ast.SQLObject;\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLInListExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLNullExpr;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\r\n\r\n\r\npublic class InSubQueryResultHandler implements RouteMiddlerReaultHandler {\r\n\t\r\n\t@Override\r\n\tpublic String dohandler(SQLStatement statement,SQLSelect sqlselect,SQLObject parent,List param) {\r\n\t\tSQLExprImpl inlistExpr = null;\r\n\t\tif(null==param||param.isEmpty()){\r\n\t\t\tinlistExpr = new SQLNullExpr();\r\n\t\t}else{\r\n\t\t\tinlistExpr = new SQLInListExpr();\r\n\t\t\t((SQLInListExpr)inlistExpr).setTargetList(param);\r\n\t\t\t((SQLInListExpr)inlistExpr).setExpr(((SQLInSubQueryExpr)parent).getExpr());\r\n\t\t\t((SQLInListExpr)inlistExpr).setNot(((SQLInSubQueryExpr)parent).isNot());\r\n\t\t\t((SQLInListExpr)inlistExpr).setParent(sqlselect.getParent());\r\n\t\t}\r\n\t\tif(parent.getParent() instanceof MySqlSelectQueryBlock){\r\n\t\t\t((MySqlSelectQueryBlock)parent.getParent()).setWhere(inlistExpr);\r\n\t\t}else if(parent.getParent() instanceof SQLBinaryOpExpr){\r\n\t\t\tSQLBinaryOpExpr pp = ((SQLBinaryOpExpr)parent.getParent());\r\n\t\t\tif(pp.getLeft().equals(parent)){\r\n\t\t\t\tpp.setLeft(inlistExpr);\r\n\t\t\t}else if(pp.getRight().equals(parent)){\r\n\t\t\t\tpp.setRight(inlistExpr);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn statement.toString();\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/route/impl/middlerResultStrategy/RouteMiddlerReaultHandler.java",
    "content": "package io.mycat.route.impl.middlerResultStrategy;\r\n\r\nimport java.util.List;\r\n\r\nimport com.alibaba.druid.sql.ast.SQLObject;\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\r\n\r\npublic interface RouteMiddlerReaultHandler {\r\n\t\r\n\t/**\r\n\t * 处理中间结果\r\n\t * @param statement\r\n\t * @param sqlselect\r\n\t * @param param\r\n\t * @return\r\n\t */\r\n\tString dohandler(SQLStatement statement,SQLSelect sqlselect,SQLObject parent,List param);\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/route/impl/middlerResultStrategy/SQLAllResultHandler.java",
    "content": "package io.mycat.route.impl.middlerResultStrategy;\r\n\r\nimport java.util.List;\r\n\r\nimport com.alibaba.druid.sql.ast.SQLExpr;\r\nimport com.alibaba.druid.sql.ast.SQLExprImpl;\r\nimport com.alibaba.druid.sql.ast.SQLObject;\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;\r\nimport com.alibaba.druid.sql.ast.expr.SQLNullExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLValuableExpr;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\r\n\r\n/**\r\n * 对于 = \r\n\tselect * from test where id = all (select id from mytab where xxx)   --->\r\n\t改写后 sql为   all: select * from test where id = val1 and id = val2 …\r\n * @author lyj\r\n *\r\n */\r\npublic class SQLAllResultHandler implements RouteMiddlerReaultHandler{\r\n\r\n\t@Override\r\n\tpublic String dohandler(SQLStatement statement, SQLSelect sqlselect, SQLObject parent, List param) {\r\n\t\tif(parent.getParent() instanceof SQLBinaryOpExpr){\r\n\t\t\t\r\n\t\t\tSQLExprImpl inlistExpr = null;\r\n\t\t\tif(null==param||param.isEmpty()){\r\n\t\t\t\tinlistExpr = new SQLNullExpr();\r\n\t\t\t\tSQLBinaryOpExpr xp = (SQLBinaryOpExpr)parent.getParent();\r\n\t\t\t\txp.setOperator(SQLBinaryOperator.Is);\r\n\t\t\t\tif(xp.getRight().equals(parent)){\r\n\t\t\t\t\txp.setRight(inlistExpr);\r\n\t\t\t\t}else if(xp.getLeft().equals(parent)){\r\n\t\t\t\t\txp.setLeft(inlistExpr);\r\n\t\t\t\t}\r\n\t\t\t}else{\r\n\t\t\t\tint len = param.size();\r\n\t\t\t\t\r\n\t\t\t\tSQLBinaryOpExpr xp = (SQLBinaryOpExpr)parent.getParent();\r\n\t\t\t\tSQLExpr left = null;\r\n\t\t\t\tif(xp.getRight().equals(parent)){\r\n\t\t\t\t\tleft = xp.getLeft();\r\n\t\t\t\t}else if(xp.getLeft().equals(parent)){\r\n\t\t\t\t\tleft = xp.getRight();\r\n\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\tSQLBinaryOpExpr p = xp;\r\n\t\t\t\tfor(int i=0;i<len;i++){\r\n\t\t\t\t\tif(i<(len-1)){\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tSQLBinaryOpExpr rightop = new SQLBinaryOpExpr();\r\n\t\t\t\t\t\trightop.setOperator(SQLBinaryOperator.Equality);\r\n\t\t\t\t\t\tSQLValuableExpr expr = (SQLValuableExpr) param.get(i);\r\n\t\t\t\t\t\trightop.setRight(expr);\r\n\t\t\t\t\t\trightop.setParent(p);\r\n\t\t\t\t\t\trightop.setLeft(left);\r\n\t\t\t\t\t\tp.setRight(rightop);\r\n\t\t\t\t\t\tp.setOperator(SQLBinaryOperator.BooleanAnd);\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tSQLBinaryOpExpr lefeop = new SQLBinaryOpExpr();\r\n\t\t\t\t\t\tlefeop.setParent(p);\r\n\t\t\t\t\t\tlefeop.setOperator(SQLBinaryOperator.Equality);\r\n\t\t\t\t\t\tp.setLeft(lefeop);\r\n\t\t\t\t\t\tp = (SQLBinaryOpExpr) p.getLeft();\r\n\t\t\t\t\t}else{\r\n\t\t\t\t\t\tp.setLeft(left);\r\n\t\t\t\t\t\tp.setOperator(SQLBinaryOperator.Equality);\r\n\t\t\t\t\t\tSQLValuableExpr expr = (SQLValuableExpr) param.get(i);\r\n\t\t\t\t\t\tp.setRight(expr);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn statement.toString();\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/route/impl/middlerResultStrategy/SQLExistsResultHandler.java",
    "content": "package io.mycat.route.impl.middlerResultStrategy;\r\n\r\nimport java.util.List;\r\n\r\nimport com.alibaba.druid.sql.ast.SQLExpr;\r\nimport com.alibaba.druid.sql.ast.SQLObject;\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLNullExpr;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\r\n\r\n/**\r\n * 对于 EXISTS/NOT EXISTS, 判断subquery结果集是否为空。\r\n * @author lyj\r\n *\r\n */\r\npublic class SQLExistsResultHandler implements RouteMiddlerReaultHandler {\r\n\r\n\t@Override\r\n\tpublic String dohandler(SQLStatement statement, SQLSelect sqlselect, SQLObject parent, List param) {\r\n\t\tSQLExpr se = null;\r\n\t\t\r\n\t\tif(param==null||param.isEmpty()){\r\n\t\t\tse = new SQLNullExpr();\r\n\t\t}else{\r\n\t\t\tse = (SQLExpr) param.get(0);\r\n\t\t}\r\n\t\t\r\n\t\tif(parent.getParent() instanceof MySqlSelectQueryBlock){\r\n\t\t\tMySqlSelectQueryBlock msqb = (MySqlSelectQueryBlock)parent.getParent();\r\n\t\t\tmsqb.setWhere(se);\r\n\t\t}else if(parent.getParent() instanceof SQLBinaryOpExpr){\r\n\t\t\tSQLBinaryOpExpr sbqe=(SQLBinaryOpExpr)parent.getParent();\r\n\t\t\t\r\n\t\t\tif(sbqe.getLeft().equals(parent)){\r\n\t\t\t\tsbqe.setLeft(se);\r\n\t\t\t}else{\r\n\t\t\t\tsbqe.setRight(se);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn statement.toString();\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/route/impl/middlerResultStrategy/SQLQueryResultHandler.java",
    "content": "package io.mycat.route.impl.middlerResultStrategy;\r\n\r\nimport java.util.List;\r\n\r\nimport com.alibaba.druid.sql.ast.SQLExpr;\r\nimport com.alibaba.druid.sql.ast.SQLExprImpl;\r\nimport com.alibaba.druid.sql.ast.SQLObject;\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLListExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLNullExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLQueryExpr;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectItem;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\r\n\r\npublic class SQLQueryResultHandler implements RouteMiddlerReaultHandler {\r\n\r\n\t@Override\r\n\tpublic String dohandler(SQLStatement statement, SQLSelect sqlselect, SQLObject parent, List param) {\r\n\t\tif(parent.getParent() instanceof SQLBinaryOpExpr){\r\n\t\t\tSQLBinaryOpExpr pp = (SQLBinaryOpExpr)parent.getParent();\r\n\t\t\tSQLExprImpl listExpr = null;\r\n\t\t\tif(null==param||param.isEmpty()){\r\n\t\t\t\tlistExpr = new SQLNullExpr();\r\n\t\t\t}else{\r\n\t\t\t\tlistExpr = new SQLListExpr();\r\n\t\t\t\t((SQLListExpr)listExpr).getItems().addAll(param);\r\n\t\t\t}\r\n\t\t\tif(pp.getLeft().equals(parent)){\r\n\t\t\t\tpp.setLeft(listExpr);\r\n\t\t\t}else if(pp.getRight().equals(parent)){\r\n\t\t\t\tpp.setRight(listExpr);\r\n\t\t\t}\r\n\t\t}else if(parent.getParent() instanceof SQLSelectItem){\r\n\t\t\tSQLSelectItem pp = (SQLSelectItem)parent.getParent();\r\n\t\t\tSQLExprImpl listExpr = null;\r\n\t\t\tif(null==param||param.isEmpty()){\r\n\t\t\t\tlistExpr = new SQLNullExpr();\r\n\t\t\t}else{\r\n\t\t\t\tlistExpr = new SQLListExpr();\r\n\t\t\t\t((SQLListExpr)listExpr).getItems().addAll(param);\r\n\t\t\t}\r\n\t\t\tpp.setExpr(listExpr);\r\n\t\t}else if(parent.getParent() instanceof SQLSelectGroupByClause){\r\n\t\t\tSQLSelectGroupByClause pp = (SQLSelectGroupByClause)parent.getParent();\r\n\t\t\t\r\n\t\t\tList<SQLExpr> items = pp.getItems();\r\n\t\t\tfor(int i=0;i<items.size();i++){\r\n\t\t\t\tSQLExpr expr = items.get(i);\r\n\t\t\t\tif(expr instanceof SQLQueryExpr \r\n\t\t\t\t\t\t&&((SQLQueryExpr)expr).getSubQuery().equals(sqlselect)){\r\n\t\t\t\t\t\r\n\t\t\t\t\tSQLExprImpl listExpr = null;\r\n\t\t\t\t\tif(null==param||param.isEmpty()){\r\n\t\t\t\t\t\tlistExpr = new SQLNullExpr();\r\n\t\t\t\t\t}else{\r\n\t\t\t\t\t\tlistExpr = new SQLListExpr();\r\n\t\t\t\t\t\t((SQLListExpr)listExpr).getItems().addAll(param);\r\n\t\t\t\t\t}\r\n\t\t\t\t\titems.set(i, listExpr);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}else if(parent.getParent() instanceof SQLSelectOrderByItem){\r\n\t\t\tSQLSelectOrderByItem orderItem = (SQLSelectOrderByItem)parent.getParent();\r\n\t\t\tSQLExprImpl listExpr = null;\r\n\t\t\tif(null==param||param.isEmpty()){\r\n\t\t\t\tlistExpr = new SQLNullExpr();\r\n\t\t\t}else{\r\n\t\t\t\tlistExpr = new SQLListExpr();\r\n\t\t\t\t((SQLListExpr)listExpr).getItems().addAll(param);\r\n\t\t\t}\r\n\t\t\tlistExpr.setParent(orderItem);\r\n\t\t\torderItem.setExpr(listExpr);\r\n\t\t}else if(parent.getParent() instanceof MySqlSelectQueryBlock){\r\n\t\t\tMySqlSelectQueryBlock query = (MySqlSelectQueryBlock)parent.getParent();\r\n\t\t\t// select * from subtest1 a where (select 1 from subtest3); 这种情况会进入到当前分支.\r\n\t\t\t// 改写为   select * from subtest1 a where (1); 或  select * from subtest1 a where (null);\r\n\t\t\tSQLExprImpl listExpr = null;\r\n\t\t\tif(null==param||param.isEmpty()){\r\n\t\t\t\tlistExpr = new SQLNullExpr();\r\n\t\t\t}else{\r\n\t\t\t\tlistExpr = new SQLListExpr();\r\n\t\t\t\t((SQLListExpr)listExpr).getItems().addAll(param);\r\n\t\t\t}\r\n\t\t\tlistExpr.setParent(query);\r\n\t\t\tquery.setWhere(listExpr);\r\n\t\t}\r\n\t\treturn statement.toString();\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/ManagerParse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser;\n\nimport io.mycat.route.parser.util.ParseUtil;\n\n/**\n * @author mycat\n * @author mycat\n */\npublic final class ManagerParse {\n\n\tpublic static final int OTHER = -1;\n\tpublic static final int SELECT = 1;\n\tpublic static final int SET = 2;\n\tpublic static final int SHOW = 3;\n\tpublic static final int SWITCH = 4;\n\tpublic static final int KILL_CONN = 5;\n\tpublic static final int STOP = 6;\n\tpublic static final int RELOAD = 7;\n\tpublic static final int ROLLBACK = 8;\n\tpublic static final int OFFLINE = 9;\n\tpublic static final int ONLINE = 10;\n\tpublic static final int CLEAR = 11;\n\tpublic static final int CONFIGFILE = 12;\n\tpublic static final int LOGFILE = 13;\n\n\tpublic static final int ZK = 14;\n\n\tpublic static int parse(String stmt) {\n\t\tfor (int i = 0; i < stmt.length(); i++) {\n\t\t\tswitch (stmt.charAt(i)) {\n\t\t\tcase ' ':\n\t\t\t\tcontinue;\n\t\t\tcase '/':\n\t\t\tcase '#':\n\t\t\t\ti = ParseUtil.comment(stmt, i);\n\t\t\t\tcontinue;\n\t\t\tcase 'C':\n\t\t\tcase 'c':\n\t\t\t\treturn cCheck(stmt, i);\n\t\t\tcase 'F':\n\t\t\tcase 'f':\n\t\t\t\treturn fCheck(stmt, i);\n\t\t\tcase 'L':\n\t\t\tcase 'l':\n\t\t\t\treturn lCheck(stmt, i);\n\t\t\tcase 'S':\n\t\t\tcase 's':\n\t\t\t\treturn sCheck(stmt, i);\n\t\t\tcase 'K':\n\t\t\tcase 'k':\n\t\t\t\treturn kill(stmt, i);\n\t\t\tcase 'O':\n\t\t\tcase 'o':\n\t\t\t\treturn oCheck(stmt, i);\n\t\t\tcase 'R':\n\t\t\tcase 'r':\n\t\t\t\treturn rCheck(stmt, i);\n\t\t\tcase 'Z':\n\t\t\tcase 'z':\n\t\t\t\treturn zCheck(stmt,i);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\tprivate static int zCheck(String stmt, int offset) {\n\t\tString thePart = stmt.substring(offset).toUpperCase();\n\t\tif(thePart.startsWith(\"ZK\")){\n\t\t\treturn ZK;\n\t\t}else {\n\t\t\treturn OTHER;\n\t\t}\n\t}\n\n\t// show LOG check\n\tprivate static int lCheck(String stmt, int offset) {\n\t\tString thePart = stmt.substring(offset).toUpperCase();\n\t\tif (thePart.startsWith(\"LOG @@\")) {\n\t\t\treturn LOGFILE;\n\t\t} else {\n\t\t\treturn OTHER;\n\t\t}\n\t}\n\n\t// config file check\n\tprivate static int fCheck(String stmt, int offset) {\n\t\tString thePart = stmt.substring(offset).toUpperCase();\n\t\tif (thePart.startsWith(\"FILE @@\")) {\n\t\t\treturn CONFIGFILE;\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// CLEAR or config file\n\tprivate static int cCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"LEAR \".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'L' || c1 == 'l') && (c2 == 'E' || c2 == 'e')\n\t\t\t\t\t&& (c3 == 'A' || c3 == 'a') && (c4 == 'R' || c4 == 'r')\n\t\t\t\t\t&& (c5 == ' ' || c5 == '\\t' || c5 == '\\r' || c5 == '\\n')) {\n\t\t\t\treturn (offset << 8) | CLEAR;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\tprivate static int oCheck(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase 'F':\n\t\t\tcase 'f':\n\t\t\t\treturn ofCheck(stmt, offset);\n\t\t\tcase 'N':\n\t\t\tcase 'n':\n\t\t\t\treturn onCheck(stmt, offset);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\tprivate static int onCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"line\".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'l' || c1 == 'L')\n\t\t\t\t\t&& (c2 == 'i' || c2 == 'I')\n\t\t\t\t\t&& (c3 == 'n' || c3 == 'N')\n\t\t\t\t\t&& (c4 == 'e' || c4 == 'E')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn ONLINE;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\tprivate static int ofCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"fline\".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'f' || c1 == 'F')\n\t\t\t\t\t&& (c2 == 'l' || c2 == 'L')\n\t\t\t\t\t&& (c3 == 'i' || c3 == 'I')\n\t\t\t\t\t&& (c4 == 'n' || c4 == 'N')\n\t\t\t\t\t&& (c5 == 'e' || c5 == 'E')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn OFFLINE;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\tprivate static int sCheck(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase 'E':\n\t\t\tcase 'e':\n\t\t\t\treturn seCheck(stmt, offset);\n\t\t\tcase 'H':\n\t\t\tcase 'h':\n\t\t\t\treturn show(stmt, offset);\n\t\t\tcase 'W':\n\t\t\tcase 'w':\n\t\t\t\treturn swh(stmt, offset);\n\t\t\tcase 'T':\n\t\t\tcase 't':\n\t\t\t\treturn stop(stmt, offset);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\tprivate static int seCheck(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase 'L':\n\t\t\tcase 'l':\n\t\t\t\treturn select(stmt, offset);\n\t\t\tcase 'T':\n\t\t\tcase 't':\n\t\t\t\tif (stmt.length() > ++offset) {\n\t\t\t\t\tchar c = stmt.charAt(offset);\n\t\t\t\t\tif (c == ' ' || c == '\\r' || c == '\\n' || c == '\\t'\n\t\t\t\t\t\t\t|| c == '/' || c == '#') {\n\t\t\t\t\t\treturn SET;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn OTHER;\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\tprivate static int rCheck(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase 'E':\n\t\t\tcase 'e':\n\t\t\t\treturn reload(stmt, offset);\n\t\t\tcase 'O':\n\t\t\tcase 'o':\n\t\t\t\treturn rollback(stmt, offset);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// RELOAD' '\n\tprivate static int reload(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 5) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'L' || c1 == 'l') && (c2 == 'O' || c2 == 'o')\n\t\t\t\t\t&& (c3 == 'A' || c3 == 'a') && (c4 == 'D' || c4 == 'd')\n\t\t\t\t\t&& (c5 == ' ' || c5 == '\\t' || c5 == '\\r' || c5 == '\\n')) {\n\t\t\t\treturn (offset << 8) | RELOAD;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// ROLLBACK' '\n\tprivate static int rollback(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 7) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'L' || c1 == 'l') && (c2 == 'L' || c2 == 'l')\n\t\t\t\t\t&& (c3 == 'B' || c3 == 'b') && (c4 == 'A' || c4 == 'a')\n\t\t\t\t\t&& (c5 == 'C' || c5 == 'c') && (c6 == 'K' || c6 == 'k')\n\t\t\t\t\t&& (c7 == ' ' || c7 == '\\t' || c7 == '\\r' || c7 == '\\n')) {\n\t\t\t\treturn (offset << 8) | ROLLBACK;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SELECT' '\n\tprivate static int select(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 4) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e') && (c2 == 'C' || c2 == 'c')\n\t\t\t\t\t&& (c3 == 'T' || c3 == 't')\n\t\t\t\t\t&& (c4 == ' ' || c4 == '\\t' || c4 == '\\r' || c4 == '\\n')) {\n\t\t\t\treturn (offset << 8) | SELECT;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SHOW' '\n\tprivate static int show(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 3) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'O' || c1 == 'o') && (c2 == 'W' || c2 == 'w')\n\t\t\t\t\t&& (c3 == ' ' || c3 == '\\t' || c3 == '\\r' || c3 == '\\n')) {\n\t\t\t\treturn (offset << 8) | SHOW;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SWITCH' '\n\tprivate static int swh(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 5) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'I' || c1 == 'i') && (c2 == 'T' || c2 == 't')\n\t\t\t\t\t&& (c3 == 'C' || c3 == 'c') && (c4 == 'H' || c4 == 'h')\n\t\t\t\t\t&& (c5 == ' ' || c5 == '\\t' || c5 == '\\r' || c5 == '\\n')) {\n\t\t\t\treturn (offset << 8) | SWITCH;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// STOP' '\n\tprivate static int stop(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 3) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'O' || c1 == 'o') && (c2 == 'P' || c2 == 'p')\n\t\t\t\t\t&& (c3 == ' ' || c3 == '\\t' || c3 == '\\r' || c3 == '\\n')) {\n\t\t\t\treturn (offset << 8) | STOP;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// KILL @\n\tprivate static int kill(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 3) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'I' || c1 == 'i') && (c2 == 'L' || c2 == 'l')\n\t\t\t\t\t&& (c3 == 'L' || c3 == 'l')\n\t\t\t\t\t&& (c4 == ' ' || c4 == '\\t' || c4 == '\\r' || c4 == '\\n')) {\n\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\tcase ' ':\n\t\t\t\t\tcase '\\t':\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase '@':\n\t\t\t\t\t\treturn killConnection(stmt, offset);\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// KILL @@CONNECTION' ' XXXXXX\n\tprivate static int killConnection(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"@CONNECTION \".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tchar c8 = stmt.charAt(++offset);\n\t\t\tchar c9 = stmt.charAt(++offset);\n\t\t\tchar c10 = stmt.charAt(++offset);\n\t\t\tchar c11 = stmt.charAt(++offset);\n\t\t\tchar c12 = stmt.charAt(++offset);\n\t\t\tif ((c1 == '@')\n\t\t\t\t\t&& (c2 == 'C' || c2 == 'c')\n\t\t\t\t\t&& (c3 == 'O' || c3 == 'o')\n\t\t\t\t\t&& (c4 == 'N' || c4 == 'n')\n\t\t\t\t\t&& (c5 == 'N' || c5 == 'n')\n\t\t\t\t\t&& (c6 == 'E' || c6 == 'e')\n\t\t\t\t\t&& (c7 == 'C' || c7 == 'c')\n\t\t\t\t\t&& (c8 == 'T' || c8 == 't')\n\t\t\t\t\t&& (c9 == 'I' || c9 == 'i')\n\t\t\t\t\t&& (c10 == 'O' || c10 == 'o')\n\t\t\t\t\t&& (c11 == 'N' || c11 == 'n')\n\t\t\t\t\t&& (c12 == ' ' || c12 == '\\t' || c12 == '\\r' || c12 == '\\n')) {\n\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\tcase ' ':\n\t\t\t\t\tcase '\\t':\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn (offset << 8) | KILL_CONN;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/ManagerParseClear.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser;\n\nimport io.mycat.route.parser.util.ParseUtil;\n\n/**\n * @author mycat\n */\npublic class ManagerParseClear {\n\n    public static final int OTHER = -1;\n    public static final int SLOW_SCHEMA = 1;\n    public static final int SLOW_DATANODE = 2;\n\n    public static int parse(String stmt, int offset) {\n        int i = offset;\n        for (; i < stmt.length(); i++) {\n            switch (stmt.charAt(i)) {\n            case ' ':\n                continue;\n            case '/':\n            case '#':\n                i = ParseUtil.comment(stmt, i);\n                continue;\n            case '@':\n                return clear2Check(stmt, i);\n            default:\n                return OTHER;\n            }\n        }\n        return OTHER;\n    }\n\n    // CLEAR @@SLOW\n    static int clear2Check(String stmt, int offset) {\n        if (stmt.length() > ++offset && stmt.charAt(offset) == '@'\n                &&stmt.length() > offset + \"SLOW \".length()) {\n                char c1 = stmt.charAt(++offset);\n                char c2 = stmt.charAt(++offset);\n                char c3 = stmt.charAt(++offset);\n                char c4 = stmt.charAt(++offset);\n                char c5 = stmt.charAt(++offset);\n                if ((c1 == 'S' || c1 == 's') && (c2 == 'L' || c2 == 'l') && (c3 == 'O' || c3 == 'o')\n                        && (c4 == 'W' || c4 == 'w') && (c5 == ' ')) {\n                    while (stmt.length() > ++offset) {\n                        switch (stmt.charAt(offset)) {\n                        case ' ':\n                            continue;\n                        case 'W':\n                        case 'w':\n                            return clear2WhereCheck(stmt, offset);\n                        default:\n                            return OTHER;\n                        }\n                    }\n                }\n        }\n        return OTHER;\n    }\n\n    // CLEAR @@SLOW WHERE\n    static int clear2WhereCheck(String stmt, int offset) {\n        if (stmt.length() > offset + \"HERE \".length()) {\n            char c1 = stmt.charAt(++offset);\n            char c2 = stmt.charAt(++offset);\n            char c3 = stmt.charAt(++offset);\n            char c4 = stmt.charAt(++offset);\n            char c5 = stmt.charAt(++offset);\n            if ((c1 == 'H' || c1 == 'h') && (c2 == 'E' || c2 == 'e') && (c3 == 'R' || c3 == 'r')\n                    && (c4 == 'E' || c4 == 'e') && (c5 == ' ')) {\n                while (stmt.length() > ++offset) {\n                    switch (stmt.charAt(offset)) {\n                    case ' ':\n                        continue;\n                    case 'D':\n                    case 'd':\n                        return clear2DCheck(stmt, offset);\n                    case 'S':\n                    case 's':\n                        return clear2SCheck(stmt, offset);\n                    default:\n                        return OTHER;\n                    }\n                }\n            }\n        }\n        return OTHER;\n    }\n\n    // CLEAR @@SLOW WHERE DATANODE = XXXXXX\n    static int clear2DCheck(String stmt, int offset) {\n        if (stmt.length() > offset + \"ATANODE\".length()) {\n            char c1 = stmt.charAt(++offset);\n            char c2 = stmt.charAt(++offset);\n            char c3 = stmt.charAt(++offset);\n            char c4 = stmt.charAt(++offset);\n            char c5 = stmt.charAt(++offset);\n            char c6 = stmt.charAt(++offset);\n            char c7 = stmt.charAt(++offset);\n            if ((c1 == 'A' || c1 == 'a') && (c2 == 'T' || c2 == 't') && (c3 == 'A' || c3 == 'a')\n                    && (c4 == 'N' || c4 == 'n') && (c5 == 'O' || c5 == 'o') && (c6 == 'D' || c6 == 'd')\n                    && (c7 == 'E' || c7 == 'e')) {\n                while (stmt.length() > ++offset) {\n                    switch (stmt.charAt(offset)) {\n                    case ' ':\n                        continue;\n                    case '=':\n                        while (stmt.length() > ++offset) {\n                            switch (stmt.charAt(offset)) {\n                            case ' ':\n                                continue;\n                            default:\n                                return (offset << 8) | SLOW_DATANODE;\n                            }\n                        }\n                        return OTHER;\n                    default:\n                        return OTHER;\n                    }\n                }\n            }\n        }\n        return OTHER;\n    }\n\n    // CLEAR @@SLOW WHERE SCHEMA = XXXXXX\n    static int clear2SCheck(String stmt, int offset) {\n        if (stmt.length() > offset + \"CHEMA\".length()) {\n            char c1 = stmt.charAt(++offset);\n            char c2 = stmt.charAt(++offset);\n            char c3 = stmt.charAt(++offset);\n            char c4 = stmt.charAt(++offset);\n            char c5 = stmt.charAt(++offset);\n            if ((c1 == 'C' || c1 == 'c') && (c2 == 'H' || c2 == 'h') && (c3 == 'E' || c3 == 'e')\n                    && (c4 == 'M' || c4 == 'm') && (c5 == 'A' || c5 == 'a')) {\n                while (stmt.length() > ++offset) {\n                    switch (stmt.charAt(offset)) {\n                    case ' ':\n                        continue;\n                    case '=':\n                        while (stmt.length() > ++offset) {\n                            switch (stmt.charAt(offset)) {\n                            case ' ':\n                                continue;\n                            default:\n                                return (offset << 8) | SLOW_SCHEMA;\n                            }\n                        }\n                        return OTHER;\n                    default:\n                        return OTHER;\n                    }\n                }\n            }\n        }\n        return OTHER;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/ManagerParseHeartbeat.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser;\n\nimport io.mycat.route.parser.util.Pair;\n\n/**\n * @author songwie\n */\npublic final class ManagerParseHeartbeat {\n\n    public static final int OTHER = -1;\n    public static final int DATASOURCE = 1;\n    \n   // SHOW @@HEARTBEAT\n    static int show2HeaCheck(String stmt, int offset) {\n        if (stmt.length() > offset + \"RTBEAT\".length()) {\n            char c1 = stmt.charAt(++offset);\n            char c2 = stmt.charAt(++offset);\n            char c3 = stmt.charAt(++offset);\n            char c4 = stmt.charAt(++offset);\n            char c5 = stmt.charAt(++offset);\n            char c6 = stmt.charAt(++offset);\n            if ((c1 == 'R' || c1 == 'r') && (c2 == 'T' || c2 == 't') & (c3 == 'B' || c3 == 'b')\n                    && (c4 == 'E' || c4 == 'e') & (c5 == 'A' || c5 == 'a') && (c6 == 'T' || c6 == 't')) {\n            \tif (stmt.length() > offset + \".DETAIL\".length()) {\n            \t\tchar c7 = stmt.charAt(++offset);\n                \tif(c7 == '.'){\n                \t\treturn show2HeaDetailCheck(stmt,offset);\n                \t}\n            \t}\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\n                    return OTHER;\n                }\n                return ManagerParseShow.HEARTBEAT;\n            }\n        }\n        return OTHER;\n    }\n    // SHOW @@HEARTBEAT.DETAIL\n    static int show2HeaDetailCheck(String stmt, int offset) {\n        if (stmt.length() > offset + \"DETAIL\".length()) {\n            char c1 = stmt.charAt(++offset);\n            char c2 = stmt.charAt(++offset);\n            char c3 = stmt.charAt(++offset);\n            char c4 = stmt.charAt(++offset);\n            char c5 = stmt.charAt(++offset);\n            char c6 = stmt.charAt(++offset);\n            if ((c1 == 'D' || c1 == 'd') && (c2 == 'E' || c2 == 'e') & (c3 == 'T' || c3 == 't')\n                    && (c4 == 'A' || c4 == 'a') & (c5 == 'I' || c5 == 'i') && (c6 == 'L' || c6 == 'l')) {\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\n                    return OTHER;\n                }\n                return ManagerParseShow.HEARTBEAT_DETAIL;\n            }\n        }\n        return OTHER;\n    }\n\n    public static Pair<String, String> getPair(String stmt) {\n        int offset = stmt.indexOf(\"@@\");\n        String s = stmt.substring(++offset + \" heartbeat.detail\".length());\n        char c = s.charAt(0);\n        offset = 0;\n        if(c == ' '){\n        \tchar c1 = s.charAt(++offset);\n    \t\tchar c2 = s.charAt(++offset);\n    \t\tchar c3 = s.charAt(++offset);\n    \t\tchar c4 = s.charAt(++offset);\n    \t\tchar c5 = s.charAt(++offset);\n    \t\tchar c6 = s.charAt(++offset);\n    \t\tchar c7 = s.charAt(++offset);\n    \t\tchar c8 = s.charAt(++offset);\n    \t\tchar c9 = s.charAt(++offset);\n    \t\tchar c10 = s.charAt(++offset);\n    \t\tchar c11 = s.charAt(++offset);\n    \t\tif ((c1 == 'W' || c1 == 'w') && (c2 == 'H' || c2 == 'h') && (c3 == 'E' || c3 == 'e')\n                    && (c4 == 'R' || c4 == 'r') && (c5 == 'E' || c5 == 'e')\n                    && c6 == ' ' && (c7 == 'N' || c7 == 'n') && (c8 == 'A' || c8 == 'a') && (c9 == 'M' || c9 == 'm')\n                    && (c10 == 'E' || c10 == 'e') && (c11 == '=')) {\n    \t        String name = s.substring(++offset).trim();\n                return new Pair<String, String>(\"name\", name);\n    \t\t}\n        }\n        return new Pair<String, String>(\"name\", \"\");\n    }\n \n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/ManagerParseKill.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser;\n\nimport io.mycat.route.parser.util.ParseUtil;\n\n/**\n * @author mycat\n */\npublic final class ManagerParseKill {\n\n    public static final int OTHER = -1;\n    public static final int CONNECTION = 1;\n\n    public static int parse(String stmt, int offset) {\n        int i = offset;\n        for (; i < stmt.length(); i++) {\n            switch (stmt.charAt(i)) {\n            case ' ':\n                continue;\n            case '/':\n            case '#':\n                i = ParseUtil.comment(stmt, i);\n                continue;\n            case '@':\n                return kill2Check(stmt, i);\n            default:\n                return OTHER;\n            }\n        }\n        return OTHER;\n    }\n\n    // KILL @@CONNECTION\n    static int kill2Check(String stmt, int offset) {\n        if (stmt.length() > ++offset && stmt.charAt(offset) == '@'\n                && stmt.length() > offset + 10) {\n                char c1 = stmt.charAt(++offset);\n                char c2 = stmt.charAt(++offset);\n                char c3 = stmt.charAt(++offset);\n                char c4 = stmt.charAt(++offset);\n                char c5 = stmt.charAt(++offset);\n                char c6 = stmt.charAt(++offset);\n                char c7 = stmt.charAt(++offset);\n                char c8 = stmt.charAt(++offset);\n                char c9 = stmt.charAt(++offset);\n                char c10 = stmt.charAt(++offset);\n                if ((c1 == 'C' || c1 == 'c') && (c2 == 'O' || c2 == 'o') && (c3 == 'N' || c3 == 'n')\n                        && (c4 == 'N' || c4 == 'n') && (c5 == 'E' || c5 == 'e') && (c6 == 'C' || c6 == 'c')\n                        && (c7 == 'T' || c7 == 't') && (c8 == 'I' || c8 == 'i') && (c9 == 'O' || c9 == 'o')\n                        && (c10 == 'N' || c10 == 'n')) {\n                    if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\n                        return OTHER;\n                    }\n                    return CONNECTION;\n                }\n        }\n        return OTHER;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/ManagerParseReload.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.route.parser;\r\n\r\nimport io.mycat.route.parser.util.ParseUtil;\r\n\r\n/**\r\n * @author mycat\r\n */\r\npublic final class ManagerParseReload {\r\n\r\n    public static final int OTHER = -1;\r\n    public static final int CONFIG = 1;\r\n    public static final int ROUTE = 2;\r\n    public static final int USER = 3;\r\n    public static final int USER_STAT = 4;\r\n    public static final int CONFIG_ALL = 5;\r\n    public static final int SQL_SLOW = 6;\r\n    public static final int QUERY_CF = 8;\r\n    public static final int SQL_STAT = 9;\r\n       \r\n    public static int parse(String stmt, int offset) {\r\n        int i = offset;\r\n        for (; i < stmt.length(); i++) {\r\n            switch (stmt.charAt(i)) {\r\n            case ' ':\r\n                continue;\r\n            case '/':\r\n            case '#':\r\n                i = ParseUtil.comment(stmt, i);\r\n                continue;\r\n            case '@':\r\n                return reload2Check(stmt, i);\r\n            default:\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    static int reload2Check(String stmt, int offset) {\r\n        if (stmt.length() > ++offset && stmt.charAt(offset) == '@'\r\n                && stmt.length() > ++offset) {\r\n                switch (stmt.charAt(offset)) {\r\n                case 'C':\r\n                case 'c':\r\n                    return reload2CCheck(stmt, offset);\r\n                case 'R':\r\n                case 'r':\r\n                    return reload2RCheck(stmt, offset);\r\n                case 'U':\r\n                case 'u':\r\n                    return reload2UCheck(stmt, offset);\r\n                case 'S':\r\n                case 's':\r\n                    return reload2SCheck(stmt, offset);       \r\n                case 'Q':\r\n                case 'q':\r\n                    return reload2QCheck(stmt, offset);     \r\n                default:\r\n                    return OTHER;\r\n                }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // RELOAD @@CONFIG\r\n    static int reload2CCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + 5) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            if ((c1 == 'O' || c1 == 'o') && (c2 == 'N' || c2 == 'n') && (c3 == 'F' || c3 == 'f')\r\n                    && (c4 == 'I' || c4 == 'i') && (c5 == 'G' || c5 == 'g')) {\r\n                if (stmt.length() > offset + 4)\r\n                {\r\n                    char c6 = stmt.charAt(++offset);\r\n                    char c7 = stmt.charAt(++offset);\r\n                    char c8 = stmt.charAt(++offset);\r\n                    char c9 = stmt.charAt(++offset);\r\n                    if ((c6 == '_' || c6 == '-') && (c7 == 'A' || c7 == 'a') && (c8 == 'L' || c8 == 'l')\r\n                            && (c9 == 'L' || c9 == 'l') ) {\r\n                          return CONFIG_ALL;\r\n                    }\r\n                }\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n\r\n                return CONFIG;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // RELOAD @@ROUTE\r\n    static int reload2RCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + 4) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            if ((c1 == 'O' || c1 == 'o') && (c2 == 'U' || c2 == 'u') && (c3 == 'T' || c3 == 't')\r\n                    && (c4 == 'E' || c4 == 'e')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return ROUTE;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // RELOAD @@USER\r\n    static int reload2UCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + 3) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            if ((c1 == 'S' || c1 == 's') && (c2 == 'E' || c2 == 'e') && (c3 == 'R' || c3 == 'r')) {\r\n            \t\r\n            \t\r\n            \tif (stmt.length() > offset + 5)\r\n                {\r\n                    char c6 = stmt.charAt(++offset);\r\n                    char c7 = stmt.charAt(++offset);\r\n                    char c8 = stmt.charAt(++offset);\r\n                    char c9 = stmt.charAt(++offset);\r\n                    char c10 = stmt.charAt(++offset);\r\n                    \r\n                    if ((c6 == '_' || c6 == '-') && (c7 == 'S' || c7 == 's') && (c8 == 'T' || c8 == 't')\r\n                            && (c9 == 'A' || c9 == 'a') && (c10 == 'T' || c10 == 't') ) {\r\n                          return USER_STAT;\r\n                    }\r\n                }\r\n            \t\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return USER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    \r\n    // RELOAD @@SQL\r\n    static int reload2SCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + 4) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);  \r\n            \r\n            // reload @@sqlslow\r\n            if ((c1 == 'Q' || c1 == 'q') && (c2 == 'L' || c2 == 'l') && (c3 == 's' || c3 == 'S')\r\n                    && (c4 == 'L' || c4 == 'l') && (c5 == 'O' || c5 == 'o') && (c6 == 'W' || c6 == 'w')\r\n                    && stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return SQL_SLOW ;\r\n            }\r\n\r\n            // reload @@sqlstat\r\n            if ((c1 == 'Q' || c1 == 'q') && (c2 == 'L' || c2 == 'l') && (c3 == 's' || c3 == 'S')\r\n                && (c4 == 'T' || c4 == 't') && (c5 == 'A' || c5 == 'a') && (c6 == 'T' || c6 == 't')\r\n                && stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                return SQL_STAT ;\r\n            }\r\n\r\n            return OTHER;\r\n        }\r\n        return OTHER;\r\n    }\r\n    \r\n    // RELOAD @@QUERY\r\n    static int reload2QCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + 4) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);  \r\n            char c7 = stmt.charAt(++offset);\r\n\r\n            if ((c1 == 'U' || c1 == 'u') && (c2 == 'E' || c2 == 'e') && (c3 == 'R' || c3 == 'r')\r\n                    && (c4 == 'Y' || c4 == 'y') && (c5 == '_' ) && (c6 == 'C' || c6 == 'c') && (c7 == 'F' || c7 == 'f') ) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return QUERY_CF ;\r\n                }\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/ManagerParseRollback.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser;\n\nimport io.mycat.route.parser.util.ParseUtil;\n\n/**\n * @author mycat\n */\npublic final class ManagerParseRollback {\n\n    public static final int OTHER = -1;\n    public static final int CONFIG = 1;\n    public static final int ROUTE = 2;\n    public static final int USER = 3;\n\n    public static int parse(String stmt, int offset) {\n        int i = offset;\n        for (; i < stmt.length(); i++) {\n            switch (stmt.charAt(i)) {\n            case ' ':\n                continue;\n            case '/':\n            case '#':\n                i = ParseUtil.comment(stmt, i);\n                continue;\n            case '@':\n                return rollback2Check(stmt, i);\n            default:\n                return OTHER;\n            }\n        }\n        return OTHER;\n    }\n\n    static int rollback2Check(String stmt, int offset) {\n        if (stmt.length() > ++offset && stmt.charAt(offset) == '@'\n                && stmt.length() > ++offset) {\n                switch (stmt.charAt(offset)) {\n                case 'C':\n                case 'c':\n                    return rollback2CCheck(stmt, offset);\n                case 'R':\n                case 'r':\n                    return rollback2RCheck(stmt, offset);\n                case 'U':\n                case 'u':\n                    return rollback2UCheck(stmt, offset);\n                default:\n                    return OTHER;\n                }\n        }\n        return OTHER;\n    }\n\n    // ROLLBACK @@CONFIG\n    static int rollback2CCheck(String stmt, int offset) {\n        if (stmt.length() > offset + 5) {\n            char c1 = stmt.charAt(++offset);\n            char c2 = stmt.charAt(++offset);\n            char c3 = stmt.charAt(++offset);\n            char c4 = stmt.charAt(++offset);\n            char c5 = stmt.charAt(++offset);\n            if ((c1 == 'O' || c1 == 'o') && (c2 == 'N' || c2 == 'n') && (c3 == 'F' || c3 == 'f')\n                    && (c4 == 'I' || c4 == 'i') && (c5 == 'G' || c5 == 'g')) {\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\n                    return OTHER;\n                }\n                return CONFIG;\n            }\n        }\n        return OTHER;\n    }\n\n    // ROLLBACK @@ROUTE\n    static int rollback2RCheck(String stmt, int offset) {\n        if (stmt.length() > offset + 4) {\n            char c1 = stmt.charAt(++offset);\n            char c2 = stmt.charAt(++offset);\n            char c3 = stmt.charAt(++offset);\n            char c4 = stmt.charAt(++offset);\n            if ((c1 == 'O' || c1 == 'o') && (c2 == 'U' || c2 == 'u') && (c3 == 'T' || c3 == 't')\n                    && (c4 == 'E' || c4 == 'e')) {\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\n                    return OTHER;\n                }\n                return ROUTE;\n            }\n        }\n        return OTHER;\n    }\n\n    // ROLLBACK @@USER\n    static int rollback2UCheck(String stmt, int offset) {\n        if (stmt.length() > offset + 3) {\n            char c1 = stmt.charAt(++offset);\n            char c2 = stmt.charAt(++offset);\n            char c3 = stmt.charAt(++offset);\n            if ((c1 == 'S' || c1 == 's') && (c2 == 'E' || c2 == 'e') && (c3 == 'R' || c3 == 'r')) {\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\n                    return OTHER;\n                }\n                return USER;\n            }\n        }\n        return OTHER;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/ManagerParseSelect.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser;\n\nimport io.mycat.route.parser.util.ParseUtil;\n\n/**\n * @author mycat\n */\npublic final class ManagerParseSelect {\n\n    public static final int OTHER = -1;\n    public static final int VERSION_COMMENT = 1;\n    public static final int SESSION_AUTO_INCREMENT = 2;\n    public static final int SESSION_TX_READ_ONLY = 3;\n\n    private static final char[] _VERSION_COMMENT = \"VERSION_COMMENT\".toCharArray();\n    private static final char[] _SESSION_AUTO_INCREMENT = \"SESSION.AUTO_INCREMENT_INCREMENT\".toCharArray();\n    private static final char[] _SESSION_TX_READ_ONLY = \"SESSION.TX_READ_ONLY\".toCharArray();\n\n    public static int parse(String stmt, int offset) {\n        int i = offset;\n        for (; i < stmt.length(); i++) {\n            switch (stmt.charAt(i)) {\n            case ' ':\n                continue;\n            case '/':\n            case '#':\n                i = ParseUtil.comment(stmt, i);\n                continue;\n            case '@':\n                return select2Check(stmt, i);\n            default:\n                return OTHER;\n            }\n        }\n        return OTHER;\n    }\n\n    static int select2Check(String stmt, int offset) {\n        if (stmt.length() > ++offset && stmt.charAt(offset) == '@'\n                && stmt.length() > ++offset) {\n                switch (stmt.charAt(offset)) {\n                case 'S':\n                case 's':\n                    return select2SCheck(stmt, offset);\n                case 'V':\n                case 'v':\n                    return select2VCheck(stmt, offset);\n                default:\n                    return OTHER;\n                }\n        }\n        return OTHER;\n    }\n\n    // VERSION_COMMENT\n    static int select2VCheck(String stmt, int offset) {\n        int length = offset + _VERSION_COMMENT.length;\n        if (stmt.length() >= length\n                && ParseUtil.compare(stmt, offset, _VERSION_COMMENT)) {\n                if (stmt.length() > length && stmt.charAt(length) != ' ') {\n                    return OTHER;\n                }\n                return VERSION_COMMENT;\n        }\n        return OTHER;\n    }\n\n    // SESSION.AUTO_INCREMENT_INCREMENT or SESSION.TX_READ_ONLY\n    static int select2SCheck(String stmt, int offset) {\n        int length = offset + _SESSION_AUTO_INCREMENT.length;\n        if (stmt.length() >= length\n                && ParseUtil.compare(stmt, offset, _SESSION_AUTO_INCREMENT)) {\n                if (stmt.length() > length && stmt.charAt(length) != ' ') {\n                    return OTHER;\n                }\n                return SESSION_AUTO_INCREMENT;\n        } else if (stmt.length() >= (offset + _SESSION_TX_READ_ONLY.length) \n        \t\t&& ParseUtil.compare(stmt, offset, _SESSION_TX_READ_ONLY)) {\n        \tif (stmt.length() > length && stmt.charAt(length) != ' ') {\n                return OTHER;\n            }\n        \treturn SESSION_TX_READ_ONLY;\n        }\n        \n        return OTHER;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/ManagerParseShow.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.route.parser;\r\n\r\nimport io.mycat.route.parser.util.ParseUtil;\r\n\r\n/**\r\n * @author mycat\r\n */\r\npublic final class ManagerParseShow {\r\n\r\n    public static final int OTHER = -1;\r\n    public static final int COMMAND = 1;\r\n    public static final int CONNECTION = 2;\r\n    public static final int DATABASE = 3;\r\n    public static final int DATANODE = 4;\r\n    public static final int DATASOURCE = 5;\r\n    public static final int HELP = 6;\r\n    public static final int PARSER = 7;\r\n    public static final int PROCESSOR = 8;\r\n    public static final int ROUTER = 9;\r\n    public static final int SERVER = 10;\r\n    public static final int SQL = 11;\r\n    public static final int SQL_DETAIL = 12;\r\n    public static final int SQL_EXECUTE = 13;\r\n    public static final int SQL_SLOW = 14;\r\n    public static final int SQL_SUM_USER = 15;\r\n    public static final int SQL_SUM_TABLE = 16;\r\n    public static final int SQL_HIGH = 17;\r\n    public static final int SQL_CONDITION = 18;\r\n    public static final int SQL_LARGE = 19;\r\n    public static final int SQL_RESULTSET = 20;\r\n    \r\n    public static final int THREADPOOL = 21;\r\n    public static final int TIME_CURRENT = 22;\r\n    public static final int TIME_STARTUP = 23;\r\n    public static final int VERSION = 24;\r\n    public static final int VARIABLES = 25;\r\n    public static final int COLLATION = 26;\r\n    public static final int CONNECTION_SQL = 27;\r\n    public static final int DATANODE_WHERE = 28;\r\n    public static final int DATASOURCE_WHERE = 29;\r\n    public static final int HEARTBEAT = 30;\r\n    public static final int SLOW_DATANODE = 31;\r\n    public static final int SLOW_SCHEMA = 32;\r\n    public static final int BACKEND = 33;\r\n    public static final int BACKEND_OLD = 34;\r\n    \r\n    public static final int CACHE = 35;\r\n    public static final int SESSION = 36;\r\n    public static final int SYSPARAM = 37;\r\n    public static final int SYSLOG = 38;\r\n    public static final int HEARTBEAT_DETAIL = 39;\r\n    public static final int DATASOURCE_SYNC = 40;\r\n    public static final int DATASOURCE_SYNC_DETAIL = 41;\r\n    public static final int DATASOURCE_CLUSTER = 42;\r\n\r\n\tpublic static final int WHITE_HOST = 43;\r\n\tpublic static final int WHITE_HOST_SET = 44;\r\n    public static final int DIRECTMEMORY_TOTAL = 45;\r\n    public static final int DIRECTMEMORY_DETAILl = 46;\r\n\r\n    public static final int CHECK_GLOBAL = 47;\r\n\r\n    public static int parse(String stmt, int offset) {\r\n        int i = offset;\r\n        for (; i < stmt.length(); i++) {\r\n            switch (stmt.charAt(i)) {\r\n            case ' ':\r\n                continue;\r\n            case '/':\r\n            case '#':\r\n                i = ParseUtil.comment(stmt, i);\r\n                continue;\r\n            case '@':\r\n                return show2Check(stmt, i);\r\n            case 'C':\r\n            case 'c':\r\n                return showCCheck(stmt, i);\r\n\t\t\tcase 'd':\r\n\t\t\tcase 'D':\r\n\t\t\t\treturn show2DCheck(stmt, i);\r\n\t\t\tcase 'V':\r\n            case 'v':\r\n                return showVCheck(stmt, i);\r\n            default:\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @\r\n    static int show2Check(String stmt, int offset) {\r\n        if (stmt.length() > ++offset && stmt.charAt(offset) == '@'\r\n                && stmt.length() > ++offset) {\r\n                switch (stmt.charAt(offset)) {\r\n                case 'B':\r\n                case 'b':\r\n                    return show2BCheck(stmt, offset);\r\n                case 'C':\r\n                case 'c':\r\n                    return show2CCheck(stmt, offset);\r\n                case 'D':\r\n                case 'd':\r\n                    return show2DCheck(stmt, offset);\r\n                case 'H':\r\n                case 'h':\r\n                    return show2HCheck(stmt, offset);\r\n                case 'P':\r\n                case 'p':\r\n                    return show2PCheck(stmt, offset);\r\n                case 'R':\r\n                case 'r':\r\n                    return show2RCheck(stmt, offset);\r\n                case 'S':\r\n                case 's':\r\n                    return show2SCheck(stmt, offset);\r\n                case 'T':\r\n                case 't':\r\n                    return show2TCheck(stmt, offset);\r\n                case 'V':\r\n                case 'v':\r\n                    return show2VCheck(stmt, offset);\r\n                case 'W':\r\n                case 'w':\r\n                    return show2WCheck(stmt, offset);                    \r\n                default:\r\n                    return OTHER;\r\n                }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW COLLATION\r\n    static int showCCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"OLLATION\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            char c7 = stmt.charAt(++offset);\r\n            char c8 = stmt.charAt(++offset);\r\n            if ((c1 == 'O' || c1 == 'o') && (c2 == 'L' || c2 == 'l') && (c3 == 'L' || c3 == 'l')\r\n                    && (c4 == 'A' || c4 == 'a') && (c5 == 'T' || c5 == 't') && (c6 == 'I' || c6 == 'i')\r\n                    && (c7 == 'O' || c7 == 'o') && (c8 == 'N' || c8 == 'n')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return COLLATION;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW VARIABLES\r\n    static int showVCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"ARIABLES\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            char c7 = stmt.charAt(++offset);\r\n            char c8 = stmt.charAt(++offset);\r\n            if ((c1 == 'A' || c1 == 'a') && (c2 == 'R' || c2 == 'r') && (c3 == 'I' || c3 == 'i')\r\n                    && (c4 == 'A' || c4 == 'a') && (c5 == 'B' || c5 == 'b') && (c6 == 'L' || c6 == 'l')\r\n                    && (c7 == 'E' || c7 == 'e') && (c8 == 'S' || c8 == 's')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return VARIABLES;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@BACKEND\r\n    static int show2BCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"ACKEND\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            if ((c1 == 'A' || c1 == 'a') && (c2 == 'C' || c2 == 'c') && (c3 == 'K' || c3 == 'k')\r\n                    && (c4 == 'E' || c4 == 'e') && (c5 == 'N' || c5 == 'n') && (c6 == 'D' || c6 == 'd')) {\r\n                \r\n                if (stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ';':\r\n                    case ' ':\r\n                        return BACKEND;\r\n                    case '.':\r\n                        return show2BackendOld(stmt, offset);\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n                return BACKEND;\r\n                \r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    \r\n    static int show2BackendOld(String stmt, int offset) {\r\n    \t  if (stmt.length() > offset + \"OLD\".length()) {\r\n              char c1 = stmt.charAt(++offset);\r\n              char c2 = stmt.charAt(++offset);\r\n              char c3 = stmt.charAt(++offset);\r\n              if ((c1 == 'O' || c1 == 'o') && (c2 == 'L' || c2 == 'l') && (c3 == 'D' || c3 == 'd')) {\r\n                  if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                      return OTHER;\r\n                  }\r\n                  return BACKEND_OLD;\r\n              }\r\n          }\r\n          return OTHER;\r\n    }\r\n\r\n    // SHOW @@C\r\n    static int show2CCheck(String stmt, int offset) {\r\n        if (stmt.length() > ++offset) {\r\n            switch (stmt.charAt(offset)) {\r\n            case 'O':\r\n            case 'o':\r\n                return show2CoCheck(stmt, offset);\r\n            case 'A':\r\n            case 'a':\r\n            \t return show2CACheck(stmt, offset);\r\n            case 'h':\r\n            case 'H':\r\n            \t return show2CHCheck(stmt, offset);\r\n            default:\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    // SHOW @@CHECK_GLOBAL\r\n    private static int show2CHCheck(String stmt, int offset) {\r\n    \tif (stmt.length() > offset + \"ECK_GLOBAL\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            char c7 = stmt.charAt(++offset);\r\n            char c8 = stmt.charAt(++offset);\r\n            char c9 = stmt.charAt(++offset);\r\n            char c10 = stmt.charAt(++offset);\r\n            \r\n            if ((c1 == 'E' || c1 == 'e') && (c2 == 'C' || c2 == 'c') && (c3 == 'K' || c3 == 'k')\r\n                    && (c4 == '_') && (c5 == 'G' || c5 == 'g') && (c6 == 'L' || c6 == 'l')\r\n                    && (c7 == 'O' || c7 == 'o') && (c8 == 'B' || c8 == 'b')  && (c9 == 'A' || c9 == 'a')\r\n                    && (c10 == 'L' || c10 == 'l')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return CHECK_GLOBAL;\r\n            }\r\n        }\r\n        return OTHER;\r\n\t}\r\n\r\n\t// SHOW @@CACHE\r\n    private static int show2CACheck(String stmt, int offset) {\r\n    \tString remain=stmt.substring(offset);\r\n    \t if(remain.equalsIgnoreCase(\"ACHE\"))\r\n    \t {\r\n    \t\t return CACHE;\r\n    \t }\r\n    \treturn OTHER;\r\n\t}\r\n\r\n\t// SHOW @@DATA\r\n    static int show2DCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"ATA\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            if ((c1 == 'A' || c1 == 'a') && (c2 == 'T' || c2 == 't') && (c3 == 'A' || c3 == 'a')\r\n                    && stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case 'B':\r\n                    case 'b':\r\n                        return show2DataBCheck(stmt, offset);\r\n                    case 'N':\r\n                    case 'n':\r\n                        return show2DataNCheck(stmt, offset);\r\n                    case 'S':\r\n                    case 's':\r\n                        return show2DataSCheck(stmt, offset);\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n            }else if( (c1 == 'I'|| c1 == 'i')\r\n                    &&(c2 == 'R' || c2 == 'r')\r\n                    && (c3 == 'E' || c3 == 'e')\r\n                    && stmt.length() > ++offset){   /**DIRECTMEMORY**/\r\n                    switch (stmt.charAt(offset)) {\r\n                        case 'C':\r\n                        case 'c':\r\n                            return show2DirectMemoryCheck(stmt,offset);\r\n                        default:\r\n                            return OTHER;\r\n                    }\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n // SHOW @@DIRECT_MEMORY=1 or 0\r\n    static int show2DirectMemoryCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"TMEMORY\".length()) {\r\n\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            char c7 = stmt.charAt(++offset);\r\n            char c8 = stmt.charAt(++offset);\r\n\r\n            if ((c1 == 'T' || c1 == 't')\r\n                    && (c2 == 'M' || c2 == 'm')\r\n                    && (c3 == 'E' || c3 == 'e')\r\n                    && (c4 == 'M' || c4 == 'm')\r\n                    && (c5 == 'O' || c5 == 'o')\r\n                    && (c6 == 'R' || c6 == 'r')\r\n                    && (c7 == 'Y' || c7 == 'y')\r\n                    && (c8 == '=' || c8 == '=')\r\n                    && stmt.length() > ++offset) {\r\n\r\n                switch (stmt.charAt(offset)) {\r\n                    case '1':\r\n                        return DIRECTMEMORY_TOTAL;\r\n                    case '2':\r\n                        return DIRECTMEMORY_DETAILl;\r\n                    default:\r\n                        return OTHER;\r\n                }\r\n\r\n            }\r\n        }\r\n\r\n        return OTHER;\r\n    }\r\n    // SHOW @@DataSyn\r\n    static int show2DataSynCheck(String stmt, int offset) {\r\n        if (stmt.length() > ++offset) {\r\n            switch (stmt.charAt(offset)) {\r\n            case 'S':\r\n            case 's':\r\n            \tif (stmt.length() > offset + \"yn\".length()) {\r\n            \t\tchar c1 = stmt.charAt(++offset);\r\n                    char c2 = stmt.charAt(++offset);\r\n                    if ((c1 == 'Y' || c1 == 'y') && (c2 == 'N' || c2 == 'n')){\r\n                        switch (stmt.charAt(++offset)) {\r\n                        case 'S':\r\n                        case 's':\r\n                        \treturn show2SynStatuslCheck(stmt,offset);\r\n                        case 'D':\r\n                        case 'd':\r\n                        \treturn show2SynDetailCheck(stmt,offset);\r\n                        default:\r\n                            return OTHER;\r\n                        }\r\n\r\n                    }else{\r\n                    \treturn OTHER;\r\n                    }\r\n            \t}\r\n            case 'C':\r\n            case 'c':\r\n            \tif (stmt.length() > offset + \"luster\".length()) {\r\n            \t\tchar c1 = stmt.charAt(++offset);\r\n                    char c2 = stmt.charAt(++offset);\r\n                    char c3 = stmt.charAt(++offset);\r\n                    char c4 = stmt.charAt(++offset);\r\n                    char c5 = stmt.charAt(++offset);\r\n                    char c6 = stmt.charAt(++offset);\r\n                    if ((c1 == 'L' || c1 == 'l') && (c2 == 'U' || c2 == 'u') \r\n                    \t\t&& (c3 == 'S' || c3 == 's') && (c4 == 'T' || c4 == 't')\r\n                    \t\t&& (c5 == 'E' || c5 == 'e')&& (c6 == 'R' || c6 == 'r') ){\r\n                    \treturn DATASOURCE_CLUSTER;\r\n                    }else{\r\n                    \treturn OTHER;\r\n                    }\r\n            \t}\r\n            default:\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    //show @@datasource.syndetail\r\n    static int show2SynDetailCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"etail\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n\r\n            if ((c1 == 'E' || c1 == 'e') && (c2 == 'T' || c2 == 't') && (c3 == 'A' || c3 == 'a')\r\n                    && (c4 == 'I' || c4 == 'i') && (c5 == 'L' || c5 == 'l')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return DATASOURCE_SYNC_DETAIL;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    //show @@datasource.synstatus  \r\n    static int show2SynStatuslCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"tatus\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n\r\n            if ((c1 == 'T' || c1 == 't') && (c2 == 'A' || c2 == 'a') && (c3 == 'T' || c3 == 't')\r\n                    && (c4 == 'U' || c4 == 'u') && (c5 == 'S' || c5 == 's')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return DATASOURCE_SYNC;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@HELP\r\n    static int show2HCheck(String stmt, int offset) {\r\n        if (stmt.length() > ++offset) {\r\n            switch (stmt.charAt(offset)) {\r\n            case 'E':\r\n            case 'e':\r\n                return show2HeCheck(stmt, offset);\r\n            default:\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@HE\r\n    static int show2HeCheck(String stmt, int offset) {\r\n        if (stmt.length() > ++offset) {\r\n            switch (stmt.charAt(offset)) {\r\n            case 'L':\r\n            case 'l':\r\n                return show2HelCheck(stmt, offset);\r\n            case 'A':\r\n            case 'a':\r\n                return ManagerParseHeartbeat.show2HeaCheck(stmt, offset);\r\n            default:\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@HELP\r\n    static int show2HelCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"P\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            if ((c1 == 'P' || c1 == 'p')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return HELP;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    \r\n\r\n    // SHOW @@P\r\n    static int show2PCheck(String stmt, int offset) {\r\n        if (stmt.length() > ++offset) {\r\n            switch (stmt.charAt(offset)) {\r\n            case 'A':\r\n            case 'a':\r\n                return show2PaCheck(stmt, offset);\r\n            case 'R':\r\n            case 'r':\r\n                return show2PrCheck(stmt, offset);\r\n            default:\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@ROUTER\r\n    static int show2RCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"OUTER\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            if ((c1 == 'O' || c1 == 'o') && (c2 == 'U' || c2 == 'u') && (c3 == 'T' || c3 == 't')\r\n                    && (c4 == 'E' || c4 == 'e') && (c5 == 'R' || c5 == 'r')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return ROUTER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@S\r\n    static int show2SCheck(String stmt, int offset) {\r\n        if (stmt.length() > ++offset) {\r\n            switch (stmt.charAt(offset)) {\r\n            case 'E':\r\n            case 'e':\r\n                return show2SeCheck(stmt, offset);\r\n            case 'Q':\r\n            case 'q':\r\n                return show2SqCheck(stmt, offset);\r\n            case 'L':\r\n            case 'l':\r\n                return show2SlCheck(stmt, offset);\r\n            case 'Y':\r\n            case 'y':\r\n            \treturn show2SyCheck(stmt, offset);\r\n            default:\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    \r\n\t// SHOW @@SLOW\r\n    static int show2SlCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"OW \".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            if ((c1 == 'O' || c1 == 'o') && (c2 == 'W' || c2 == 'w') && c3 == ' ') {\r\n                while (stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        continue;\r\n                    case 'W':\r\n                    case 'w':\r\n                        return show2SlowWhereCheck(stmt, offset);\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    \r\n    // SHOW @@SYSPARAM\r\n    static int show2sysparam(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"ARAM\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n\r\n            if ((c1 == 'A' || c1 == 'a')  && (c2 == 'R' || c2 == 'r') \r\n            \t\t&& (c3 == 'A' || c3 == 'a') && (c4 == 'M' || c4 == 'm')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return SYSPARAM;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    \r\n    static int show2syslog(String stmt, int offset) {\r\n    \t\r\n    \tif (stmt.length() > offset + \"SLOG\".length()) {    \t\t\r\n    \t\t \r\n    \t\t char c1 = stmt.charAt(++offset);\r\n             char c2 = stmt.charAt(++offset);\r\n             char c3 = stmt.charAt(++offset);\r\n             \r\n             if ( (c1 == 'O' || c1 == 'o') && (c2 == 'G' || c2 == 'g') && c3 == ' ' ) {\r\n            \t \r\n            \t char c4 = stmt.charAt(++offset);\r\n                 char c5 = stmt.charAt(++offset);\r\n                 char c6 = stmt.charAt(++offset);\r\n                 char c7 = stmt.charAt(++offset);\r\n                 char c8 = stmt.charAt(++offset);\r\n                 \r\n                 if ((c4 == 'L' || c4 == 'l') && (c5 == 'I' || c5 == 'i') && (c6 == 'M' || c6 == 'm')\r\n                         && (c7 == 'I' || c7 == 'i') && (c8 == 'T' || c8 == 't')  ) {\r\n                \t \r\n                     while (stmt.length() > ++offset) {\r\n                         switch (stmt.charAt(offset)) {\r\n                         case ' ':\r\n                             continue;\r\n                         case '=':\r\n                             while (stmt.length() > ++offset) {\r\n                                 switch (stmt.charAt(offset)) {\r\n                                 case ' ':\r\n                                     continue;\r\n                                 default:\r\n                                     return (offset << 8) | SYSLOG;\r\n                                 }\r\n                             }\r\n                             return OTHER;\r\n                         default:\r\n                             return OTHER;\r\n                         }\r\n                     }\r\n                 }\r\n\r\n                 return SYSLOG;\r\n             }\r\n    \t}\r\n    \t\r\n    \treturn OTHER; \r\n    }\r\n    \r\n    // SHOW @@SYSPARAM\r\n    // SHOW @@SYSLOG LIMIT=1000\r\n    static int show2SyCheck(String stmt, int offset) {\r\n    \t\r\n    \tif (stmt.length() > offset + \"YS\".length()) {    \t\t\r\n    \t\tchar c1 = stmt.charAt(++offset);\r\n    \t\tchar c2 = stmt.charAt(++offset);\r\n    \t\tif ( c1 == 'S' || c1 == 's' ) {\t    \t\t\r\n    \t\t\tswitch (c2) {\r\n\t            case 'L':\r\n\t            case 'l':\r\n\t                return show2syslog(stmt, offset);\r\n\t            case 'P':\r\n\t            case 'p':\r\n\t                return show2sysparam(stmt, offset);\r\n\t            default:\r\n\t                return OTHER;\r\n\t            }\r\n    \t\t}\r\n    \t}\r\n        return OTHER;    \t\r\n    }\r\n   \r\n    \r\n\r\n    // SHOW @@SLOW WHERE\r\n    static int show2SlowWhereCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"HERE\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            if ((c1 == 'H' || c1 == 'h') && (c2 == 'E' || c2 == 'e') && (c3 == 'R' || c3 == 'r')\r\n                    && (c4 == 'E' || c4 == 'e')) {\r\n                while (stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        continue;\r\n                    case 'D':\r\n                    case 'd':\r\n                        return show2SlowWhereDCheck(stmt, offset);\r\n                    case 'S':\r\n                    case 's':\r\n                        return show2SlowWhereSCheck(stmt, offset);\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@SLOW WHERE DATANODE= XXXXXX\r\n    static int show2SlowWhereDCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"ATANODE\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            char c7 = stmt.charAt(++offset);\r\n            if ((c1 == 'A' || c1 == 'a') && (c2 == 'T' || c2 == 't') && (c3 == 'A' || c3 == 'a')\r\n                    && (c4 == 'N' || c4 == 'n') && (c5 == 'O' || c5 == 'o') && (c6 == 'D' || c6 == 'd')\r\n                    && (c7 == 'E' || c7 == 'e')) {\r\n                while (stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        continue;\r\n                    case '=':\r\n                        while (stmt.length() > ++offset) {\r\n                            switch (stmt.charAt(offset)) {\r\n                            case ' ':\r\n                                continue;\r\n                            default:\r\n                                return (offset << 8) | SLOW_DATANODE;\r\n                            }\r\n                        }\r\n                        return OTHER;\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@SLOW WHERE SCHEMA= XXXXXX\r\n    static int show2SlowWhereSCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"CHEMA\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            if ((c1 == 'C' || c1 == 'c') && (c2 == 'H' || c2 == 'h') && (c3 == 'E' || c3 == 'e')\r\n                    && (c4 == 'M' || c4 == 'm') && (c5 == 'A' || c5 == 'a')) {\r\n                while (stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        continue;\r\n                    case '=':\r\n                        while (stmt.length() > ++offset) {\r\n                            switch (stmt.charAt(offset)) {\r\n                            case ' ':\r\n                                continue;\r\n                            default:\r\n                                return (offset << 8) | SLOW_SCHEMA;\r\n                            }\r\n                        }\r\n                        return OTHER;\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@T\r\n    static int show2TCheck(String stmt, int offset) {\r\n        if (stmt.length() > ++offset) {\r\n            switch (stmt.charAt(offset)) {\r\n            case 'H':\r\n            case 'h':\r\n                return show2ThCheck(stmt, offset);\r\n            case 'I':\r\n            case 'i':\r\n                return show2TiCheck(stmt, offset);\r\n            default:\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@VERSION\r\n    static int show2VCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"ERSION\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            if ((c1 == 'E' || c1 == 'e') && (c2 == 'R' || c2 == 'r') && (c3 == 'S' || c3 == 's')\r\n                    && (c4 == 'I' || c4 == 'i') && (c5 == 'O' || c5 == 'o') && (c6 == 'N' || c6 == 'n')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return VERSION;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    // SHOW @@White  ip白名单\r\n    static int show2WCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"HITE\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            if ((c1 == 'H' || c1 == 'h') && (c2 == 'I' || c2 == 'i') && (c3 == 'T' || c3 == 't')\r\n                    && (c4 == 'E' || c4 == 'e') ) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) == '.') {\r\n                    return show2WhiteCheck(stmt, offset);\r\n                }\r\n                return WHITE_HOST;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    static int show2WhiteCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"set\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n\r\n            if ((c1 == 'S' || c1 == 's') && (c2 == 'E' || c2 == 'e') && (c3 == 'T' || c3 == 't')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) == '=') {\r\n                    return WHITE_HOST_SET;\r\n                }\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    // SHOW @@CO\r\n    static int show2CoCheck(String stmt, int offset) {\r\n        if (stmt.length() > ++offset) {\r\n            switch (stmt.charAt(offset)) {\r\n            case 'M':\r\n            case 'm':\r\n                return show2ComCheck(stmt, offset);\r\n            case 'N':\r\n            case 'n':\r\n                return show2ConCheck(stmt, offset);\r\n            default:\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@DATABASE\r\n    static int show2DataBCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"ASE\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            if ((c1 == 'A' || c1 == 'a') && (c2 == 'S' || c2 == 's') && (c3 == 'E' || c3 == 'e')) {\r\n//                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n//                    return OTHER;\r\n//                }\r\n                return DATABASE;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@DATANODE\r\n    static int show2DataNCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"ODE\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            if ((c1 == 'O' || c1 == 'o') && (c2 == 'D' || c2 == 'd') && (c3 == 'E' || c3 == 'e')) {\r\n                while (stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        continue;\r\n                    case 'W':\r\n                    case 'w':\r\n                        return show2DataNWhereCheck(stmt, offset);\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n                return DATANODE;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@DATANODE WHERE\r\n    static int show2DataNWhereCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"HERE\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            if ((c1 == 'H' || c1 == 'h') && (c2 == 'E' || c2 == 'e') && (c3 == 'R' || c3 == 'r')\r\n                    && (c4 == 'E' || c4 == 'e')) {\r\n                while (stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        continue;\r\n                    case 'S':\r\n                    case 's':\r\n                        return show2DataNWhereSchemaCheck(stmt, offset);\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@DATANODE WHERE SCHEMA = XXXXXX\r\n    static int show2DataNWhereSchemaCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"CHEMA\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            if ((c1 == 'C' || c1 == 'c') && (c2 == 'H' || c2 == 'h') && (c3 == 'E' || c3 == 'e')\r\n                    && (c4 == 'M' || c4 == 'm') && (c5 == 'A' || c5 == 'a')) {\r\n                while (stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        continue;\r\n                    case '=':\r\n                        while (stmt.length() > ++offset) {\r\n                            switch (stmt.charAt(offset)) {\r\n                            case ' ':\r\n                                continue;\r\n                            default:\r\n                                return (offset << 8) | DATANODE_WHERE;\r\n                            }\r\n                        }\r\n                        return OTHER;\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@DATASOURCE\r\n    static int show2DataSCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"OURCE\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            if ((c1 == 'O' || c1 == 'o') && (c2 == 'U' || c2 == 'u') && (c3 == 'R' || c3 == 'r')\r\n                    && (c4 == 'C' || c4 == 'c') && (c5 == 'E' || c5 == 'e')) {\r\n                while (stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        continue;\r\n                    case 'W':\r\n                    case 'w':\r\n                        return show2DataSWhereCheck(stmt, offset);\r\n                    case '.':\r\n                        return show2DataSynCheck(stmt, offset);\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n\r\n                return DATASOURCE;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@DATASOURCE WHERE\r\n    static int show2DataSWhereCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"HERE\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            if ((c1 == 'H' || c1 == 'h') && (c2 == 'E' || c2 == 'e') && (c3 == 'R' || c3 == 'r')\r\n                    && (c4 == 'E' || c4 == 'e')) {\r\n                while (stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        continue;\r\n                    case 'd':\r\n                    case 'D':\r\n                        return show2DataSWhereDatanodeCheck(stmt, offset);\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@DATASOURCE WHERE DATANODE = XXXXXX\r\n    static int show2DataSWhereDatanodeCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"ATANODE\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            char c7 = stmt.charAt(++offset);\r\n            if ((c1 == 'A' || c1 == 'a') && (c2 == 'T' || c2 == 't') && (c3 == 'A' || c3 == 'a')\r\n                    && (c4 == 'N' || c4 == 'n') && (c5 == 'O' || c5 == 'o') && (c6 == 'D' || c6 == 'd')\r\n                    && (c7 == 'E' || c7 == 'e')) {\r\n                while (stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        continue;\r\n                    case '=':\r\n                        while (stmt.length() > ++offset) {\r\n                            switch (stmt.charAt(offset)) {\r\n                            case ' ':\r\n                                continue;\r\n                            default:\r\n                                return (offset << 8) | DATASOURCE_WHERE;\r\n                            }\r\n                        }\r\n                        return OTHER;\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@PARSER\r\n    static int show2PaCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"RSER\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            if ((c1 == 'R' || c1 == 'r') && (c2 == 'S' || c2 == 's') && (c3 == 'E' || c3 == 'e')\r\n                    && (c4 == 'R' || c4 == 'r')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return PARSER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@PROCESSOR\r\n    static int show2PrCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"OCESSOR\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            char c7 = stmt.charAt(++offset);\r\n            if ((c1 == 'O' || c1 == 'o') && (c2 == 'C' || c2 == 'c') && (c3 == 'E' || c3 == 'e')\r\n                    && (c4 == 'S' || c4 == 's') && (c5 == 'S' || c5 == 's') && (c6 == 'O' || c6 == 'o')\r\n                    && (c7 == 'R' || c7 == 'r')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return PROCESSOR;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@SERVER\r\n    // SHOW @@SESSION\r\n    static int show2SeCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"SSION\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            if ((c1 == 'S' || c1 == 's') && (c2 == 'S' || c2 == 's') && (c3 == 'I' || c3 == 'i')\r\n                    && (c4 == 'O' || c4 == 'o') && (c5 == 'N' || c5 == 'n')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return SESSION;\r\n            }\r\n        }\r\n        else if (stmt.length() > offset + \"RVER\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            if ((c1 == 'R' || c1 == 'r') && (c2 == 'V' || c2 == 'v') && (c3 == 'E' || c3 == 'e')\r\n                    && (c4 == 'R' || c4 == 'r')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return SERVER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@THREADPOOL\r\n    static int show2ThCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"READPOOL\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            char c7 = stmt.charAt(++offset);\r\n            char c8 = stmt.charAt(++offset);\r\n            if ((c1 == 'R' || c1 == 'r') && (c2 == 'E' || c2 == 'e') && (c3 == 'A' || c3 == 'a')\r\n                    && (c4 == 'D' || c4 == 'd') && (c5 == 'P' || c5 == 'p') && (c6 == 'O' || c6 == 'o')\r\n                    && (c7 == 'O' || c7 == 'o') && (c8 == 'L' || c8 == 'l')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return THREADPOOL;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@TIME.\r\n    static int show2TiCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"ME.\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            if ((c1 == 'M' || c1 == 'm') && (c2 == 'E' || c2 == 'e') && (c3 == '.')\r\n                    && (stmt.length() > ++offset)) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case 'C':\r\n                    case 'c':\r\n                        return show2TimeCCheck(stmt, offset);\r\n                    case 'S':\r\n                    case 's':\r\n                        return show2TimeSCheck(stmt, offset);\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@COMMAND\r\n    static int show2ComCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"MAND\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            if ((c1 == 'M' || c1 == 'm') && (c2 == 'A' || c2 == 'a') && (c3 == 'N' || c3 == 'n')\r\n                    && (c4 == 'D' || c4 == 'd')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return COMMAND;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@CONNECTION\r\n    static int show2ConCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"NECTION\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            char c7 = stmt.charAt(++offset);\r\n            if ((c1 == 'N' || c1 == 'n') && (c2 == 'E' || c2 == 'e') && (c3 == 'C' || c3 == 'c')\r\n                    && (c4 == 'T' || c4 == 't') && (c5 == 'I' || c5 == 'i') && (c6 == 'O' || c6 == 'o')\r\n                    && (c7 == 'N' || c7 == 'n')) {\r\n                if (stmt.length() > ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        return CONNECTION;\r\n                    case '.':\r\n                        return show2ConnectonSQL(stmt, offset);\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n                return CONNECTION;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@CONNECTION.SQL\r\n    static int show2ConnectonSQL(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"SQL\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            if ((c1 == 'S' || c1 == 's') && (c2 == 'Q' || c2 == 'q') && (c3 == 'L' || c3 == 'l')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return CONNECTION_SQL;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@TIME.CURRENT\r\n    static int show2TimeCCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"URRENT\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            if ((c1 == 'U' || c1 == 'u') && (c2 == 'R' || c2 == 'r') && (c3 == 'R' || c3 == 'r')\r\n                    && (c4 == 'E' || c4 == 'e') && (c5 == 'N' || c5 == 'n') && (c6 == 'T' || c6 == 't')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return TIME_CURRENT;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@TIME.STARTUP\r\n    static int show2TimeSCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"TARTUP\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            if ((c1 == 'T' || c1 == 't') && (c2 == 'A' || c2 == 'a') && (c3 == 'R' || c3 == 'r')\r\n                    && (c4 == 'T' || c4 == 't') && (c5 == 'U' || c5 == 'u') && (c6 == 'P' || c6 == 'p')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return TIME_STARTUP;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@SQ\r\n    static int show2SqCheck(String stmt, int offset) {\r\n        if (stmt.length() > ++offset) {\r\n            switch (stmt.charAt(offset)) {\r\n            case 'L':\r\n            case 'l':\r\n                return show2SqlCheck(stmt, offset);\r\n            default:\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@SQL\r\n    static int show2SqlCheck(String stmt, int offset) {\r\n        if (stmt.length() > ++offset) {\r\n            switch (stmt.charAt(offset)) {\r\n            case '.':\r\n                return show2SqlDotCheck(stmt, offset);\r\n            case ' ':\r\n                return show2SqlBlankCheck(stmt, offset);\r\n            default:            \t          \t\r\n            \treturn SQL;\r\n            }\r\n        } else {\r\n        \treturn SQL;\r\n        }\r\n    }\r\n\r\n    // SHOW @@SQL.\r\n    static int show2SqlDotCheck(String stmt, int offset) {\r\n        if (stmt.length() > ++offset) {\r\n            switch (stmt.charAt(offset)) {\r\n            case 'D':\r\n            case 'd':\r\n                return show2SqlDCheck(stmt, offset);\r\n            case 'E':\r\n            case 'e':\r\n                return show2SqlECheck(stmt, offset);\r\n            case 'S':\r\n            case 's':\r\n            \tchar c1 = stmt.charAt(++offset);\r\n            \tswitch (c1) {\r\n            \tcase 'L':\r\n            \tcase 'l':\r\n                \treturn show2SqlSLCheck(stmt, offset);\r\n            \tcase 'U':\r\n            \tcase 'u':\r\n            \t\treturn show2SqlSUCheck(stmt, offset);\r\n            \t}\r\n            case 'H':\r\n            case 'h':\r\n            \treturn show2SqlHCheck(stmt, offset);\r\n            case 'L':\r\n            case 'l':\r\n            \treturn show2SqlLCheck(stmt, offset);\r\n            case 'C':\r\n            case 'c':\r\n            \treturn show2SqlCCheck(stmt, offset);\r\n            case 'R':\r\n            case 'r':\r\n            \treturn show2SqlRCheck(stmt, offset);\r\n            default:\r\n                return OTHER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@SQL WHERE ID = XXXXXX\r\n    static int show2SqlBlankCheck(String stmt, int offset) {\r\n        for (++offset; stmt.length() > offset;) {\r\n            switch (stmt.charAt(offset)) {\r\n            case ' ':\r\n            \treturn SQL;\r\n            case 'W':\r\n            case 'w':\r\n                if (isWhere(stmt, offset)) {\r\n                    return SQL;\r\n                } else {\r\n                    return OTHER;\r\n                }\r\n            default:\r\n            \treturn (offset << 8) | SQL;\r\n            }\r\n        }\r\n\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@SQL.DETAIL WHERE ID = XXXXXX\r\n    static int show2SqlDCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"ETAIL\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            if ((c1 == 'E' || c1 == 'e') && (c2 == 'T' || c2 == 't') && (c3 == 'A' || c3 == 'a')\r\n                    && (c4 == 'I' || c4 == 'i') && (c5 == 'L' || c5 == 'l')) {\r\n                for (++offset; stmt.length() > offset; ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        continue;\r\n                    case 'W':\r\n                    case 'w':\r\n                        if (isWhere(stmt, offset)) {\r\n                            return SQL_DETAIL;\r\n                        } else {\r\n                            return OTHER;\r\n                        }\r\n                    default:\r\n                        return OTHER;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@SQL.EXECUTE\r\n    static int show2SqlECheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"XECUTE\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            if ((c1 == 'X' || c1 == 'x') && (c2 == 'E' || c2 == 'e') && (c3 == 'C' || c3 == 'c')\r\n                    && (c4 == 'U' || c4 == 'u') && (c5 == 'T' || c5 == 't') && (c6 == 'E' || c6 == 'e')) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return SQL_EXECUTE;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n\r\n    // SHOW @@SQL.SLOW\r\n    static int show2SqlSLCheck(String stmt, int offset) {\r\n        if (stmt.length() > offset + \"OW\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            if ((c1 == 'O' || c1 == 'o') && (c2 == 'W' || c2 == 'w')) {\r\n            \t\r\n            \twhile (stmt.length() > ++offset) {\r\n\t           \t\t switch (stmt.charAt(offset)) {\r\n\t           \t\t case ' ':\r\n\t                     continue;\r\n\t                 default:\r\n\t                   \t return (offset << 8) | SQL_SLOW;\t \r\n\t           \t\t }\r\n\t           \t}\r\n            \t\r\n                return SQL_SLOW;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    \r\n    // SHOW @@SQL.HIGH\r\n    static int show2SqlHCheck(String stmt, int offset) {\r\n    \t\r\n    \tif (stmt.length() > offset + \"IGH\".length()) {\r\n    \t\tchar c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            if ((c1 == 'I' || c1 == 'i') && (c2 == 'G' || c2 == 'g') && (c3 == 'H' || c3 == 'h') ) {\r\n            \t\r\n            \twhile (stmt.length() > ++offset) {\r\n            \t\t switch (stmt.charAt(offset)) {\r\n            \t\t case ' ':\r\n                         continue;\r\n                     default:\r\n                    \t return (offset << 8) | SQL_HIGH;\t \r\n            \t\t }\r\n            \t}\r\n            \t\r\n                return SQL_HIGH;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    \r\n    // SHOW @@SQL.RESULTSET\r\n    static int show2SqlRCheck(String stmt, int offset) {\r\n    \t\r\n    \tif (stmt.length() > offset + \"ESULTSET\".length()) {\r\n    \t\tchar c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            char c7 = stmt.charAt(++offset);\r\n            char c8 = stmt.charAt(++offset);\r\n            if ((c1 == 'E' || c1 == 'e') && (c2 == 'S' || c2 == 's') && (c3 == 'U' || c3 == 'u')&&\r\n            \t\t(c4 == 'l' || c4 == 'i') && (c5 == 'T' || c5 == 't') && (c6 == 'S' || c6 == 's')&&\r\n            \t\t(c7 == 'E' || c7 == 'e') && (c8 == 'T' || c8 == 't') ) {\r\n            \t\r\n            \twhile (stmt.length() > ++offset) {\r\n            \t\t switch (stmt.charAt(offset)) {\r\n            \t\t case ' ':\r\n                         continue;\r\n                     default:\r\n                    \t return (offset << 8) | SQL_RESULTSET;\t \r\n            \t\t }\r\n            \t}\r\n            \t\r\n                return SQL_RESULTSET;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    \r\n    // SHOW @@SQL.LARGE\r\n    static int show2SqlLCheck(String stmt, int offset) {\r\n    \t\r\n    \tif (stmt.length() > offset + \"ARGE\".length()) {\r\n    \t\tchar c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            if ((c1 == 'A' || c1 == 'a') && (c2 == 'R' || c2 == 'r') && (c3 == 'G' || c3 == 'g') && (c4 == 'E' || c4 == 'e') ) {\r\n            \t\r\n            \twhile (stmt.length() > ++offset) {\r\n            \t\t switch (stmt.charAt(offset)) {\r\n            \t\t case ' ':\r\n                         continue;\r\n                     default:\r\n                    \t return (offset << 8) | SQL_LARGE;\t \r\n            \t\t }\r\n            \t}\r\n            \t\r\n                return SQL_LARGE;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    \r\n    // SHOW @@sql.condition\r\n    static int show2SqlCCheck(String stmt, int offset) {\r\n    \t\r\n    \tif (stmt.length() > offset + \"ONDITION\".length()) {\r\n    \t\tchar c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            char c6 = stmt.charAt(++offset);\r\n            char c7 = stmt.charAt(++offset);\r\n            char c8 = stmt.charAt(++offset);\r\n            if ( (c1 == 'O' || c1 == 'o') && (c2 == 'N' || c2 == 'n') && (c3 == 'D' || c3 == 'd') &&\r\n            \t\t(c4 == 'I' || c4 == 'i') && (c5 == 'T' || c5 == 't') && (c6 == 'I' || c6 == 'i') &&\r\n            \t\t(c7 == 'O' || c7 == 'o') && (c8 == 'N' || c8 == 'n') ) {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\r\n                    return OTHER;\r\n                }\r\n                return SQL_CONDITION;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    \r\n    // SHOW @@SQL.SUM\r\n    static int show2SqlSUCheck(String stmt, int offset) {\r\n    \tif (stmt.length() > offset + \"M\".length()) {\r\n            char c1 = stmt.charAt(++offset);\r\n            if ( c1 == 'M' || c1 == 'm') {\r\n                if (stmt.length() > ++offset && stmt.charAt(offset) == '.') {\r\n                \t\r\n                \t/**\r\n                \t *  TODO: modify by zhuam\r\n                \t *   \r\n                \t *  兼容之前指令\r\n                \t *  在保留 SHOW @@SQL.SUM 指令的同时， 扩展支持  SHOW @@SQL.SUM.TABLE 、 SHOW @@SQL.SUM.USER   \r\n                \t */       \t\r\n                \tif ( stmt.length() > (offset+4) ) {                \t\t\r\n\t                \tchar c2 = stmt.charAt(++offset);\r\n\t                \tchar c3 = stmt.charAt(++offset);\r\n\t                \tchar c4 = stmt.charAt(++offset);\r\n\t                \tchar c5 = stmt.charAt(++offset);\t                \t\r\n\t                \t\r\n\t                \tif ( (c2 == 'U' || c2 == 'u') && (c3 == 'S' || c3 == 's')\r\n\t                \t  && (c4 == 'E' || c4 == 'e') && (c5 == 'R' || c5 == 'r') ) {\t\r\n\t                \t\treturn SQL_SUM_USER;\r\n\t                \t\t\r\n\t                \t} else if ( (c2 == 'T' || c2 == 't') && (c3 == 'A' || c3 == 'a')\r\n\t\t\t\t             \t && (c4 == 'B' || c4 == 'b') && (c5 == 'L' || c5 == 'l')\r\n                                && stmt.length() > (offset+1)) {\r\n\r\n\t                \t\t\t char c6 = stmt.charAt(++offset);\r\n\t                \t\t\t if ( c6 == 'E' || c6 == 'e') {\r\n\t                \t\t\t\t \r\n                \t            \twhile (stmt.length() > ++offset) {\r\n                \t\t           \t\t switch (stmt.charAt(offset)) {\r\n                \t\t           \t\t case ' ':\r\n                \t\t                     continue;\r\n                \t\t                 default:\r\n                \t\t                   \t return (offset << 8) | SQL_SUM_TABLE;\t \r\n                \t\t           \t\t }\r\n                \t\t           \t}\r\n\t \r\n\t                \t\t\t\treturn SQL_SUM_TABLE;\r\n\t                \t\t\t }\r\n\t                \t}\r\n\t                \t\r\n                \t} \r\n                \t\r\n                    return OTHER;\r\n                }\r\n                \r\n            \twhile (stmt.length() > ++offset) {\r\n              \t\t switch (stmt.charAt(offset)) {\r\n              \t\t case ' ':\r\n                           continue;\r\n                       default:\r\n                      \t return (offset << 8) | SQL_SUM_USER;\t \r\n              \t\t }\r\n                 \t}\r\n                return SQL_SUM_USER;\r\n            }\r\n        }\r\n        return OTHER;\r\n    }\r\n    \r\n\r\n    static boolean isWhere(String stmt, int offset) {\r\n        if (stmt.length() > offset + 5) {\r\n            char c1 = stmt.charAt(++offset);\r\n            char c2 = stmt.charAt(++offset);\r\n            char c3 = stmt.charAt(++offset);\r\n            char c4 = stmt.charAt(++offset);\r\n            char c5 = stmt.charAt(++offset);\r\n            if ((c1 == 'H' || c1 == 'h') && (c2 == 'E' || c2 == 'e') && (c3 == 'R' || c3 == 'r')\r\n                    && (c4 == 'E' || c4 == 'e') && (c5 == ' ')) {\r\n                boolean jump1 = false;\r\n                for (++offset; stmt.length() > offset && !jump1; ++offset) {\r\n                    switch (stmt.charAt(offset)) {\r\n                    case ' ':\r\n                        continue;\r\n                    case 'I':\r\n                    case 'i':\r\n                        jump1 = true;\r\n                        break;\r\n                    default:\r\n                        return false;\r\n                    }\r\n                }\r\n                if ((stmt.length() > offset) && (stmt.charAt(offset) == 'D' || stmt.charAt(offset) == 'd')) {\r\n                    boolean jump2 = false;\r\n                    for (++offset; stmt.length() > offset && !jump2; ++offset) {\r\n                        switch (stmt.charAt(offset)) {\r\n                        case ' ':\r\n                            continue;\r\n                        case '=':\r\n                            jump2 = true;\r\n                            break;\r\n                        default:\r\n                            return false;\r\n                        }\r\n                    }\r\n                    return isSqlId(stmt, offset);\r\n                }\r\n            }\r\n        }\r\n        return false;\r\n    }\r\n\r\n    static boolean isSqlId(String stmt, int offset) {\r\n        String id = stmt.substring(offset).trim();\r\n        try {\r\n            Long.parseLong(id);\r\n        } catch (Exception e) {\r\n            return false;\r\n        }\r\n        return true;\r\n    }\r\n\r\n    public static String getWhereParameter(String stmt) {\r\n        int offset = stmt.indexOf('=');\r\n        ++offset;\r\n        return stmt.substring(offset).trim();\r\n    }\r\n\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/ManagerParseStop.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser;\n\nimport io.mycat.route.parser.util.Pair;\nimport io.mycat.route.parser.util.ParseUtil;\nimport io.mycat.util.SplitUtil;\n\n/**\n * @author mycat\n */\npublic final class ManagerParseStop {\n\n    public static final int OTHER = -1;\n    public static final int HEARTBEAT = 1;\n\n    public static int parse(String stmt, int offset) {\n        int i = offset;\n        for (; i < stmt.length(); i++) {\n            switch (stmt.charAt(i)) {\n            case ' ':\n                continue;\n            case '/':\n            case '#':\n                i = ParseUtil.comment(stmt, i);\n                continue;\n            case '@':\n                return stop2Check(stmt, i);\n            default:\n                return OTHER;\n            }\n        }\n        return OTHER;\n    }\n\n    public static Pair<String[], Integer> getPair(String stmt) {\n        int offset = stmt.indexOf(\"@@\");\n        String s = stmt.substring(offset + 11).trim();\n        int p1 = s.lastIndexOf(':');\n        if (p1 == -1) {\n            String[] src = SplitUtil.split(s, ',', '$', '-', '[', ']');\n            return new Pair<String[], Integer>(src, null);\n        } else {\n            String[] src = SplitUtil.split(s, ':', true);\n            String[] src1 = SplitUtil.split(src[0], ',', '$', '-', '[', ']');\n            return new Pair<String[], Integer>(src1, Integer.valueOf(src[1]));\n        }\n    }\n\n    // HEARTBEAT\n    static int stop2Check(String stmt, int offset) {\n        if (stmt.length() > ++offset && stmt.charAt(offset) == '@'\n                && stmt.length() > offset + 9) {\n                char c1 = stmt.charAt(++offset);\n                char c2 = stmt.charAt(++offset);\n                char c3 = stmt.charAt(++offset);\n                char c4 = stmt.charAt(++offset);\n                char c5 = stmt.charAt(++offset);\n                char c6 = stmt.charAt(++offset);\n                char c7 = stmt.charAt(++offset);\n                char c8 = stmt.charAt(++offset);\n                char c9 = stmt.charAt(++offset);\n                if ((c1 == 'H' || c1 == 'h') && (c2 == 'E' || c2 == 'e') && (c3 == 'A' || c3 == 'a')\n                        && (c4 == 'R' || c4 == 'r') && (c5 == 'T' || c5 == 't') && (c6 == 'B' || c6 == 'b')\n                        && (c7 == 'E' || c7 == 'e') && (c8 == 'A' || c8 == 'a') && (c9 == 'T' || c9 == 't')) {\n                    if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\n                        return OTHER;\n                    }\n                    return HEARTBEAT;\n                }\n        }\n        return OTHER;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/ManagerParseSwitch.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser;\n\nimport io.mycat.route.parser.util.Pair;\nimport io.mycat.route.parser.util.ParseUtil;\nimport io.mycat.util.SplitUtil;\n\n/**\n * @author mycat\n */\npublic final class ManagerParseSwitch {\n\n    public static final int OTHER = -1;\n    public static final int DATASOURCE = 1;\n\n    public static int parse(String stmt, int offset) {\n        int i = offset;\n        for (; i < stmt.length(); i++) {\n            switch (stmt.charAt(i)) {\n            case ' ':\n                continue;\n            case '/':\n            case '#':\n                i = ParseUtil.comment(stmt, i);\n                continue;\n            case '@':\n                return switch2Check(stmt, i);\n            default:\n                return OTHER;\n            }\n        }\n        return OTHER;\n    }\n\n    public static Pair<String[], Integer> getPair(String stmt) {\n        int offset = stmt.indexOf(\"@@\");\n        String s = stmt.substring(offset + 12).trim();\n        int p1 = s.lastIndexOf(':');\n        if (p1 == -1) {\n            String[] src = SplitUtil.split(s, ',', '$', '-', '[', ']');\n            return new Pair<String[], Integer>(src, null);\n        } else {\n            String[] src = SplitUtil.split(s, ':', true);\n            String[] src1 = SplitUtil.split(src[0], ',', '$', '-', '[', ']');\n            return new Pair<String[], Integer>(src1, Integer.valueOf(src[1]));\n        }\n    }\n\n    // DATASOURCE\n    static int switch2Check(String stmt, int offset) {\n        if (stmt.length() > ++offset && stmt.charAt(offset) == '@'\n                && stmt.length() > offset + 10) {\n                char c1 = stmt.charAt(++offset);\n                char c2 = stmt.charAt(++offset);\n                char c3 = stmt.charAt(++offset);\n                char c4 = stmt.charAt(++offset);\n                char c5 = stmt.charAt(++offset);\n                char c6 = stmt.charAt(++offset);\n                char c7 = stmt.charAt(++offset);\n                char c8 = stmt.charAt(++offset);\n                char c9 = stmt.charAt(++offset);\n                char c10 = stmt.charAt(++offset);\n                if ((c1 == 'D' || c1 == 'd') && (c2 == 'A' || c2 == 'a') && (c3 == 'T' || c3 == 't')\n                        && (c4 == 'A' || c4 == 'a') && (c5 == 'S' || c5 == 's') && (c6 == 'O' || c6 == 'o')\n                        && (c7 == 'U' || c7 == 'u') && (c8 == 'R' || c8 == 'r') && (c9 == 'C' || c9 == 'c')\n                        && (c10 == 'E' || c10 == 'e')) {\n                    if (stmt.length() > ++offset && stmt.charAt(offset) != ' ') {\n                        return OTHER;\n                    }\n                    return DATASOURCE;\n                }\n        }\n        return OTHER;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/DruidParser.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport java.sql.SQLNonTransientException;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\n\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.route.RouteResultset;\n\n/**\n * 对SQLStatement解析\n * 主要通过visitor解析和statement解析：有些类型的SQLStatement通过visitor解析足够了，\n *  有些只能通过statement解析才能得到所有信息\n *  有些需要通过两种方式解析才能得到完整信息\n * @author wang.dw\n *\n */\npublic interface DruidParser {\n\t/**\n\t * 使用MycatSchemaStatVisitor解析,得到tables、tableAliasMap、conditions等\n\t * @param schema\n\t * @param stmt\n\t */\n\tpublic void parser(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt, String originSql,LayerCachePool cachePool,MycatSchemaStatVisitor schemaStatVisitor) throws SQLNonTransientException;\n\t\n\t/**\n\t * statement方式解析\n\t * 子类可覆盖（如果visitorParse解析得不到表名、字段等信息的，就通过覆盖该方法来解析）\n\t * 子类覆盖该方法一般是将SQLStatement转型后再解析（如转型为MySqlInsertStatement）\n\t */\n\tpublic void statementParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt) throws SQLNonTransientException;\n\n\t/**\n\t * 子类可覆盖（如果该方法解析得不到表名、字段等信息的，就覆盖该方法，覆盖成空方法，然后通过statementPparse去解析）\n\t * 通过visitor解析：有些类型的Statement通过visitor解析得不到表名、\n\t * @param stmt\n\t */\n\tpublic void visitorParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt, MycatSchemaStatVisitor visitor)\n\t\t\tthrows SQLNonTransientException;\n\t\n\t/**\n\t * 改写sql：加limit，加group by、加order by如有些没有加limit的可以通过该方法增加\n\t * @param schema\n\t * @param rrs\n\t * @param stmt\n\t * @throws SQLNonTransientException\n\t */\n\tpublic void changeSql(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt,LayerCachePool cachePool) throws SQLNonTransientException;\n\t/**\n\t * 获取解析到的信息\n\t * @return\n\t */\n\tpublic DruidShardingParseInfo getCtx();\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/DruidParserFactory.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLAlterTableStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLockTableStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;\nimport com.alibaba.druid.sql.visitor.SchemaStatVisitor;\n\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.route.parser.druid.impl.DefaultDruidParser;\nimport io.mycat.route.parser.druid.impl.DruidAlterTableParser;\nimport io.mycat.route.parser.druid.impl.DruidCreateTableParser;\nimport io.mycat.route.parser.druid.impl.DruidDeleteParser;\nimport io.mycat.route.parser.druid.impl.DruidInsertParser;\nimport io.mycat.route.parser.druid.impl.DruidLockTableParser;\nimport io.mycat.route.parser.druid.impl.DruidSelectDb2Parser;\nimport io.mycat.route.parser.druid.impl.DruidSelectOracleParser;\nimport io.mycat.route.parser.druid.impl.DruidSelectParser;\nimport io.mycat.route.parser.druid.impl.DruidSelectPostgresqlParser;\nimport io.mycat.route.parser.druid.impl.DruidSelectSqlServerParser;\nimport io.mycat.route.parser.druid.impl.DruidUpdateParser;\n\n/**\n * DruidParser的工厂类\n *\n * @author wdw\n */\npublic class DruidParserFactory\n{\n\n    public static DruidParser create(SchemaConfig schema, SQLStatement statement, SchemaStatVisitor visitor)\n    {\n        DruidParser parser = null;\n        if (statement instanceof SQLSelectStatement)\n        {\n            if(schema.isNeedSupportMultiDBType())\n            {\n                parser = getDruidParserForMultiDB(schema, statement, visitor);\n\n            }\n\n            if (parser == null)\n            {\n                parser = new DruidSelectParser();\n            }\n        } else if (statement instanceof MySqlInsertStatement)\n        {\n            parser = new DruidInsertParser();\n        } else if (statement instanceof MySqlDeleteStatement)\n        {\n            parser = new DruidDeleteParser();\n        } else if (statement instanceof MySqlCreateTableStatement)\n        {\n            parser = new DruidCreateTableParser();\n        } else if (statement instanceof MySqlUpdateStatement)\n        {\n            parser = new DruidUpdateParser();\n        } else if (statement instanceof SQLAlterTableStatement)\n        {\n            parser = new DruidAlterTableParser();\n        } else if (statement instanceof MySqlLockTableStatement) {\n        \tparser = new DruidLockTableParser();\n        } else\n        {\n            parser = new DefaultDruidParser();\n        }\n\n        return parser;\n    }\n\n    private static DruidParser getDruidParserForMultiDB(SchemaConfig schema, SQLStatement statement, SchemaStatVisitor visitor)\n    {\n        DruidParser parser=null;\n        //先解出表，判断表所在db的类型，再根据不同db类型返回不同的解析\n        /**\n         * 不能直接使用visitor变量，防止污染后续sql解析\n         * @author SvenAugustus\n         */\n\t\tMycatSchemaStatVisitor _visitor = SchemaStatVisitorFactory.create(schema);\n        List<String> tables = parseTables(statement, _visitor);\n        for (String table : tables)\n        {\n            Set<String> dbTypes =null;\n            TableConfig tableConfig = schema.getTables().get(table);\n            if(tableConfig==null)\n            {\n                dbTypes=new HashSet<>();\n                dbTypes.add(schema.getDefaultDataNodeDbType())  ;\n            }else\n            {\n                dbTypes = tableConfig.getDbTypes();\n            }\n            if (dbTypes.contains(\"oracle\"))\n            {\n                parser = new DruidSelectOracleParser();\n                ((DruidSelectOracleParser)parser).setInvocationHandler(SqlMethodInvocationHandlerFactory.getForOracle());\n                break;\n            } else if (dbTypes.contains(\"db2\"))\n            {\n                parser = new DruidSelectDb2Parser();\n                break;\n            } else if (dbTypes.contains(\"sqlserver\"))\n            {\n                parser = new DruidSelectSqlServerParser();\n                break;\n            } else if (dbTypes.contains(\"postgresql\"))\n            {\n                parser = new DruidSelectPostgresqlParser();\n                ((DruidSelectPostgresqlParser)parser).setInvocationHandler(SqlMethodInvocationHandlerFactory.getForPgsql());\n                break;\n            }\n        }\n        return parser;\n    }\n\n\n\tprivate static List<String> parseTables(SQLStatement stmt, MycatSchemaStatVisitor schemaStatVisitor)\n    {\n        List<String> tables = new ArrayList<>();\n        stmt.accept(schemaStatVisitor);\n\n        if (schemaStatVisitor.getAliasMap() != null)\n        {\n            for (Map.Entry<String, String> entry : schemaStatVisitor.getAliasMap().entrySet())\n            {\n                String key = entry.getKey();\n                String value = entry.getValue();\n                if (value != null && value.indexOf(\"`\") >= 0)\n                {\n                    value = value.replaceAll(\"`\", \"\");\n                }\n                //表名前面带database的，去掉\n                if (key != null)\n                {\n                    int pos = key.indexOf(\"`\");\n                    if (pos > 0)\n                    {\n                        key = key.replaceAll(\"`\", \"\");\n                    }\n                    pos = key.indexOf(\".\");\n                    if (pos > 0)\n                    {\n                        key = key.substring(pos + 1);\n                    }\n\n\t\t\t\t\t// if (key.equalsIgnoreCase(value))\n\t\t\t\t\t// {\n\t\t\t\t\ttables.add(value.toUpperCase());\n\t\t\t\t\t// }\n                }\n            }\n\n        }\n        return tables;\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/DruidSequenceHandler.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport java.io.UnsupportedEncodingException;\nimport java.lang.reflect.Constructor;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.SessionSQLPair;\nimport io.mycat.route.sequence.handler.*;\nimport io.mycat.util.TimeUtil;\n\n/**\n * 使用Druid解析器实现对Sequence处理\n *\n * @author 兵临城下\n * @date 2015/03/13\n */\npublic class DruidSequenceHandler {\n    private final SequenceHandler sequenceHandler;\n\n    /**\n     * 分段锁\n     */\n    private final static Map<String,ReentrantLock> segmentLock = new ConcurrentHashMap<>();\n\n    /**\n     * 获取MYCAT SEQ的匹配语句\n     */\n    private final static String MATCHED_FEATURE = \"NEXT VALUE FOR MYCATSEQ_\";\n\n    private final  Pattern pattern;\n\n    public DruidSequenceHandler(int seqHandlerType,String sequnceHandlerPattern) {\n      this.pattern =  Pattern.compile(sequnceHandlerPattern, Pattern.CASE_INSENSITIVE);\n        switch (seqHandlerType) {\n            case SystemConfig.SEQUENCEHANDLER_MYSQLDB:\n                sequenceHandler = IncrSequenceMySQLHandler.getInstance();\n                break;\n            case SystemConfig.SEQUENCEHANDLER_LOCALFILE:\n                sequenceHandler = IncrSequencePropHandler.getInstance();\n                break;\n            case SystemConfig.SEQUENCEHANDLER_LOCAL_TIME:\n                sequenceHandler = IncrSequenceTimeHandler.getInstance();\n                break;\n            case SystemConfig.SEQUENCEHANDLER_ZK_DISTRIBUTED:\n                sequenceHandler = DistributedSequenceHandler.getInstance(MycatServer.getInstance().getConfig().getSystem());\n                break;\n            case SystemConfig.SEQUENCEHANDLER_ZK_GLOBAL_INCREMENT:\n                sequenceHandler = IncrSequenceZKHandler.getInstance();\n                break;\n            case SystemConfig.SEQUENCEHANDLER_DEF_GLOBAL_INCREMENT:\n                try {\n                    String sequenceHanlderClass = MycatServer.getInstance().getConfig().getSystem().getSequenceHanlderClass();\n                    Class<?> aClass = Class.forName(sequenceHanlderClass);\n                    Constructor constructor=aClass.getConstructor();\n                    sequenceHandler  =(SequenceHandler) constructor.newInstance();\n                }catch (Exception e){\n                    e.printStackTrace();\n                    throw new RuntimeException(e);\n                }\n                break;\n            default:\n                throw new java.lang.IllegalArgumentException(\"Invalid sequnce handler type \" + seqHandlerType);\n        }\n    }\n\n    /**\n     * 根据原sql获取可执行的sql\n     *\n     * @param sql\n     * @return\n     * @throws UnsupportedEncodingException\n     */\n    public String getExecuteSql(SessionSQLPair pair, String charset) throws UnsupportedEncodingException,InterruptedException {\n    \tString executeSql = pair.sql;\n        if (null != pair.sql && !\"\".equals(pair.sql)) {\n            Matcher matcher = pattern.matcher(executeSql);\n            if(matcher.find()){\n            \tString tableName = matcher.group(2);\n                ReentrantLock lock = getSegLock(tableName);\n\t\t\t\tlock.lock();\n\t\t\t\ttry {\n                \tmatcher = pattern.matcher(executeSql);\n                \twhile(matcher.find()){\n                \t\tlong value = sequenceHandler.nextId(tableName.toUpperCase());\n                        executeSql = executeSql.replaceFirst(matcher.group(1), Long.toString(value));\n                        pair.session.getSource().setLastWriteTime(TimeUtil.currentTimeMillis());\n                    }\n\t\t\t\t} finally {\n\t\t\t\t\tlock.unlock();\n\t\t\t\t}\n            }\n        }\n        return executeSql;\n    }\n\n    /*\n     * 获取分段锁\n     * @param name\n     * @return\n     */\n    private ReentrantLock getSegLock(String name){\n    \tReentrantLock lock = segmentLock.get(name);\n    \tif(lock==null){\n    \t\tsynchronized (segmentLock) {\n    \t\t\tlock = segmentLock.get(name);\n\t\t\t\tif(lock==null){\n\t\t\t\t\tlock = new ReentrantLock();\n\t\t\t\t\tsegmentLock.put(name, lock);\n\t\t\t\t}\n\t\t\t}\n    \t}\n    \treturn lock;\n    }\n\n\n    //just for test\n    public String getTableName(String sql) {\n        Matcher matcher = pattern.matcher(sql);\n        if (matcher.find()) {\n            return matcher.group(2);\n        }\n        return null;\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/DruidShardingParseInfo.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.druid.sql.visitor.SchemaStatVisitor;\nimport com.alibaba.druid.stat.TableStat;\nimport com.alibaba.druid.stat.TableStat.Name;\n\nimport io.mycat.route.util.RouterUtil;\n\n/**\n * druid parser result\n * @author wang.dw\n *\n */\npublic class DruidShardingParseInfo {\n\t/**\n\t * 一个sql中可能有多个WhereUnit（如子查询中的where可能导致多个）\n\t */\n\tprivate List<WhereUnit> whereUnits = new ArrayList<WhereUnit>();\n\t\n\tprivate List<RouteCalculateUnit> routeCalculateUnits = new ArrayList<RouteCalculateUnit>();\n\t\n\t/**\n\t * （共享属性）\n\t */\n\tprivate String sql = \"\";\n\t\n\t//tables为路由计算共享属性，多组RouteCalculateUnit使用同样的tables\n\tprivate List<String> tables = new ArrayList<String>();\n\t\n//\tprivate RouteCalculateUnit routeCalculateUnit = new RouteCalculateUnit(this); \n\n\t/**\n\t * key table alias, value talbe realname;\n\t */\n\tprivate Map<String, String> tableAliasMap = new LinkedHashMap<String, String>();\n\n\tprivate SchemaStatVisitor visitor;\n\n\tpublic Map<String, String> getTableAliasMap() {\n\t\treturn tableAliasMap;\n\t}\n\n\tpublic void setTableAliasMap(Map<String, String> tableAliasMap) {\n\t\tthis.tableAliasMap = tableAliasMap;\n\t}\n\n\tpublic String getSql() {\n\t\treturn sql;\n\t}\n\n\tpublic void setSql(String sql) {\n\t\tthis.sql = sql;\n\t}\n\n\tpublic List<String> getTables() {\n\t\treturn tables;\n\t}\n\n\tpublic void addTable(String tableName) {\n\t\tthis.tables.add(tableName);\n\t}\n\n\tpublic RouteCalculateUnit getRouteCalculateUnit() {\n\t\treturn routeCalculateUnits.get(0);\n\t}\n\t\n\tpublic List<RouteCalculateUnit> getRouteCalculateUnits() {\n\t\treturn routeCalculateUnits;\n\t}\n\t\n\tpublic void setRouteCalculateUnits(List<RouteCalculateUnit> routeCalculateUnits) {\n\t\tthis.routeCalculateUnits = routeCalculateUnits;\n\t}\n\t\n\tpublic void addRouteCalculateUnit(RouteCalculateUnit routeCalculateUnit) {\n\t\tthis.routeCalculateUnits.add(routeCalculateUnit);\n\t}\n\t\n\n\tpublic void clear() {\n\t\tfor(RouteCalculateUnit unit : routeCalculateUnits ) {\n\t\t\tunit.clear();\n\t\t}\n\t}\n\t\n\tpublic void setVisitor(SchemaStatVisitor visitor) {\n\t\t\n\t\tthis.visitor = visitor;\n\t}\n\t\n\tpublic SchemaStatVisitor getVisitor(){\n\t\t\n\t\treturn this.visitor;\n\t}\n\n\tpublic void addTables(Map<Name, TableStat> map, String sessionSchema) {\n\t\t\n\t\tint dotIndex;\n\t\tfor(Name _name : map.keySet()){\n\t\t\t\n\t\t\tString _tableName = _name.getName().toString().toUpperCase();\n\t\t\t//系统表直接跳过，路由到默认datanode\n\t\t\tif(RouterUtil.isSystemSchema(_tableName)){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif ((dotIndex = _tableName.indexOf('.')) != -1) {\n\t\t\t\tString schemaInSQL = _tableName.substring(0, dotIndex);\n\t\t\t\tif (schemaInSQL.equalsIgnoreCase(sessionSchema)) {\n\t\t\t\t\t_tableName = _tableName.substring(dotIndex + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\taddTable(_tableName);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/LoadDataOutputVisitor.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement;\nimport com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor;\n\n/**\n * Created by nange on 2015/10/20.\n */\npublic class LoadDataOutputVisitor extends MySqlOutputVisitor\n{\n    public LoadDataOutputVisitor(Appendable appender)\n    {\n        super(appender);\n    }\n    @Override\n    public boolean visit(MySqlLoadDataInFileStatement x) {\n        print(\"LOAD DATA \");\n\n        if (x.isLowPriority()) {\n            print(\"LOW_PRIORITY \");\n        }\n\n        if (x.isConcurrent()) {\n            print(\"CONCURRENT \");\n        }\n\n        if (x.isLocal()) {\n            print(\"LOCAL \");\n        }\n\n        print(\"INFILE \");\n\n        x.getFileName().accept(this);\n\n        if (x.isReplicate()) {\n            print(\" REPLACE \");\n        }\n\n        if (x.isIgnore()) {\n            print(\" IGNORE \");\n        }\n\n        print(\" INTO TABLE \");\n        x.getTableName().accept(this);\n        if(x.getCharset()!=null)\n        {\n            print(\" CHARACTER SET \");\n            print(\"'\"+x.getCharset()+\"'\");\n        }\n\n        if (x.getColumnsTerminatedBy() != null || x.getColumnsEnclosedBy() != null || x.getColumnsEscaped() != null) {\n            print(\" COLUMNS\");\n            if (x.getColumnsTerminatedBy() != null) {\n                print(\" TERMINATED BY \");\n                x.getColumnsTerminatedBy().accept(this);\n            }\n\n            if (x.getColumnsEnclosedBy() != null) {\n                if (x.isColumnsEnclosedOptionally()) {\n                    print(\" OPTIONALLY\");\n                }\n                print(\" ENCLOSED BY \");\n                x.getColumnsEnclosedBy().accept(this);\n            }\n\n            if (x.getColumnsEscaped() != null) {\n                print(\" ESCAPED BY \");\n                x.getColumnsEscaped().accept(this);\n            }\n        }\n\n        if (x.getLinesStartingBy() != null || x.getLinesTerminatedBy() != null) {\n            print(\" LINES\");\n            if (x.getLinesStartingBy() != null) {\n                print(\" STARTING BY \");\n                x.getLinesStartingBy().accept(this);\n            }\n\n            if (x.getLinesTerminatedBy() != null) {\n                print(\" TERMINATED BY \");\n                x.getLinesTerminatedBy().accept(this);\n            }\n        }\n\n        if(x.getIgnoreLinesNumber() != null) {\n            print(\" IGNORE \");\n            x.getIgnoreLinesNumber().accept(this);\n            print(\" LINES\");\n        }\n\n        if (x.getColumns().size() != 0) {\n            print(\" (\");\n            printAndAccept(x.getColumns(), \", \");\n            print(\")\");\n        }\n\n        if (x.getSetList().size() != 0) {\n            print(\" SET \");\n            printAndAccept(x.getSetList(), \", \");\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/LoadDataStatement.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement;\n\n\npublic class LoadDataStatement extends MySqlLoadDataInFileStatement\n{\n\n    public String toString()\n    {\n        StringBuilder out = new StringBuilder();\n        this.accept(new LoadDataOutputVisitor(out));\n\n        return out.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/MycatExprParser.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport com.alibaba.druid.sql.ast.statement.SQLSelectItem;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlExprParser;\nimport com.alibaba.druid.sql.parser.Lexer;\nimport com.alibaba.druid.sql.parser.Token;\n\n/**\n * Created by nange on 2015/3/13.\n */\npublic class MycatExprParser extends MySqlExprParser\n{\n    public static final String[] max_agg_functions = {\"AVG\", \"COUNT\", \"GROUP_CONCAT\", \"MAX\", \"MIN\", \"STDDEV\", \"SUM\", \"ROW_NUMBER\"};\n\n    public MycatExprParser(Lexer lexer)\n    {\n        super(lexer);\n        super.aggregateFunctions = max_agg_functions;\n    }\n\n    public MycatExprParser(String sql)\n    {\n        super(new MycatLexer(sql));\n        lexer.nextToken();\n    }\n    @Override\n    public SQLSelectItem parseSelectItem()\n    {\n        parseTop();\n        return super.parseSelectItem();\n    }\n    public void parseTop()\n    {\n        if (lexer.token() == Token.TOP)\n        {\n            lexer.nextToken();\n\n            boolean paren = false;\n            if (lexer.token() == Token.LPAREN)\n            {\n                paren = true;\n                lexer.nextToken();\n            }\n\n            if (paren)\n            {\n                accept(Token.RPAREN);\n            }\n\n            if (lexer.token() == Token.LITERAL_INT)\n            {\n                lexer.mark();\n                lexer.nextToken();\n            }\n            if (lexer.token() == Token.IDENTIFIER)\n            {\n                lexer.nextToken();\n\n            }\n            if (lexer.token() == Token.EQ||lexer.token() == Token.DOT)\n            {\n                lexer.nextToken();\n            } else  if(lexer.token() != Token.STAR)\n            {\n                lexer.reset();\n            }\n            if (lexer.token() == Token.PERCENT)\n            {\n                lexer.nextToken();\n            }\n\n\n        }\n\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/MycatLexer.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlLexer;\nimport com.alibaba.druid.sql.parser.Keywords;\nimport com.alibaba.druid.sql.parser.Token;\n\n/**\n * Created by magicdoom on 2015/3/13.\n */\npublic class MycatLexer extends MySqlLexer\n{\n    public final static Keywords DEFAULT_MYCAT_KEYWORDS;\n    static {\n        Map<String, Token> map = new HashMap<String, Token>();\n\n        map.putAll(Keywords.DEFAULT_KEYWORDS.getKeywords());\n\n        map.put(\"DUAL\", Token.DUAL);\n        map.put(\"FALSE\", Token.FALSE);\n        map.put(\"IDENTIFIED\", Token.IDENTIFIED);\n        map.put(\"IF\", Token.IF);\n        map.put(\"KILL\", Token.KILL);\n\n        map.put(\"LIMIT\", Token.LIMIT);\n        map.put(\"TRUE\", Token.TRUE);\n        map.put(\"BINARY\", Token.BINARY);\n        map.put(\"SHOW\", Token.SHOW);\n        map.put(\"CACHE\", Token.CACHE);\n        map.put(\"ANALYZE\", Token.ANALYZE);\n        map.put(\"OPTIMIZE\", Token.OPTIMIZE);\n        map.put(\"ROW\", Token.ROW);\n        map.put(\"BEGIN\", Token.BEGIN);\n        map.put(\"END\", Token.END);\n\n        map.put(\"TOP\", Token.TOP);\n\n\n        DEFAULT_MYCAT_KEYWORDS = new Keywords(map);\n    }\n\n\n    public MycatLexer(char[] input, int inputLength, boolean skipComment)\n    {\n        super(input, inputLength, skipComment);\n\t\tsuper.keywords = DEFAULT_MYCAT_KEYWORDS;\n    }\n\n    public MycatLexer(String input)\n    {\n        super(input);\n\t\tsuper.keywords = DEFAULT_MYCAT_KEYWORDS;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/MycatSchemaStatVisitor.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport com.alibaba.druid.sql.SQLUtils;\nimport com.alibaba.druid.sql.ast.SQLCommentHint;\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.SQLExprImpl;\nimport com.alibaba.druid.sql.ast.SQLName;\nimport com.alibaba.druid.sql.ast.SQLObject;\nimport com.alibaba.druid.sql.ast.SQLObjectImpl;\nimport com.alibaba.druid.sql.ast.expr.SQLAggregateExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLAllExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLAnyExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBetweenExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;\nimport com.alibaba.druid.sql.ast.expr.SQLCharExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLExistsExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLInListExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLQueryExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLSomeExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLAlterTableItem;\nimport com.alibaba.druid.sql.ast.statement.SQLAlterTableStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLExprTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLJoinTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectItem;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHintStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\nimport com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;\nimport com.alibaba.druid.stat.TableStat;\nimport com.alibaba.druid.stat.TableStat.Column;\nimport com.alibaba.druid.stat.TableStat.Condition;\nimport com.alibaba.druid.stat.TableStat.Mode;\n\nimport io.mycat.route.util.RouterUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * Druid解析器中用来从ast语法中提取表名、条件、字段等的vistor\n * @author wang.dw\n *\n */\npublic class MycatSchemaStatVisitor extends MySqlSchemaStatVisitor {\n\tprivate boolean hasOrCondition = false;\n\tprivate List<WhereUnit> whereUnits = new ArrayList<WhereUnit>();\n\tprivate List<WhereUnit> storedwhereUnits = new ArrayList<>();\n    private List<SQLBinaryOpExpr> orBinaryExprs = new ArrayList<SQLBinaryOpExpr>();\n    private Queue<SQLSelect> subQuerys = new LinkedList<>();  //子查询集合\n\tprivate boolean hasChange = false; // 是否有改写sql\n\tprivate boolean subqueryRelationOr = false;   //子查询存在关联条件的情况下，是否有 or 条件\n\tprivate Map<String, String> aliasMap = new LinkedHashMap<>();\n\tprivate List<String> selectTableList = new ArrayList<>();\n\tprivate String currentTable;\n\t// 标识一个元素是否已经被VISIT，防止多次重复访问导致添加aliasMap时报表冲突异常\n\tprivate final String TABLE_HAS_VISIT_FLAG = \"TABLE_HAS_VISIT_FLAG\";\n\t// 记录哪些表已经被visit\n\tprivate List<SQLObjectImpl> visitedTableSourceExpr = new ArrayList<SQLObjectImpl>();\n\n\tprivate void reset() {\n\t\tthis.conditions.clear();\n\t\tthis.whereUnits.clear();\n        this.orBinaryExprs.clear();\n\t\tthis.hasOrCondition = false;\n\t}\n\t\n\tpublic List<WhereUnit> getWhereUnits() {\n\t\treturn whereUnits;\n\t}\n\n\tpublic boolean hasOrCondition() {\n\t\treturn hasOrCondition;\n\t}\n\t\n    @Override\n    public boolean visit(SQLSelectStatement x) {\n\t\t// setAliasMap();\n//        getAliasMap().put(\"DUAL\", null);\n\t\taliasMap.clear();\n\t\tselectTableList.clear();\n\t\tclearTableVisitFlag();\n        return true;\n    }\n\n    @Override\n    public boolean visit(SQLBetweenExpr x) {\n        String begin = null;\n        if(x.beginExpr instanceof SQLCharExpr)\n        {\n            begin= (String) ( (SQLCharExpr)x.beginExpr).getValue();\n        }  else {\n            begin = x.beginExpr.toString();\n        }\n        String end = null;\n        if(x.endExpr instanceof SQLCharExpr)\n        {\n            end= (String) ( (SQLCharExpr)x.endExpr).getValue();\n        }  else {\n            end = x.endExpr.toString();\n        }\n        Column column = getColumn(x);\n        if (column == null) {\n            return true;\n        }\n\n        Condition condition = null;\n        for (Condition item : this.getConditions()) {\n            if (item.getColumn().equals(column) && item.getOperator().equals(\"between\")) {\n                condition = item;\n                break;\n            }\n        }\n\n        if (condition == null) {\n\t\t\tcondition = new Condition(column, \"between\");\n            this.conditions.add(condition);\n        }\n\n\n        condition.getValues().add(begin);\n        condition.getValues().add(end);\n\n\n        return true;\n    }\n\n    @Override\n    protected Column getColumn(SQLExpr expr) {\n\t\tMap<String, String> aliasMap = getAliasMap();\n\t\tif (aliasMap == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (expr instanceof SQLBetweenExpr) {\n\t\t\treturn getColumnByExpr((SQLBetweenExpr) expr);\n\t\t} else if (expr instanceof SQLIdentifierExpr) {\n\t\t\treturn getColumnByExpr((SQLIdentifierExpr) expr);\n\t\t} else {// SQLPropertyExpr\n\t\t\treturn super.getColumn(expr);\n\t\t}\n\t\t// return null;\n    }\n\n\tprivate Column getColumnByExpr(SQLIdentifierExpr expr) {\n//        Column attrColumn = (Column) expr.getAttribute(ATTR_COLUMN);\n//        if (attrColumn != null) {\n//            return attrColumn;\n//        }\n\n\t\tString column = expr.getName();\n\t\tString table = getCurrentTable();\n\t\tif (table != null && aliasMap.containsKey(table)) {\n\t\t\ttable = aliasMap.get(table);\n\t\t\tif (table == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tif (table != null) {\n\t\t\treturn new Column(table, column);\n\t\t}\n\n//        if (variants.containsKey(column)) {\n//            return null;\n//        }\n\n\t\treturn new Column(\"UNKNOWN\", column);\n\t}\n\tprivate Column getColumnByExpr(SQLBetweenExpr betweenExpr) {\n\t\tif (betweenExpr.getTestExpr() != null) {\n\t\t\tString tableName = null;\n\t\t\tString column = null;\n\t\t\tif (betweenExpr.getTestExpr() instanceof SQLPropertyExpr) { // field\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// has\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// alias\n\t\t\t\ttableName = ((SQLIdentifierExpr) ((SQLPropertyExpr) betweenExpr.getTestExpr()).getOwner()).getName();\n\t\t\t\tcolumn = ((SQLPropertyExpr) betweenExpr.getTestExpr()).getName();\n\t\t\t\ttableName = getTableNameByAlias(tableName);\n\n\t\t\t\treturn new Column(tableName, column);\n\t\t\t} else if (betweenExpr.getTestExpr() instanceof SQLIdentifierExpr) {\n\t\t\t\tcolumn = ((SQLIdentifierExpr) betweenExpr.getTestExpr()).getName();\n\t\t\t\ttableName = getOwnerTableName(betweenExpr, column);\n\t\t\t}\n\t\t\tString table = tableName;\n\t\t\ttable = getTableNameByAlias(table);\n\n\t\t\tif (table != null && !\"\".equals(table)) {\n\t\t\t\treturn new Column(table, column);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * 通过别名获取到表的名称.如果获取不到返回alias\n\t * \n\t * @param alias\n\t * @return\n\t */\n\tprivate String getTableNameByAlias(String alias) {\n\t\tif (alias != null) {\n\t\t\tString upperCaseAlias = alias.toUpperCase();\n\t\t\tif (aliasMap.containsKey(upperCaseAlias)) {\n\t\t\t\treturn aliasMap.get(upperCaseAlias);\n\t\t\t}\n\t\t}\n\t\treturn alias;\n\t}\n    /**\n     * 从between语句中获取字段所属的表名。\n     * 对于容易出现ambiguous的（字段不知道到底属于哪个表），实际应用中必须使用别名来避免歧义\n     * @param betweenExpr\n     * @param column\n     * @return\n     */\n    private String getOwnerTableName(SQLBetweenExpr betweenExpr,String column) {\n        if(tableStats.size() == 1) {//只有一个表，直接返回这一个表名\n            return tableStats.keySet().iterator().next().getName();\n        } else if(tableStats.size() == 0) {//一个表都没有，返回空串\n            return \"\";\n        } else {//多个表名\n\t\t\tfor (Column col : columns.values())\n            {\n                if(col.getName().equals(column)) {\n                    return col.getTable();\n                }\n            }\n//            for(Column col : columns) {//从columns中找表名\n//                if(col.getName().equals(column)) {\n//                    return col.getTable();\n//                }\n//            }\n\n            //前面没找到表名的，自己从parent中解析\n\n            SQLObject parent = betweenExpr.getParent();\n            if(parent instanceof SQLBinaryOpExpr)\n            {\n                parent=parent.getParent();\n            }\n\n            if(parent instanceof MySqlSelectQueryBlock) {\n                MySqlSelectQueryBlock select = (MySqlSelectQueryBlock) parent;\n                if(select.getFrom() instanceof SQLJoinTableSource) {//多表连接\n                    SQLJoinTableSource joinTableSource = (SQLJoinTableSource)select.getFrom();\n                    return joinTableSource.getLeft().toString();//将left作为主表，此处有不严谨处，但也是实在没有办法，如果要准确，字段前带表名或者表的别名即可\n                } else if(select.getFrom() instanceof SQLExprTableSource) {//单表\n                    return select.getFrom().toString();\n                }\n            }\n            else if(parent instanceof SQLUpdateStatement) {\n                SQLUpdateStatement update = (SQLUpdateStatement) parent;\n                return update.getTableName().getSimpleName();\n            } else if(parent instanceof SQLDeleteStatement) {\n                SQLDeleteStatement delete = (SQLDeleteStatement) parent;\n                return delete.getTableName().getSimpleName();\n            } else {\n                \n            }\n        }\n        return \"\";\n    }\n    \n    private void setSubQueryRelationOrFlag(SQLExprImpl x){\n    \tMycatSubQueryVisitor subQueryVisitor = new MycatSubQueryVisitor();\n    \tx.accept(subQueryVisitor);\n    \tif(subQueryVisitor.isRelationOr()){\n    \t\tsubqueryRelationOr = true;\n    \t}\n    }\n    \n    /*\n     * 子查询\n     * (non-Javadoc)\n     * @see com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter#visit(com.alibaba.druid.sql.ast.expr.SQLQueryExpr)\n     */\n    @Override\n    public boolean visit(SQLQueryExpr x) {\n    \tsetSubQueryRelationOrFlag(x);\n    \taddSubQuerys(x.getSubQuery());\n    \treturn super.visit(x);\n    }\n    /*\n     * (non-Javadoc)\n     * @see com.alibaba.druid.sql.visitor.SchemaStatVisitor#visit(com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource)\n     */\n    @Override\n    public boolean visit(SQLSubqueryTableSource x){\n    \taddSubQuerys(x.getSelect());\n    \treturn super.visit(x);\n    }\n    \n    /*\n     * (non-Javadoc)\n     * @see com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter#visit(com.alibaba.druid.sql.ast.expr.SQLExistsExpr)\n     */\n    @Override\n    public boolean visit(SQLExistsExpr x) {\n    \tsetSubQueryRelationOrFlag(x);\n    \taddSubQuerys(x.getSubQuery());\n    \treturn super.visit(x);\n    }\n    \n    @Override\n    public boolean visit(SQLInListExpr x) {\n    \treturn super.visit(x);\n    }\n    \n    /*\n     *  对 in 子查询的处理\n     * (non-Javadoc)\n     * @see com.alibaba.druid.sql.visitor.SchemaStatVisitor#visit(com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr)\n     */\n    @Override\n    public boolean visit(SQLInSubQueryExpr x) {\n    \tsetSubQueryRelationOrFlag(x);\n    \taddSubQuerys(x.getSubQuery());\n    \treturn super.visit(x);\n    }\n    \n    /* \n     *  遇到 all 将子查询改写成  SELECT MAX(name) FROM subtest1\n     *  例如:\n     *        select * from subtest where id > all (select name from subtest1);\n     *    \t\t>/>= all ----> >/>= max\n     *    \t\t</<= all ----> </<= min\n     *    \t\t<>   all ----> not in\n     *          =    all ----> id = 1 and id = 2\n     *          other  不改写\n     */    \n    @Override\n    public boolean visit(SQLAllExpr x) {\n    \tsetSubQueryRelationOrFlag(x);\n    \t\n    \tList<SQLSelectItem> itemlist = ((SQLSelectQueryBlock)(x.getSubQuery().getQuery())).getSelectList();\n    \tSQLExpr sexpr = itemlist.get(0).getExpr();\n    \t\n\t\tif(x.getParent() instanceof SQLBinaryOpExpr){\n\t\t\tSQLBinaryOpExpr parentExpr = (SQLBinaryOpExpr)x.getParent();\n\t\t\tSQLAggregateExpr saexpr = null;\n\t\t\tswitch (parentExpr.getOperator()) {\n\t\t\tcase GreaterThan:\n\t\t\tcase GreaterThanOrEqual:\n\t\t\tcase NotLessThan:\n\t\t\t\tthis.hasChange = true;\n\t\t\t\tif(sexpr instanceof SQLIdentifierExpr \n\t\t\t\t\t\t|| (sexpr instanceof SQLPropertyExpr&&((SQLPropertyExpr)sexpr).getOwner() instanceof SQLIdentifierExpr)){\n\t\t\t\t\tsaexpr = new SQLAggregateExpr(\"MAX\");\n\t\t\t\t\tsaexpr.getArguments().add(sexpr);\n\t        \t\tsaexpr.setParent(itemlist.get(0));\n\t        \t\titemlist.get(0).setExpr(saexpr);\n\t\t\t\t}\n\t\t\t\tSQLQueryExpr maxSubQuery = new SQLQueryExpr(x.getSubQuery());\n        \t\tx.getSubQuery().setParent(x.getParent());\n        \t\t// 生成新的SQLQueryExpr 替换当前 SQLAllExpr 节点\n            \tif(x.getParent() instanceof SQLBinaryOpExpr){\n            \t\tif(((SQLBinaryOpExpr)x.getParent()).getLeft().equals(x)){\n            \t\t\t((SQLBinaryOpExpr)x.getParent()).setLeft(maxSubQuery);\n            \t\t}else if(((SQLBinaryOpExpr)x.getParent()).getRight().equals(x)){\n            \t\t\t((SQLBinaryOpExpr)x.getParent()).setRight(maxSubQuery);\n            \t\t}\n            \t}\n            \taddSubQuerys(x.getSubQuery());\n            \treturn super.visit(x.getSubQuery());\n\t\t\tcase LessThan:\n\t\t\tcase LessThanOrEqual:\n\t\t\tcase NotGreaterThan:\n\t\t\t\tthis.hasChange = true;\n\t\t\t\tif(sexpr instanceof SQLIdentifierExpr \n\t\t\t\t\t\t|| (sexpr instanceof SQLPropertyExpr&&((SQLPropertyExpr)sexpr).getOwner() instanceof SQLIdentifierExpr)){\n\t\t\t\t\tsaexpr = new SQLAggregateExpr(\"MIN\");\n\t\t\t\t\tsaexpr.getArguments().add(sexpr);\n\t        \t\tsaexpr.setParent(itemlist.get(0));\n\t        \t\titemlist.get(0).setExpr(saexpr);\n\t        \t\t\n\t            \tx.subQuery.setParent(x.getParent());\n\t\t\t\t}\n\t\t\t\t// 生成新的SQLQueryExpr 替换当前 SQLAllExpr 节点\n            \tSQLQueryExpr minSubQuery = new SQLQueryExpr(x.getSubQuery());\n            \tif(x.getParent() instanceof SQLBinaryOpExpr){\n            \t\tif(((SQLBinaryOpExpr)x.getParent()).getLeft().equals(x)){\n            \t\t\t((SQLBinaryOpExpr)x.getParent()).setLeft(minSubQuery);\n            \t\t}else if(((SQLBinaryOpExpr)x.getParent()).getRight().equals(x)){\n            \t\t\t((SQLBinaryOpExpr)x.getParent()).setRight(minSubQuery);\n            \t\t}\n            \t}\n            \taddSubQuerys(x.getSubQuery());\n            \treturn super.visit(x.getSubQuery());\n\t\t\t case LessThanOrGreater:\n\t\t\t case NotEqual:\n\t\t\t\tthis.hasChange = true;\n\t\t\t\tSQLInSubQueryExpr notInSubQueryExpr = new SQLInSubQueryExpr(x.getSubQuery());\n\t\t\t\tx.getSubQuery().setParent(notInSubQueryExpr);\n\t\t\t\tnotInSubQueryExpr.setNot(true);\n\t\t\t\t// 生成新的SQLQueryExpr 替换当前 SQLAllExpr 节点\n\t\t\t\tif(x.getParent() instanceof SQLBinaryOpExpr){\n\t\t\t\t\tSQLBinaryOpExpr xp = (SQLBinaryOpExpr)x.getParent();\n\t\t\t\t\t\n\t\t\t\t\tif(xp.getLeft().equals(x)){\n\t\t\t\t\t\tnotInSubQueryExpr.setExpr(xp.getRight());\n\t\t\t\t\t}else if(xp.getRight().equals(x)){\n\t\t\t\t\t\tnotInSubQueryExpr.setExpr(xp.getLeft());\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif(xp.getParent() instanceof MySqlSelectQueryBlock){\n\t\t\t\t\t\t((MySqlSelectQueryBlock)xp.getParent()).setWhere(notInSubQueryExpr);\n\t\t\t\t\t}else if(xp.getParent() instanceof SQLBinaryOpExpr){\n\t\t\t\t\t\tSQLBinaryOpExpr pp = ((SQLBinaryOpExpr)xp.getParent());\n\t\t\t\t\t\tif(pp.getLeft().equals(xp)){\n\t\t\t\t\t\t\tpp.setLeft(notInSubQueryExpr);\n\t\t\t\t\t\t}else if(pp.getRight().equals(xp)){\n\t\t\t\t\t\t\tpp.setRight(notInSubQueryExpr);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t            }\n\t\t\t\taddSubQuerys(x.getSubQuery());\n\t            return super.visit(notInSubQueryExpr);\n\t\t\t default:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\taddSubQuerys(x.getSubQuery());\n    \treturn super.visit(x);\n    }\n    \n    /* \n     *  遇到 some 将子查询改写成  SELECT MIN(name) FROM subtest1\n     *  例如:\n     *        select * from subtest where id > some (select name from subtest1);\n     *    >/>= some ----> >/>= min\n     *    </<= some ----> </<= max\n     *    <>   some ----> not in\n     *    =    some ----> in\n     *    other  不改写\n     */\n    @Override\n    public boolean visit(SQLSomeExpr x) {\n    \t\n    \tsetSubQueryRelationOrFlag(x);\n    \t\n    \tList<SQLSelectItem> itemlist = ((SQLSelectQueryBlock)(x.getSubQuery().getQuery())).getSelectList();\n    \tSQLExpr sexpr = itemlist.get(0).getExpr();\n    \t\n\t\tif(x.getParent() instanceof SQLBinaryOpExpr){\n\t\t\tSQLBinaryOpExpr parentExpr = (SQLBinaryOpExpr)x.getParent();\n\t\t\tSQLAggregateExpr saexpr = null;\n\t\t\tswitch (parentExpr.getOperator()) {\n\t\t\tcase GreaterThan:\n\t\t\tcase GreaterThanOrEqual:\n\t\t\tcase NotLessThan:\n\t\t\t\tthis.hasChange = true;\n\t\t\t\tif(sexpr instanceof SQLIdentifierExpr \n\t\t\t\t\t\t|| (sexpr instanceof SQLPropertyExpr&&((SQLPropertyExpr)sexpr).getOwner() instanceof SQLIdentifierExpr)){\n\t\t\t\t\tsaexpr = new SQLAggregateExpr(\"MIN\");\n\t\t\t\t\tsaexpr.getArguments().add(sexpr);\n\t        \t\tsaexpr.setParent(itemlist.get(0));\n\t        \t\titemlist.get(0).setExpr(saexpr);\n\t\t\t\t}\n\t\t\t\tSQLQueryExpr maxSubQuery = new SQLQueryExpr(x.getSubQuery());\n        \t\tx.getSubQuery().setParent(maxSubQuery);\n        \t\t// 生成新的SQLQueryExpr 替换当前 SQLAllExpr 节点\n            \tif(x.getParent() instanceof SQLBinaryOpExpr){\n            \t\tif(((SQLBinaryOpExpr)x.getParent()).getLeft().equals(x)){\n            \t\t\t((SQLBinaryOpExpr)x.getParent()).setLeft(maxSubQuery);\n            \t\t}else if(((SQLBinaryOpExpr)x.getParent()).getRight().equals(x)){\n            \t\t\t((SQLBinaryOpExpr)x.getParent()).setRight(maxSubQuery);\n            \t\t}\n            \t}\n            \taddSubQuerys(x.getSubQuery());\n            \treturn super.visit(x.getSubQuery());\n\t\t\tcase LessThan:\n\t\t\tcase LessThanOrEqual:\n\t\t\tcase NotGreaterThan:\n\t\t\t\tthis.hasChange = true;\n\t\t\t\tif(sexpr instanceof SQLIdentifierExpr \n\t\t\t\t\t\t|| (sexpr instanceof SQLPropertyExpr&&((SQLPropertyExpr)sexpr).getOwner() instanceof SQLIdentifierExpr)){\n\t\t\t\t\tsaexpr = new SQLAggregateExpr(\"MAX\");\n\t\t\t\t\tsaexpr.getArguments().add(sexpr);\n\t        \t\tsaexpr.setParent(itemlist.get(0));\n\t        \t\titemlist.get(0).setExpr(saexpr);\n\t\t\t\t}\n\t\t\t\t// 生成新的SQLQueryExpr 替换当前 SQLAllExpr 节点\n            \tSQLQueryExpr minSubQuery = new SQLQueryExpr(x.getSubQuery());\n        \t\tx.getSubQuery().setParent(minSubQuery);\n            \t\n            \tif(x.getParent() instanceof SQLBinaryOpExpr){\n            \t\tif(((SQLBinaryOpExpr)x.getParent()).getLeft().equals(x)){\n            \t\t\t((SQLBinaryOpExpr)x.getParent()).setLeft(minSubQuery);\n            \t\t}else if(((SQLBinaryOpExpr)x.getParent()).getRight().equals(x)){\n            \t\t\t((SQLBinaryOpExpr)x.getParent()).setRight(minSubQuery);\n            \t\t}\n            \t}\n            \taddSubQuerys(x.getSubQuery());\n            \treturn super.visit(x.getSubQuery());\n\t\t\t case LessThanOrGreater:\n\t\t\t case NotEqual:\n\t\t\t\t this.hasChange = true;\n\t\t\t\t\tSQLInSubQueryExpr notInSubQueryExpr = new SQLInSubQueryExpr(x.getSubQuery());\n\t\t\t\t\tx.getSubQuery().setParent(notInSubQueryExpr);\n\t\t\t\t\tnotInSubQueryExpr.setNot(true);\n\t\t\t\t\t// 生成新的SQLQueryExpr 替换当前 SQLAllExpr 节点\n\t\t\t\t\tif(x.getParent() instanceof SQLBinaryOpExpr){\n\t\t\t\t\t\tSQLBinaryOpExpr xp = (SQLBinaryOpExpr)x.getParent();\n\t\t\t\t\t\t\n\t\t\t\t\t\tif(xp.getLeft().equals(x)){\n\t\t\t\t\t\t\tnotInSubQueryExpr.setExpr(xp.getRight());\n\t\t\t\t\t\t}else if(xp.getRight().equals(x)){\n\t\t\t\t\t\t\tnotInSubQueryExpr.setExpr(xp.getLeft());\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tif(xp.getParent() instanceof MySqlSelectQueryBlock){\n\t\t\t\t\t\t\t((MySqlSelectQueryBlock)xp.getParent()).setWhere(notInSubQueryExpr);\n\t\t\t\t\t\t}else if(xp.getParent() instanceof SQLBinaryOpExpr){\n\t\t\t\t\t\t\tSQLBinaryOpExpr pp = ((SQLBinaryOpExpr)xp.getParent());\n\t\t\t\t\t\t\tif(pp.getLeft().equals(xp)){\n\t\t\t\t\t\t\t\tpp.setLeft(notInSubQueryExpr);\n\t\t\t\t\t\t\t}else if(pp.getRight().equals(xp)){\n\t\t\t\t\t\t\t\tpp.setRight(notInSubQueryExpr);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t            }\n\t\t\t\t\taddSubQuerys(x.getSubQuery());\n\t\t            return super.visit(notInSubQueryExpr);\n\t\t\t case Equality:\n\t\t\t\t this.hasChange = true;\n\t\t\t\tSQLInSubQueryExpr inSubQueryExpr = new SQLInSubQueryExpr(x.getSubQuery());\n\t\t\t\tx.getSubQuery().setParent(inSubQueryExpr);\n\t\t\t\tinSubQueryExpr.setNot(false);\n\t\t\t\t// 生成新的SQLQueryExpr 替换当前 SQLAllExpr 节点\n\t\t\t\tif(x.getParent() instanceof SQLBinaryOpExpr){\n\t\t\t\t\tSQLBinaryOpExpr xp = (SQLBinaryOpExpr)x.getParent();\n\t\t\t\t\t\n\t\t\t\t\tif(xp.getLeft().equals(x)){\n\t\t\t\t\t\tinSubQueryExpr.setExpr(xp.getRight());\n\t\t\t\t\t}else if(xp.getRight().equals(x)){\n\t\t\t\t\t\tinSubQueryExpr.setExpr(xp.getLeft());\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif(xp.getParent() instanceof MySqlSelectQueryBlock){\n\t\t\t\t\t\t((MySqlSelectQueryBlock)xp.getParent()).setWhere(inSubQueryExpr);\n\t\t\t\t\t}else if(xp.getParent() instanceof SQLBinaryOpExpr){\n\t\t\t\t\t\tSQLBinaryOpExpr pp = ((SQLBinaryOpExpr)xp.getParent());\n\t\t\t\t\t\tif(pp.getLeft().equals(xp)){\n\t\t\t\t\t\t\tpp.setLeft(inSubQueryExpr);\n\t\t\t\t\t\t}else if(pp.getRight().equals(xp)){\n\t\t\t\t\t\t\tpp.setRight(inSubQueryExpr);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t            }\n\t\t\t\taddSubQuerys(x.getSubQuery());\n\t            return super.visit(inSubQueryExpr);\n\t\t\t default:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\taddSubQuerys(x.getSubQuery());\n    \treturn super.visit(x);\n    }\n\n    /* \n     *  遇到 any 将子查询改写成  SELECT MIN(name) FROM subtest1\n     *  例如:\n     *    select * from subtest where id oper any (select name from subtest1);\n     *    >/>= any ----> >/>= min\n     *    </<= any ----> </<= max\n     *    <>   any ----> not in\n     *    =    some ----> in\n     *    other  不改写\n     */\n    @Override\n    public boolean visit(SQLAnyExpr x) {\n    \t\n    \tsetSubQueryRelationOrFlag(x);\n    \t\n    \tList<SQLSelectItem> itemlist = ((SQLSelectQueryBlock)(x.getSubQuery().getQuery())).getSelectList();\n    \tSQLExpr sexpr = itemlist.get(0).getExpr();\n    \t\n\t\tif(x.getParent() instanceof SQLBinaryOpExpr){\n\t\t\tSQLBinaryOpExpr parentExpr = (SQLBinaryOpExpr)x.getParent();\n\t\t\tSQLAggregateExpr saexpr = null;\n\t\t\tswitch (parentExpr.getOperator()) {\n\t\t\tcase GreaterThan:\n\t\t\tcase GreaterThanOrEqual:\n\t\t\tcase NotLessThan:\n\t\t\t\tthis.hasChange = true;\n\t\t\t\tif(sexpr instanceof SQLIdentifierExpr \n\t\t\t\t\t\t|| (sexpr instanceof SQLPropertyExpr&&((SQLPropertyExpr)sexpr).getOwner() instanceof SQLIdentifierExpr)){\n\t\t\t\t\tsaexpr = new SQLAggregateExpr(\"MIN\");\n\t\t\t\t\tsaexpr.getArguments().add(sexpr);\n\t        \t\tsaexpr.setParent(itemlist.get(0));\n\t        \t\titemlist.get(0).setExpr(saexpr);\n\t\t\t\t}\n\t\t\t\tSQLQueryExpr maxSubQuery = new SQLQueryExpr(x.getSubQuery());\n        \t\tx.getSubQuery().setParent(maxSubQuery);\n\t\t\t\t// 生成新的SQLQueryExpr 替换当前 SQLAllExpr 节点\n            \tif(x.getParent() instanceof SQLBinaryOpExpr){\n            \t\tif(((SQLBinaryOpExpr)x.getParent()).getLeft().equals(x)){\n            \t\t\t((SQLBinaryOpExpr)x.getParent()).setLeft(maxSubQuery);\n            \t\t}else if(((SQLBinaryOpExpr)x.getParent()).getRight().equals(x)){\n            \t\t\t((SQLBinaryOpExpr)x.getParent()).setRight(maxSubQuery);\n            \t\t}\n            \t}\n            \taddSubQuerys(x.getSubQuery());\n            \treturn super.visit(x.getSubQuery());\n\t\t\tcase LessThan:\n\t\t\tcase LessThanOrEqual:\n\t\t\tcase NotGreaterThan:\n\t\t\t\tthis.hasChange = true;\n\t\t\t\tif(sexpr instanceof SQLIdentifierExpr \n\t\t\t\t\t\t|| (sexpr instanceof SQLPropertyExpr&&((SQLPropertyExpr)sexpr).getOwner() instanceof SQLIdentifierExpr)){\n\t\t\t\t\tsaexpr = new SQLAggregateExpr(\"MAX\");\n\t\t\t\t\tsaexpr.getArguments().add(sexpr);\n\t        \t\tsaexpr.setParent(itemlist.get(0));\n\t        \t\titemlist.get(0).setExpr(saexpr);\n\t\t\t\t}\n\t\t\t\t// 生成新的SQLQueryExpr 替换当前 SQLAllExpr 节点\n            \tSQLQueryExpr minSubQuery = new SQLQueryExpr(x.getSubQuery());\n            \tx.subQuery.setParent(minSubQuery);\n            \tif(x.getParent() instanceof SQLBinaryOpExpr){\n            \t\tif(((SQLBinaryOpExpr)x.getParent()).getLeft().equals(x)){\n            \t\t\t((SQLBinaryOpExpr)x.getParent()).setLeft(minSubQuery);\n            \t\t}else if(((SQLBinaryOpExpr)x.getParent()).getRight().equals(x)){\n            \t\t\t((SQLBinaryOpExpr)x.getParent()).setRight(minSubQuery);\n            \t\t}\n            \t}\n            \taddSubQuerys(x.getSubQuery());\n            \treturn super.visit(x.getSubQuery());\n\t\t\t case LessThanOrGreater:\n\t\t\t case NotEqual:\n\t\t\t\t this.hasChange = true;\n\t\t\t\t\tSQLInSubQueryExpr notInSubQueryExpr = new SQLInSubQueryExpr(x.getSubQuery());\n\t\t\t\t\tx.getSubQuery().setParent(notInSubQueryExpr);\n\t\t\t\t\tnotInSubQueryExpr.setNot(true);\n\t\t\t\t\t// 生成新的SQLQueryExpr 替换当前 SQLAllExpr 节点\n\t\t\t\t\tif(x.getParent() instanceof SQLBinaryOpExpr){\n\t\t\t\t\t\tSQLBinaryOpExpr xp = (SQLBinaryOpExpr)x.getParent();\n\t\t\t\t\t\t\n\t\t\t\t\t\tif(xp.getLeft().equals(x)){\n\t\t\t\t\t\t\tnotInSubQueryExpr.setExpr(xp.getRight());\n\t\t\t\t\t\t}else if(xp.getRight().equals(x)){\n\t\t\t\t\t\t\tnotInSubQueryExpr.setExpr(xp.getLeft());\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tif(xp.getParent() instanceof MySqlSelectQueryBlock){\n\t\t\t\t\t\t\t((MySqlSelectQueryBlock)xp.getParent()).setWhere(notInSubQueryExpr);\n\t\t\t\t\t\t}else if(xp.getParent() instanceof SQLBinaryOpExpr){\n\t\t\t\t\t\t\tSQLBinaryOpExpr pp = ((SQLBinaryOpExpr)xp.getParent());\n\t\t\t\t\t\t\tif(pp.getLeft().equals(xp)){\n\t\t\t\t\t\t\t\tpp.setLeft(notInSubQueryExpr);\n\t\t\t\t\t\t\t}else if(pp.getRight().equals(xp)){\n\t\t\t\t\t\t\t\tpp.setRight(notInSubQueryExpr);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t            }\n\t\t\t\t\taddSubQuerys(x.getSubQuery());\n\t\t            return super.visit(notInSubQueryExpr);\n\t\t\t case Equality:\n\t\t\t\t this.hasChange = true;\n\t\t\t\tSQLInSubQueryExpr inSubQueryExpr = new SQLInSubQueryExpr(x.getSubQuery());\n\t\t\t\tx.getSubQuery().setParent(inSubQueryExpr);\n\t\t\t\tinSubQueryExpr.setNot(false);\n\t\t\t\t// 生成新的SQLQueryExpr 替换当前 SQLAllExpr 节点\n\t\t\t\tif(x.getParent() instanceof SQLBinaryOpExpr){\n\t\t\t\t\tSQLBinaryOpExpr xp = (SQLBinaryOpExpr)x.getParent();\n\t\t\t\t\t\n\t\t\t\t\tif(xp.getLeft().equals(x)){\n\t\t\t\t\t\tinSubQueryExpr.setExpr(xp.getRight());\n\t\t\t\t\t}else if(xp.getRight().equals(x)){\n\t\t\t\t\t\tinSubQueryExpr.setExpr(xp.getLeft());\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif(xp.getParent() instanceof MySqlSelectQueryBlock){\n\t\t\t\t\t\t((MySqlSelectQueryBlock)xp.getParent()).setWhere(inSubQueryExpr);\n\t\t\t\t\t}else if(xp.getParent() instanceof SQLBinaryOpExpr){\n\t\t\t\t\t\tSQLBinaryOpExpr pp = ((SQLBinaryOpExpr)xp.getParent());\n\t\t\t\t\t\tif(pp.getLeft().equals(xp)){\n\t\t\t\t\t\t\tpp.setLeft(inSubQueryExpr);\n\t\t\t\t\t\t}else if(pp.getRight().equals(xp)){\n\t\t\t\t\t\t\tpp.setRight(inSubQueryExpr);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t            }\n\t\t\t\taddSubQuerys(x.getSubQuery());\n\t            return super.visit(inSubQueryExpr);\n\t\t\t default:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\taddSubQuerys(x.getSubQuery());\n    \treturn super.visit(x);\n    }\n    \n    @Override\n\tpublic boolean visit(SQLBinaryOpExpr x) {\n        x.getLeft().setParent(x);\n        x.getRight().setParent(x);\n        \n        /*\n         * fix bug 当 selectlist 存在多个子查询时, 主表没有别名的情况下.主表的查询条件 被错误的附加到子查询上.\n         *  eg. select (select id from subtest2 where id = 1), (select id from subtest3 where id = 2) from subtest1 where id =4;\n         *  像这样的子查询, subtest1 的 过滤条件  id = 4 .  被 加入到  subtest3 上. 加别名的情况下正常,不加别名,就会存在这个问题.\n         *  这里设置好操作的是哪张表后,再进行判断.\n         */\n\t\t// druid新版本没有这个变量，先注释掉。\n//        String currenttable = x.getParent()==null?null: (String) x.getParent().getAttribute(SchemaStatVisitor.ATTR_TABLE);\n//        if(currenttable!=null){\n//        \tthis.setCurrentTable(currenttable);\n//        }\n//        \n        switch (x.getOperator()) {\n            case Equality:\n            case LessThanOrEqualOrGreaterThan:\n            case Is:\n            case IsNot:\n            case GreaterThan:\n            case GreaterThanOrEqual:\n            case LessThan:\n            case LessThanOrEqual:\n            case NotLessThan:\n            case LessThanOrGreater:\n\t\t\tcase NotEqual:\n\t\t\tcase NotGreaterThan:\n                handleCondition(x.getLeft(), x.getOperator().name, x.getRight());\n                handleCondition(x.getRight(), x.getOperator().name, x.getLeft());\n                handleRelationship(x.getLeft(), x.getOperator().name, x.getRight());\n                break;\n            case BooleanOr:\n            \t//永真条件，where条件抛弃\n                if (!RouterUtil.isConditionAlwaysTrue(x)) {\n                  hasOrCondition = true;\n                  orBinaryExprs.add(x);\n                }\n            \treturn false;\n            case Like:\n            case NotLike:\n            default:\n                break;\n        }\n\n\t\tsuper.statExpr(x.getLeft());\n\t\tsuper.statExpr(x.getRight());\n\t\treturn false;\n\t\t// return true;\n    }\n\t\n    /**\n     * 根据带or的expr创建 WhereUnits\n     */\n    private void buildWhereUnits() {\n        for (SQLBinaryOpExpr orExpr : orBinaryExprs) {\n            WhereUnit whereUnit = new WhereUnit();\n            whereUnit.setFinishedParse(true);\n            whereUnit.addOutConditions(getConditions());\n            WhereUnit innerWhereUnit = new WhereUnit(orExpr);\n            whereUnit.addSubWhereUnit(innerWhereUnit);\n            whereUnits.add(whereUnit);\n        }\n    }\n\n    /**\n     * 分解条件\n     */\n    public List<List<Condition>> splitConditions() {\n        buildWhereUnits();\n\n        // 按照or拆分\n\t\tfor(WhereUnit whereUnit : whereUnits) {\n\t\t\tsplitUntilNoOr(whereUnit);\n\t\t}\n\t\t\n\t\tthis.storedwhereUnits.addAll(whereUnits);\n\t\t\n\t\tloopFindSubWhereUnit(whereUnits);\n\t\t\n\t\t//拆分后的条件块解析成Condition列表\n\t\tfor(WhereUnit whereUnit : storedwhereUnits) {\n\t\t\tthis.getConditionsFromWhereUnit(whereUnit);\n\t\t}\n\t\t\n\t\t//多个WhereUnit组合:多层集合的组合\n\t\treturn mergedConditions();\n\t}\n\t\n\t/**\n\t * 循环寻找子WhereUnit（实际是嵌套的or）\n\t * @param whereUnitList\n\t */\n\tprivate void loopFindSubWhereUnit(List<WhereUnit> whereUnitList) {\n\t\tList<WhereUnit> subWhereUnits = new ArrayList<WhereUnit>();\n\t\tfor(WhereUnit whereUnit : new ArrayList<>(whereUnitList)) {\n\t\t\tif(whereUnit.getSplitedExprList().size() > 0) {\n\t\t\t\tList<SQLExpr> removeSplitedList = new ArrayList<SQLExpr>();\n\t\t\t\tfor(SQLExpr sqlExpr : whereUnit.getSplitedExprList()) {\n\t\t\t\t\treset();\n\n\t\t\t\t\tif(isExprHasOr(sqlExpr)) {\n                        this.buildWhereUnits();\n                        removeSplitedList.add(sqlExpr);\n\t\t\t\t\t\tWhereUnit subWhereUnit = this.whereUnits.get(0);\n\t\t\t\t\t\tsplitUntilNoOr(subWhereUnit);\n\t\t\t\t\t\twhereUnit.addSubWhereUnit(subWhereUnit);\n\t\t\t\t\t\tsubWhereUnits.add(subWhereUnit);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.conditions.clear();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(removeSplitedList.size() > 0) {\n\t\t\t\t\twhereUnit.getSplitedExprList().removeAll(removeSplitedList);\n\t\t\t\t}\n\t\t\t}\n\t\t\tsubWhereUnits.addAll(whereUnit.getSubWhereUnit());\n\t\t}\n\t\tif(subWhereUnits.size() > 0) {\n\t\t\tloopFindSubWhereUnit(subWhereUnits);\n\t\t}\n\t}\n\t\n\tprivate boolean isExprHasOr(SQLExpr expr) {\n\t\texpr.accept(this);\n\t\treturn hasOrCondition;\n\t}\n\t\n\tprivate List<List<Condition>> mergedConditions() {\n\t\tif(storedwhereUnits.size() == 0) {\n\t\t\treturn new ArrayList<List<Condition>>();\n\t\t}\n\t\tfor(WhereUnit whereUnit : storedwhereUnits) {\n\t\t\tmergeOneWhereUnit(whereUnit);\n\t\t}\n\t\treturn getMergedConditionList(storedwhereUnits);\n\t\t\n\t}\n\t\n\t/**\n\t * 一个WhereUnit内递归\n\t * @param whereUnit\n\t */\n\tprivate void mergeOneWhereUnit(WhereUnit whereUnit) {\n\t\tif(whereUnit.getSubWhereUnit().size() > 0) {\n\t\t\tfor(WhereUnit sub : whereUnit.getSubWhereUnit()) {\n\t\t\t\tmergeOneWhereUnit(sub);\n\t\t\t}\n\t\t\t\n\t\t\tif(whereUnit.getSubWhereUnit().size() > 1) {\n\t\t\t\tList<List<Condition>> mergedConditionList = getMergedConditionList(whereUnit.getSubWhereUnit());\n\t\t\t\tif(whereUnit.getOutConditions().size() > 0) {\n\t\t\t\t\tfor(int i = 0; i < mergedConditionList.size() ; i++) {\n\t\t\t\t\t\tmergedConditionList.get(i).addAll(whereUnit.getOutConditions());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\twhereUnit.setConditionList(mergedConditionList);\n\t\t\t} else if(whereUnit.getSubWhereUnit().size() == 1) {\n\t\t\t\tif(whereUnit.getOutConditions().size() > 0 && whereUnit.getSubWhereUnit().get(0).getConditionList().size() > 0) {\n\t\t\t\t\tfor(int i = 0; i < whereUnit.getSubWhereUnit().get(0).getConditionList().size() ; i++) {\n\t\t\t\t\t\twhereUnit.getSubWhereUnit().get(0).getConditionList().get(i).addAll(whereUnit.getOutConditions());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\twhereUnit.getConditionList().addAll(whereUnit.getSubWhereUnit().get(0).getConditionList());\n\t\t\t}\n\t\t} else {\n\t\t\t//do nothing\n\t\t}\n\t}\n\t\n\t/**\n\t * 条件合并：多个WhereUnit中的条件组合\n\t * @return\n\t */\n\tprivate List<List<Condition>> getMergedConditionList(List<WhereUnit> whereUnitList) {\n\t\tList<List<Condition>> mergedConditionList = new ArrayList<List<Condition>>();\n\t\tif(whereUnitList.size() == 0) {\n\t\t\treturn mergedConditionList; \n\t\t}\n\t\tmergedConditionList.addAll(whereUnitList.get(0).getConditionList());\n\t\t\n\t\tfor(int i = 1; i < whereUnitList.size(); i++) {\n\t\t\tmergedConditionList = merge(mergedConditionList, whereUnitList.get(i).getConditionList());\n\t\t}\n\t\treturn mergedConditionList;\n\t}\n\t\n\t/**\n\t * 两个list中的条件组合\n\t * @param list1\n\t * @param list2\n\t * @return\n\t */\n\t    private List<List<Condition>> merge(List<List<Condition>> list1, List<List<Condition>> list2) {\n        if(list1.size() == 0) {\n            return list2;\n        } else if (list2.size() == 0) {\n            return list1;\n        }\n        \n\t\tList<List<Condition>> retList = new ArrayList<List<Condition>>();\n\t\tfor(int i = 0; i < list1.size(); i++) {\n\t\t\tfor(int j = 0; j < list2.size(); j++) {\n//\t\t\t\tList<Condition> listTmp = new ArrayList<Condition>();\n//\t\t\t\tlistTmp.addAll(list1.get(i));\n//\t\t\t\tlistTmp.addAll(list2.get(j));\n//\t\t\t\tretList.add(listTmp);\n\t\t\t    /**\n\t\t         * 单纯做笛卡尔积运算，会导致非常多不必要的条件列表，</br>\n\t\t         * 当whereUnit和条件相对多时，会急剧增长条件列表项，内存直线上升，导致假死状态</br>\n\t\t         * 因此，修改算法为 </br>\n\t\t         * 1、先合并两个条件列表的元素为一个条件列表</br>\n\t\t         * 2、计算合并后的条件列表，在结果retList中：</br>\n\t\t         * &nbsp;2-1、如果当前的条件列表 是 另外一个条件列表的 超集，更新，并标识已存在</br>\n\t\t         * &nbsp;2-2、如果当前的条件列表 是 另外一个条件列表的 子集，标识已存在</br>\n\t\t         * 3、最后，如果被标识不存在，加入结果retList，否则丢弃。</br>\n\t\t         * \n\t\t         * @author SvenAugustus\n\t\t         */\n  \t\t\t    // 合并两个条件列表的元素为一个条件列表\n                List<Condition> listTmp = mergeSqlConditionList(list1.get(i), list2.get(j));\n      \n                // 判定当前的条件列表 是否 另外一个条件列表的 子集\n                boolean exists = false;\n                Iterator<List<Condition>> it = retList.iterator();\n                while (it.hasNext()) {\n                  List<Condition> result = (List<Condition>) it.next();\n                  if (result != null && listTmp != null && listTmp.size() > result.size()) {\n                    // 如果当前的条件列表 是 另外一个条件列表的 超集，更新，并标识已存在\n                    if (sqlConditionListInOther(result, listTmp)) {\n                      result.clear();\n                      result.addAll(listTmp);\n                      exists = true;\n                      break;\n                    }\n                  } else {\n                    // 如果当前的条件列表 是 另外一个条件列表的 子集，标识已存在\n                    if (sqlConditionListInOther(listTmp, result)) {\n                      exists = true;\n                      break;\n                    }\n                  }\n                }\n                if (!exists) {// 被标识不存在，加入\n                  retList.add(listTmp);\n                } // 否则丢弃\n\t\t\t}\n\t\t}\n        return retList;\n    }\n\t\n\tprivate void getConditionsFromWhereUnit(WhereUnit whereUnit) {\n\t\tList<List<Condition>> retList = new ArrayList<List<Condition>>();\n\t\t//or语句外层的条件:如where condition1 and (condition2 or condition3),condition1就会在外层条件中,因为之前提取\n\t\tList<Condition> outSideCondition = new ArrayList<Condition>();\n//\t\tstashOutSideConditions();\n\t\toutSideCondition.addAll(conditions);\n\t\tthis.conditions.clear();\n\t\tfor(SQLExpr sqlExpr : whereUnit.getSplitedExprList()) {\n\t\t\tsqlExpr.accept(this);\n//            List<Condition> conditions = new ArrayList<Condition>();\n//            conditions.addAll(getConditions()); conditions.addAll(outSideCondition);\n          /**\n           * 合并两个条件列表的元素为一个条件列表，减少不必要多的条件项</br>\n           * \n           * @author SvenAugustus\n           */\n          List<Condition> conditions = mergeSqlConditionList(getConditions(), outSideCondition);\n\t\t\tretList.add(conditions);\n\t\t\tthis.conditions.clear();\n\t\t}\n\t\twhereUnit.setConditionList(retList);\n\t\t\n\t\tfor(WhereUnit subWhere : whereUnit.getSubWhereUnit()) {\n\t\t\tgetConditionsFromWhereUnit(subWhere);\n\t\t}\n\t}\n\t\n\t/**\n\t * 递归拆分OR\n\t * \n\t * @param whereUnit\n\t * TODO:考虑嵌套or语句，条件中有子查询、 exists等很多种复杂情况是否能兼容\n\t */\n\tprivate void splitUntilNoOr(WhereUnit whereUnit) {\n\t\tif(whereUnit.isFinishedParse()) {\n\t\t\tif(whereUnit.getSubWhereUnit().size() > 0) {\n\t\t\t\tfor(int i = 0; i < whereUnit.getSubWhereUnit().size(); i++) {\n\t\t\t\t\tsplitUntilNoOr(whereUnit.getSubWhereUnit().get(i));\n\t\t\t\t}\n\t\t\t} \n\t\t} else {\n\t\t\tSQLBinaryOpExpr expr = whereUnit.getCanSplitExpr();\n\t\t\tif(expr.getOperator() == SQLBinaryOperator.BooleanOr) {\n//\t\t\t\twhereUnit.addSplitedExpr(expr.getRight());\n\t\t\t\taddExprIfNotFalse(whereUnit, expr.getRight());\n\t\t\t\tif(expr.getLeft() instanceof SQLBinaryOpExpr) {\n\t\t\t\t\twhereUnit.setCanSplitExpr((SQLBinaryOpExpr)expr.getLeft());\n\t\t\t\t\tsplitUntilNoOr(whereUnit);\n\t\t\t\t} else {\n\t\t\t\t\taddExprIfNotFalse(whereUnit, expr.getLeft());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\taddExprIfNotFalse(whereUnit, expr);\n\t\t\t\twhereUnit.setFinishedParse(true);\n\t\t\t}\n\t\t}\n    }\n\n\tprivate void addExprIfNotFalse(WhereUnit whereUnit, SQLExpr expr) {\n\t\t//非永假条件加入路由计算\n\t\tif(!RouterUtil.isConditionAlwaysFalse(expr)) {\n\t\t\twhereUnit.addSplitedExpr(expr);\n\t\t}\n\t}\n\t\n\t@Override\n    public boolean visit(SQLAlterTableStatement x) {\n        String tableName = x.getName().toString();\n\t\tTableStat stat = getTableStat(tableName);\n        stat.incrementAlterCount();\n\n\t\tsetCurrentTable(tableName);\n\t\t// setCurrentTable(x, tableName);\n\n        for (SQLAlterTableItem item : x.getItems()) {\n            item.setParent(x);\n            item.accept(this);\n        }\n\n        return false;\n    }\n    public boolean visit(MySqlCreateTableStatement x) {\n        SQLName sqlName=  x.getName();\n        if(sqlName!=null)\n        {\n            String table = sqlName.toString();\n            if(table.startsWith(\"`\"))\n            {\n                table=table.substring(1,table.length()-1);\n            }\n            setCurrentTable(table);\n        }\n        return false;\n    }\n    public boolean visit(MySqlInsertStatement x) {\n        SQLName sqlName=  x.getTableName();\n        if(sqlName!=null)\n        {\n            String table = sqlName.toString();\n            if(table.startsWith(\"`\"))\n            {\n                table=table.substring(1,table.length()-1);\n            }\n            setCurrentTable(sqlName.toString());\n        }\n        return false;\n    }\n\t// DUAL\n    public boolean visit(MySqlDeleteStatement x) {\n\t\t// setAliasMap();\n\t\taliasMap.clear();\n\t\tthis.clearTableVisitFlag();\n\n        setMode(x, Mode.Delete);\n\n        accept(x.getFrom());\n        accept(x.getUsing());\n        x.getTableSource().accept(this);\n\n        if (x.getTableSource() instanceof SQLExprTableSource) {\n            SQLName tableName = (SQLName) ((SQLExprTableSource) x.getTableSource()).getExpr();\n            String ident = tableName.toString();\n\t\t\t// setCurrentTable(x, ident);\n\t\t\tsetCurrentTable(ident);\n\n\t\t\tTableStat stat = this.getTableStat(ident);\n            stat.incrementDeleteCount();\n        }\n\n        accept(x.getWhere());\n\n        accept(x.getOrderBy());\n        accept(x.getLimit());\n\n        return false;\n    }\n    \n    public void endVisit(MySqlDeleteStatement x) {\n    }\n    \n    public boolean visit(SQLUpdateStatement x) {\n\t\t// setAliasMap();\n\t\taliasMap.clear();\n\t\tthis.clearTableVisitFlag();\n\n        setMode(x, Mode.Update);\n\n        SQLName identName = x.getTableName();\n        if (identName != null) {\n            String ident = identName.toString();\n            String alias = x.getTableSource().getAlias();\n            setCurrentTable(ident);\n\n            TableStat stat = getTableStat(ident);\n            stat.incrementUpdateCount();\n\n\t\t\t// Map<String, String> aliasMap = getAliasMap();\n\t\t\tputAliasToMap(ident, ident);\n            if(alias != null) {\n\t\t\t\tputAliasToMap(alias, ident);\n            }\n        } else {\n            x.getTableSource().accept(this);\n        }\n\n        accept(x.getItems());\n        accept(x.getWhere());\n\n        return false;\n    }\n    \n    @Override\n    public void endVisit(MySqlHintStatement x) {\n    \tsuper.endVisit(x);\n    }\n    \n    @Override\n    public boolean visit(MySqlHintStatement x) {\n    \tList<SQLCommentHint> hits = x.getHints();\n    \tif(hits != null && !hits.isEmpty()) {\n    \t\tString schema = parseSchema(hits);\n    \t\tif(schema != null ) {\n\t\t\t\t// setCurrentTable(x, schema + \".\");\n\t\t\t\tsetCurrentTable(schema + \".\");\n    \t\t\treturn true;\n    \t\t}\n    \t}\n    \treturn true;\n    }\n\n\t@Override\n\tpublic boolean visit(SQLExprTableSource x) {\n\t\tsuper.visit(x);\n\n\t\tif (this.isSimpleExprTableSource(x)) {\n\t\t\tString ident = x.getExpr().toString();\n\t\t\tsetCurrentTable(ident);\n\t\t\tselectTableList.add(ident);\n\t\t\tString alias = x.getAlias();\n\n\t\t\tif (x.getAttribute(TABLE_HAS_VISIT_FLAG) == null || !((Boolean) x.getAttribute(TABLE_HAS_VISIT_FLAG))) {\n\t\t\t\tx.putAttribute(TABLE_HAS_VISIT_FLAG, true);\n\t\t\t\tthis.visitedTableSourceExpr.add(x);\n\t\t\t\tif (alias != null) {\n\t\t\t\t\tputAliasToMap(alias, ident);\n\t\t\t\t} else {\n\t\t\t\t\tputAliasToMap(ident, ident);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis.accept(x.getExpr());\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate String parseSchema(List<SQLCommentHint> hits) {\n    \tString regx = \"\\\\!mycat:schema\\\\s*=([\\\\s\\\\w]*)$\";\n    \tfor(SQLCommentHint hit : hits ) {\n    \t\tPattern pattern = Pattern.compile(regx);\n    \t\tMatcher m = pattern.matcher(hit.getText());\n    \t\tif(m.matches()) {\n    \t\t\treturn m.group(1).trim();\n    \t\t}\n    \t}\n\t\treturn null;\n    }\n\n    public Queue<SQLSelect> getSubQuerys() {\n\t\treturn subQuerys;\n\t}\n\t\n\tprivate void addSubQuerys(SQLSelect sqlselect){\n\t\t/* 多个 sqlselect 之间  , equals 和 hashcode 是相同的.去重时 都被过滤掉了. */\n\t\tif(subQuerys.isEmpty()){\n\t\t\tsubQuerys.add(sqlselect);\n\t\t\treturn;\n\t\t}\n        boolean exists = false;\n\t\tIterator<SQLSelect> iter = subQuerys.iterator();\n\t\twhile(iter.hasNext()){\n\t\t\tSQLSelect ss = iter.next();\n\t\t\tif(ss.getQuery() instanceof SQLSelectQueryBlock\n\t\t\t\t\t&&sqlselect.getQuery() instanceof SQLSelectQueryBlock){\n\t\t\t\tSQLSelectQueryBlock current = (SQLSelectQueryBlock)sqlselect.getQuery();\n\t\t\t\tSQLSelectQueryBlock ssqb = (SQLSelectQueryBlock)ss.getQuery();\n//                  if(!sqlSelectQueryBlockEquals(ssqb,current)){\n//                    subQuerys.add(sqlselect);\n//                  }\n                /**\n                 * 修正判定逻辑，应改为全不在subQuerys中才加入<br/>\n                 * \n                 * @author SvenAugustus\n                 */\n                if(sqlSelectQueryBlockEquals(current,ssqb)){\n                   exists = true;\n                   break;\n                }\n\t\t\t\t}\n\t\t\t}\n        if(!exists) {\n          subQuerys.add(sqlselect);\n\t\t}\n\t}\n\t\n\t/* 多个 sqlselect 之间  , equals 和 hashcode 是相同的.去重时 使用 SQLSelectQueryBlock equals 方法 */\n    private boolean sqlSelectQueryBlockEquals(SQLSelectQueryBlock obj1,SQLSelectQueryBlock obj2) {\n        if (obj1 == obj2) return true;\n        if (obj2 == null) return false;\n        if (obj1.getClass() != obj2.getClass()) return false;\n        if (obj1.isParenthesized() ^ obj2.isParenthesized()) return false;\n        if (obj1.getDistionOption() != obj2.getDistionOption()) return false;\n        if (obj1.getFrom() == null) {\n            if (obj2.getFrom() != null) return false;\n        } else if (!obj1.getFrom().equals(obj2.getFrom())) return false;\n        if (obj1.getGroupBy() == null) {\n            if (obj2.getGroupBy() != null) return false;\n        } else if (!obj1.getGroupBy().equals(obj2.getGroupBy())) return false;\n        if (obj1.getInto() == null) {\n            if (obj2.getInto() != null) return false;\n        } else if (!obj1.getInto().equals(obj2.getInto())) return false;\n        if (obj1.getSelectList() == null) {\n            if (obj2.getSelectList() != null) return false;\n        } else if (!obj1.getSelectList().equals(obj2.getSelectList())) return false;\n        if (obj1.getWhere() == null) {\n            if (obj2.getWhere() != null) return false;\n        } else if (!obj1.getWhere().equals(obj2.getWhere())) return false;\n        return true;\n    }\n\n\tpublic boolean isHasChange() {\n\t\treturn hasChange;\n\t}\n\n\tpublic boolean isSubqueryRelationOr() {\n\t\treturn subqueryRelationOr;\n\t}\n    \n    /**\n     * 判定当前的条件列表 是否 另外一个条件列表的 子集\n     * \n     * @author SvenAugustus\n     * @param current 当前的条件列表 \n     * @param other 另外一个条件列表\n     * @return\n     */\n    private boolean sqlConditionListInOther(List<Condition> current, List<Condition> other) {\n      if (current == null) {\n        if (other != null) {\n          return false;\n        }\n        return true;\n      }\n      if (current.size() > other.size()) {\n        return false;\n      }\n      if (other.size() == current.size()) {\n        // 判定两个条件列表的元素是否内容相等\n        return sqlConditionListEquals(current, other);\n      }\n      for (int j = 0; j < current.size(); j++) {\n        boolean exists = false;\n        for (int i = 0; i < other.size(); i++) {\n          // 判定两个条件是否相等\n          if (sqlConditionEquals(current.get(j), other.get(i))) {\n            exists = true;\n            break;\n          }\n        }\n        if (!exists) {\n          return false;\n        }\n      }\n      return true;\n    }\n    \n    /**\n     * 判定两个条件列表的元素是否内容相等\n     * \n     * @author SvenAugustus\n     * @param list1\n     * @param list2\n     * @return\n     */\n    private boolean sqlConditionListEquals(List<Condition> list1, List<Condition> list2) {\n      if (list1 == null) {\n        if (list2 != null) {\n          return false;\n        }\n        return true;\n      }\n      if (list2.size() != list1.size()) {\n        return false;\n      }\n      int len = list1.size();\n      for (int j = 0; j < len; j++) {\n        boolean exists = false;\n        for (int i = 0; i < len; i++) {\n          // 判定两个条件是否相等\n          if (sqlConditionEquals(list2.get(j), list1.get(i))) {\n            exists = true;\n            break;\n          }\n        }\n        if (!exists) {\n          return false;\n        }\n      }\n      return true;\n    }\n\n    /**\n     * 合并两个条件列表的元素为一个条件列表\n     * \n     * @author SvenAugustus\n     * @param list1 条件列表1\n     * @param list2 条件列表2\n     * @return\n     */\n    private List<Condition> mergeSqlConditionList(List<Condition> list1, List<Condition> list2) {\n      if (list1 == null) {\n        list1 = new ArrayList();\n      }\n      if (list2 == null) {\n        list2 = new ArrayList();\n      }\n      List<Condition> retList = new ArrayList<Condition>();\n      if (!list1.isEmpty() && !(list1.get(0) instanceof Condition)) {\n        return retList;\n      }\n      if (!list2.isEmpty() && !(list2.get(0) instanceof Condition)) {\n        return retList;\n      }\n      retList.addAll(list1);\n      for (int j = 0; j < list2.size(); j++) {\n        boolean exists = false;\n        for (int i = 0; i < list1.size(); i++) {\n          if (sqlConditionEquals(list2.get(j), list1.get(i))) {\n            exists = true;\n            break;\n          }\n        }\n        if (!exists) {\n          retList.add(list2.get(j));\n        }\n      }\n      return retList;\n    }\n    \n    /**\n     * 判定两个条件是否相等\n     * \n     * @author SvenAugustus\n     * @param obj1\n     * @param obj2\n     * @return\n     */\n    private boolean sqlConditionEquals(Condition obj1, Condition obj2) {\n      if (obj1 == obj2) {\n        return true;\n      }\n      if (obj2 == null) {\n        return false;\n      }\n      if (obj1.getClass() != obj2.getClass()) {\n        return false;\n      }\n      Condition other = (Condition) obj2;\n      if (obj1.getColumn() == null) {\n        if (other.getColumn() != null) {\n          return false;\n        }\n      } else if (!obj1.getColumn().equals(other.getColumn())) {\n        return false;\n      }\n      if (obj1.getOperator() == null) {\n        if (other.getOperator() != null) {\n          return false;\n        }\n      } else if (!obj1.getOperator().equals(other.getOperator())) {\n        return false;\n      }\n      if (obj1.getValues() == null) {\n        if (other.getValues() != null) {\n          return false;\n        }\n      } else {\n        boolean notEquals=false;\n        for (Object val1: obj1.getValues()) {\n          for (Object val2: obj2.getValues()) {\n            if(val1==null) {\n              if(val2!=null) {\n                notEquals=true;\n                break;\n              }\n            }else if(!val1.equals(val2)) {\n              notEquals=true;\n              break;\n            }\n          }\n          if(notEquals)break;\n        }\n        if(notEquals)\n        return false;\n      }\n      return true;\n    }\n\n\tpublic Map<String, String> getAliasMap() {\n\t\treturn aliasMap;\n\t}\n\n\tpublic List<String> getSelectTableList() {\n\t\treturn selectTableList;\n\t}\n\n\tpublic String getCurrentTable() {\n\t\treturn currentTable;\n\t}\n\n\tpublic void setCurrentTable(String currentTable) {\n\t\tthis.currentTable = SQLUtils.normalize(currentTable);\n\t}\n\n\t/**\n\t * 清理掉表已经被visit标识\n\t */\n\tprivate void clearTableVisitFlag() {\n\t\tfor (SQLObjectImpl tableSourceExpr : visitedTableSourceExpr) {\n\t\t\tif (tableSourceExpr.getAttribute(TABLE_HAS_VISIT_FLAG) != null) {\n\t\t\t\ttableSourceExpr.putAttribute(TABLE_HAS_VISIT_FLAG, false);\n\t\t\t}\n\t\t}\n\t\tvisitedTableSourceExpr.clear();\n\t}\n\n\t/**\n\t * key全部转变成大写\n\t * \n\t * @param key\n\t * @param value\n\t */\n\tprivate void putAliasToMap(String key, String value) {\n\t\tif (key != null) {\n\t\t\tkey = StringUtil.removeBackquote(key.toUpperCase());\n\t\t\tif (!aliasMap.containsKey(key)) {\n\t\t\t\taliasMap.put(key, StringUtil.removeBackquote(value));\n\t\t\t}\n\n//\t\t\tif (aliasMap.containsKey(key)) {\n//\t\t\t\t\n//\t\t\t\tString msg = String.format(\n//\t\t\t\t\t\t\"The alias[%s] of the table[%s] shoud be unique.If the table has no alias, the alias is equal to the name of the table!\",\n//\t\t\t\t\t\tkey, value);\n//\t\t\t\tthrow new NotSupportException(msg);\n//\t\t\t} else {\n\n\t\t\t// }\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/MycatSelectParser.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport com.alibaba.druid.sql.ast.statement.SQLSelectItem;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQuery;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlSelectParser;\nimport com.alibaba.druid.sql.parser.SQLExprParser;\nimport com.alibaba.druid.sql.parser.Token;\n\n/**\n * Created by nange on 2015/3/13.\n */\npublic class MycatSelectParser extends MySqlSelectParser\n{\n    public MycatSelectParser(SQLExprParser exprParser)\n    {\n        super(exprParser);\n    }\n\n    public MycatSelectParser(String sql)\n    {\n        super(sql);\n    }\n\n\n//public SQLSelectQuery query()\n//{\n//    parseTop();\n//    return super.query();\n//}\n\n    public void parseTop()\n    {\n        if (lexer.token() == Token.TOP)\n        {\n            lexer.nextToken();\n\n            boolean paren = false;\n            if (lexer.token() == Token.LPAREN)\n            {\n                paren = true;\n                lexer.nextToken();\n            }\n\n            if (paren)\n            {\n                accept(Token.RPAREN);\n            }\n\n            if (lexer.token() == Token.LITERAL_INT)\n            {\n                lexer.mark();\n                lexer.nextToken();\n            }\n            if (lexer.token() == Token.IDENTIFIER)\n            {\n                lexer.nextToken();\n\n            }\n            if (lexer.token() == Token.EQ||lexer.token() == Token.DOT)\n            {\n                lexer.nextToken();\n            } else  if(lexer.token() != Token.STAR)\n            {\n                lexer.reset();\n            }\n            if (lexer.token() == Token.PERCENT)\n            {\n                lexer.nextToken();\n            }\n\n\n        }\n\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/MycatStatementParser.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport com.alibaba.druid.sql.ast.SQLName;\nimport com.alibaba.druid.sql.ast.expr.SQLCharExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLLiteralExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\nimport com.alibaba.druid.sql.parser.*;\nimport com.alibaba.druid.util.JdbcConstants;\n\n/**\n * Created by nange on 2015/3/13.\n */\npublic class MycatStatementParser extends MySqlStatementParser\n{\n    private static final String LOW_PRIORITY   = \"LOW_PRIORITY\";\n    private static final String LOCAL          = \"LOCAL\";\n    private static final String IGNORE         = \"IGNORE\";\n    private static final String CHARACTER      = \"CHARACTER\";\n    public MycatStatementParser(String sql)\n    {\n        super(sql);\n        selectExprParser = new MycatExprParser(sql);\n    }\n\n    public MycatStatementParser(Lexer lexer)\n    {\n        super(lexer);\n        selectExprParser = new MycatExprParser(lexer);\n    }\n\n    protected SQLExprParser selectExprParser;\n    @Override\n    public SQLSelectStatement parseSelect()\n    {\n\n        MycatSelectParser selectParser = new MycatSelectParser(this.selectExprParser);\n        return new SQLSelectStatement(selectParser.select(), JdbcConstants.MYSQL);\n    }\n\n\n    //此处注释掉，以修正后端jdbc方式时，delete语句解析出错的情况\n    //\n//    public SQLSelectParser createSQLSelectParser()\n//    {\n//        return new MycatSelectParser(this.selectExprParser);\n//    }\n\n    @Override\n    protected MySqlLoadDataInFileStatement parseLoadDataInFile()\n    {\n        acceptIdentifier(\"DATA\");\n\n        LoadDataStatement stmt = new LoadDataStatement();\n\n        if (identifierEquals(LOW_PRIORITY)) {\n            stmt.setLowPriority(true);\n            lexer.nextToken();\n        }\n\n        if (identifierEquals(\"CONCURRENT\")) {\n            stmt.setConcurrent(true);\n            lexer.nextToken();\n        }\n\n        if (identifierEquals(LOCAL)) {\n            stmt.setLocal(true);\n            lexer.nextToken();\n        }\n\n        acceptIdentifier(\"INFILE\");\n\n        SQLLiteralExpr fileName = (SQLLiteralExpr) exprParser.expr();\n        stmt.setFileName(fileName);\n\n        if (lexer.token() == Token.REPLACE) {\n            stmt.setReplicate(true);\n            lexer.nextToken();\n        }\n\n        if (identifierEquals(IGNORE)) {\n            stmt.setIgnore(true);\n            lexer.nextToken();\n        }\n\n        accept(Token.INTO);\n        accept(Token.TABLE);\n\n        SQLName tableName = exprParser.name();\n        stmt.setTableName(tableName);\n\n        if (identifierEquals(CHARACTER)) {\n            lexer.nextToken();\n            accept(Token.SET);\n\n            if (lexer.token() != Token.LITERAL_CHARS) {\n                throw new ParserException(\"syntax error, illegal charset\");\n            }\n\n            String charset = lexer.stringVal();\n            lexer.nextToken();\n            stmt.setCharset(charset);\n        }\n\n        if (identifierEquals(\"FIELDS\") || identifierEquals(\"COLUMNS\")) {\n            lexer.nextToken();\n            if (identifierEquals(\"TERMINATED\")) {\n                lexer.nextToken();\n                accept(Token.BY);\n                stmt.setColumnsTerminatedBy(new SQLCharExpr(lexer.stringVal()));\n                lexer.nextToken();\n            }\n\n            if (identifierEquals(\"OPTIONALLY\")) {\n                stmt.setColumnsEnclosedOptionally(true);\n                lexer.nextToken();\n            }\n\n            if (identifierEquals(\"ENCLOSED\")) {\n                lexer.nextToken();\n                accept(Token.BY);\n                stmt.setColumnsEnclosedBy(new SQLCharExpr(lexer.stringVal()));\n                lexer.nextToken();\n            }\n\n            if (identifierEquals(\"ESCAPED\")) {\n                lexer.nextToken();\n                accept(Token.BY);\n                stmt.setColumnsEscaped(new SQLCharExpr(lexer.stringVal()));\n                lexer.nextToken();\n            }\n        }\n\n        if (identifierEquals(\"LINES\")) {\n            lexer.nextToken();\n            if (identifierEquals(\"STARTING\")) {\n                lexer.nextToken();\n                accept(Token.BY);\n                stmt.setLinesStartingBy(new SQLCharExpr(lexer.stringVal()));\n                lexer.nextToken();\n            }\n\n            if (identifierEquals(\"TERMINATED\")) {\n                lexer.nextToken();\n                accept(Token.BY);\n                stmt.setLinesTerminatedBy(new SQLCharExpr(lexer.stringVal()));\n                lexer.nextToken();\n            }\n        }\n\n        if (identifierEquals(IGNORE)) {\n            lexer.nextToken();\n            stmt.setIgnoreLinesNumber( this.exprParser.expr());\n            acceptIdentifier(\"LINES\");\n        }\n\n        if (lexer.token() == Token.LPAREN) {\n            lexer.nextToken();\n            this.exprParser.exprList(stmt.getColumns(), stmt);\n            accept(Token.RPAREN);\n        }\n\n        if (lexer.token() == Token.SET) {\n            lexer.nextToken();\n            this.exprParser.exprList(stmt.getSetList(), stmt);\n        }\n\n        return stmt;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/MycatSubQueryVisitor.java",
    "content": "package io.mycat.route.parser.druid;\r\n\r\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\r\nimport com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;\r\n\r\n/**\r\n * 子查询访问器\r\n */\r\npublic class MycatSubQueryVisitor extends MySqlSchemaStatVisitor{\r\n\t\r\n\tprivate boolean relationOr;\r\n\t\r\n\t@Override\r\n\tpublic boolean visit(SQLBinaryOpExpr x) {\r\n\r\n\t\tswitch (x.getOperator()) {\r\n\t            case Equality:\r\n\t            case LessThanOrEqualOrGreaterThan:\r\n\t            case GreaterThan:\r\n\t            case GreaterThanOrEqual:\r\n\t            case LessThan:\r\n\t            case LessThanOrEqual:\r\n\t            case NotLessThan:\r\n\t            case LessThanOrGreater:\r\n   \t\t\t \tcase NotEqual:\r\n   \t\t\t \tcase NotGreaterThan:\t            \t\r\n\t                break;\r\n\t            case BooleanOr:\r\n\t            \trelationOr = true;\r\n\t            \tbreak;\r\n\t            case Like:\r\n\t            case NotLike:\r\n\t            default:\r\n\t                break;\r\n\t        }\r\n\t        return true;\r\n\t}\r\n\r\n\tpublic boolean isRelationOr() {\r\n\t\treturn relationOr;\r\n\t}\r\n\t\r\n\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/RouteCalculateUnit.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport io.mycat.sqlengine.mpp.ColumnRoutePair;\nimport io.mycat.sqlengine.mpp.RangeValue;\n\n/**\n * 路由计算单元\n * \n * @author wang.dw\n * @date 2015-3-14 下午6:24:54\n * @version 0.1.0 \n * @copyright wonhigh.cn\n */\npublic class RouteCalculateUnit {\n\tprivate Map<String, Map<String, Set<ColumnRoutePair>>> tablesAndConditions = new LinkedHashMap<String, Map<String, Set<ColumnRoutePair>>>();\n\n\tpublic Map<String, Map<String, Set<ColumnRoutePair>>> getTablesAndConditions() {\n\t\treturn tablesAndConditions;\n\t}\n\n\tpublic void addShardingExpr(String tableName, String columnName, Object value) {\n\t\tMap<String, Set<ColumnRoutePair>> tableColumnsMap = tablesAndConditions.get(tableName);\n\t\t\n\t\tif (value == null) {\n\t\t\t// where a=null\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (tableColumnsMap == null) {\n\t\t\ttableColumnsMap = new LinkedHashMap<String, Set<ColumnRoutePair>>();\n\t\t\ttablesAndConditions.put(tableName, tableColumnsMap);\n\t\t}\n\t\t\n\t\tString uperColName = columnName.toUpperCase();\n\t\tSet<ColumnRoutePair> columValues = tableColumnsMap.get(uperColName);\n\n\t\tif (columValues == null) {\n\t\t\tcolumValues = new LinkedHashSet<ColumnRoutePair>();\n\t\t\ttablesAndConditions.get(tableName).put(uperColName, columValues);\n\t\t}\n\n\t\tif (value instanceof Object[]) {\n\t\t\tfor (Object item : (Object[]) value) {\n\t\t\t\tif(item == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tcolumValues.add(new ColumnRoutePair(item.toString()));\n\t\t\t}\n\t\t} else if (value instanceof RangeValue) {\n\t\t\tcolumValues.add(new ColumnRoutePair((RangeValue) value));\n\t\t} else {\n\t\t\tcolumValues.add(new ColumnRoutePair(value.toString()));\n\t\t}\n\t}\n\t\n\tpublic void clear() {\n\t\ttablesAndConditions.clear();\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/SchemaStatVisitorFactory.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport io.mycat.config.model.SchemaConfig;\n\n/**\n * 为防止SchemaStatVisitor被污染，采用factory创建\n *\n * Date：2017年12月1日\n * \n * @author SvenAugustus\n * @version 1.0\n * @since JDK 1.7\n */\npublic class SchemaStatVisitorFactory {\n\n  /**\n   * 创建\n   * \n   * @return\n   */\n\tpublic static MycatSchemaStatVisitor create(SchemaConfig schema) {\n\t\tMycatSchemaStatVisitor visitor = new MycatSchemaStatVisitor();\n\t\treturn visitor;\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/SqlMethodInvocationHandler.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;\n\nimport java.sql.SQLNonTransientException;\n\n/**\n * 调用sql函数如now()等\n *\n * @author zhuyiqiang\n * @version 2018/9/3\n */\npublic interface SqlMethodInvocationHandler {\n    /**\n     * 调用sql函数，返回结果\n     * @param expr 函数表达式\n     * @return 执行结果\n     * @throws SQLNonTransientException\n     */\n    String invoke(SQLMethodInvokeExpr expr) throws SQLNonTransientException;\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/SqlMethodInvocationHandlerFactory.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport io.mycat.route.parser.druid.impl.MysqlMethodInvocationHandler;\nimport io.mycat.route.parser.druid.impl.OracleMethodInvocationHandler;\nimport io.mycat.route.parser.druid.impl.PgsqlMethodInvocationHandler;\n\n/**\n * 按不同的db生成对应的sql函数处理器\n *\n * @author zhuyiqiang\n * @version 2018/9/5\n */\npublic class SqlMethodInvocationHandlerFactory {\n    private static MysqlMethodInvocationHandler mysql = new MysqlMethodInvocationHandler();\n    private static OracleMethodInvocationHandler oracle = new OracleMethodInvocationHandler();\n    private static PgsqlMethodInvocationHandler pgsql = new PgsqlMethodInvocationHandler();\n\n    public static MysqlMethodInvocationHandler getForMysql() {\n        return mysql;\n    }\n\n    public static OracleMethodInvocationHandler getForOracle() {\n        return oracle;\n    }\n\n    public static PgsqlMethodInvocationHandler getForPgsql() {\n        return pgsql;\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/WhereUnit.java",
    "content": "package io.mycat.route.parser.druid;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\nimport com.alibaba.druid.stat.TableStat.Condition;\n\n/**\n * Where条件单元\n * \n * @author wang.dw\n * @date 2015-3-17 下午4:21:21\n * @version 0.1.0 \n * @copyright wonhigh.cn\n * \n * 示例：\nSELECT id,traveldate\nFROM   travelrecord\nWHERE  id = 1\n       AND ( fee > 0\n              OR days > 0\n              OR ( traveldate > '2015-05-04 00:00:07.375'\n                   AND ( user_id <= 2\n                          OR fee = days\n                          OR fee > 0 ) ) )\n       AND name = 'zhangsan'\nORDER  BY traveldate DESC\nLIMIT  20 \n * \n * \n * \n */\npublic class WhereUnit {\n\t/**\n\t * 完整的where条件\n\t */\n\tprivate SQLBinaryOpExpr whereExpr;\n\t\n\t/**\n\t * 还能继续再分的表达式:可能还有or关键字\n\t */\n\tprivate SQLBinaryOpExpr canSplitExpr;\n\t\n\tprivate List<SQLExpr> splitedExprList = new ArrayList<SQLExpr>();\n\t\n\tprivate List<List<Condition>> conditionList = new ArrayList<List<Condition>>();\n\t\n\t/**\n\t * whereExpr并不是一个where的全部，有部分条件在outConditions\n\t */\n\tprivate List<Condition> outConditions = new ArrayList<Condition>();\n\n\t/**\n\t * 按照or拆分后的条件片段中可能还有or语句，这样的片段实际上是嵌套的or语句，将其作为内层子whereUnit，不管嵌套多少层，循环处理\n\t */\n\tprivate List<WhereUnit> subWhereUnits = new ArrayList<WhereUnit>();\n\t\n\tprivate boolean finishedParse = false;\n\t\n\tpublic List<Condition> getOutConditions() {\n\t\treturn outConditions;\n\t}\n\n\tpublic void addOutConditions(List<Condition> outConditions) {\n\t\tthis.outConditions.addAll(outConditions);\n\t}\n\t\n\tpublic boolean isFinishedParse() {\n\t\treturn finishedParse;\n\t}\n\n\tpublic void setFinishedParse(boolean finishedParse) {\n\t\tthis.finishedParse = finishedParse;\n\t}\n\n\tpublic WhereUnit() {\n\t}\n\t\n\tpublic WhereUnit(SQLBinaryOpExpr whereExpr) {\n\t\tthis.whereExpr = whereExpr;\n\t\tthis.canSplitExpr = whereExpr;\n\t}\n\n\tpublic SQLBinaryOpExpr getWhereExpr() {\n\t\treturn whereExpr;\n\t}\n\n\tpublic void setWhereExpr(SQLBinaryOpExpr whereExpr) {\n\t\tthis.whereExpr = whereExpr;\n\t}\n\n\tpublic SQLBinaryOpExpr getCanSplitExpr() {\n\t\treturn canSplitExpr;\n\t}\n\n\tpublic void setCanSplitExpr(SQLBinaryOpExpr canSplitExpr) {\n\t\tthis.canSplitExpr = canSplitExpr;\n\t}\n\n\tpublic List<SQLExpr> getSplitedExprList() {\n\t\treturn splitedExprList;\n\t}\n\n\tpublic void addSplitedExpr(SQLExpr splitedExpr) {\n\t\tthis.splitedExprList.add(splitedExpr);\n\t}\n\n\tpublic List<List<Condition>> getConditionList() {\n\t\treturn conditionList;\n\t}\n\n\tpublic void setConditionList(List<List<Condition>> conditionList) {\n\t\tthis.conditionList = conditionList;\n\t}\n\t\n\tpublic void addSubWhereUnit(WhereUnit whereUnit) {\n\t\tthis.subWhereUnits.add(whereUnit);\n\t}\n\t\n\tpublic List<WhereUnit> getSubWhereUnit() {\n\t\treturn this.subWhereUnits;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/DefaultDruidParser.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQuery;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\nimport com.alibaba.druid.stat.TableStat.Condition;\n\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.parser.druid.DruidParser;\nimport io.mycat.route.parser.druid.DruidShardingParseInfo;\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\nimport io.mycat.route.parser.druid.RouteCalculateUnit;\nimport io.mycat.route.parser.druid.SqlMethodInvocationHandler;\nimport io.mycat.route.parser.druid.SqlMethodInvocationHandlerFactory;\nimport io.mycat.sqlengine.mpp.RangeValue;\nimport io.mycat.util.StringUtil;\n\n/**\n * 对SQLStatement解析\n * 主要通过visitor解析和statement解析：有些类型的SQLStatement通过visitor解析足够了，\n *  有些只能通过statement解析才能得到所有信息\n *  有些需要通过两种方式解析才能得到完整信息\n * @author wang.dw\n *\n */\npublic class DefaultDruidParser implements DruidParser {\n\tprotected static final Logger LOGGER = LoggerFactory.getLogger(DefaultDruidParser.class);\n\t/**\n\t * 解析得到的结果\n\t */\n\tprotected DruidShardingParseInfo ctx;\n\t\n\tprivate Map<String,String> tableAliasMap = new HashMap<String,String>();\n\n\tprivate List<Condition> conditions = new ArrayList<Condition>();\n\n\tprotected SqlMethodInvocationHandler invocationHandler;\n\t\n\tpublic Map<String, String> getTableAliasMap() {\n\t\treturn tableAliasMap;\n\t}\n\n\tpublic List<Condition> getConditions() {\n\t\treturn conditions;\n\t}\n\n\tpublic DefaultDruidParser() {\n\t\tinvocationHandler = SqlMethodInvocationHandlerFactory.getForMysql();\n\t}\n\n\t/**\n\t * 使用MycatSchemaStatVisitor解析,得到tables、tableAliasMap、conditions等\n\t * @param schema\n\t * @param stmt\n\t */\n\tpublic void parser(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt, String originSql,LayerCachePool cachePool,MycatSchemaStatVisitor schemaStatVisitor) throws SQLNonTransientException {\n\t\tctx = new DruidShardingParseInfo();\n\t\t//设置为原始sql，如果有需要改写sql的，可以通过修改SQLStatement中的属性，然后调用SQLStatement.toString()得到改写的sql\n\t\tctx.setSql(originSql);\n\t\t//通过visitor解析\n\t\tvisitorParse(schema, rrs, stmt, schemaStatVisitor);\n\n\t\t//通过Statement解析\n\t\tstatementParse(schema, rrs, stmt);\n\t}\n\t\n\t/**\n\t * 是否终止解析,子类可覆盖此方法控制解析进程.\n\t * 存在子查询的情况下,如果子查询需要先执行获取返回结果后,进一步改写sql后,再执行 在这种情况下,不再需要statement 和changeSql 解析。增加此模板方法\n\t * @param schemaStatVisitor\n\t * @return\n\t */\n\tpublic boolean afterVisitorParser(RouteResultset rrs, SQLStatement stmt,MycatSchemaStatVisitor schemaStatVisitor){\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 子类可覆盖（如果visitorParse解析得不到表名、字段等信息的，就通过覆盖该方法来解析）\n\t * 子类覆盖该方法一般是将SQLStatement转型后再解析（如转型为MySqlInsertStatement）\n\t */\n\t@Override\n\tpublic void statementParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt) throws SQLNonTransientException {\n\t\t\n\t}\n\t\n\t/**\n\t * 改写sql：如insert是\n\t */\n\t@Override\n\tpublic void changeSql(SchemaConfig schema, RouteResultset rrs,\n\t\t\tSQLStatement stmt,LayerCachePool cachePool) throws SQLNonTransientException {\n\t\t\n\t}\n\n\t/**\n\t * 子类可覆盖（如果该方法解析得不到表名、字段等信息的，就覆盖该方法，覆盖成空方法，然后通过statementPparse去解析）\n\t * 通过visitor解析：有些类型的Statement通过visitor解析得不到表名、\n\t * @param stmt\n\t */\n\t@Override\n\tpublic void visitorParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt, MycatSchemaStatVisitor visitor)\n\t\t\tthrows SQLNonTransientException {\n\n\t\tstmt.accept(visitor);\n\t\tctx.setVisitor(visitor);\n\n\t\tif(stmt instanceof SQLSelectStatement){\n\t\t\tSQLSelectQuery query = ((SQLSelectStatement) stmt).getSelect().getQuery();\n\t\t\tif(query instanceof MySqlSelectQueryBlock){\n\t\t\t\tif(((MySqlSelectQueryBlock)query).isForUpdate()){\n\t\t\t\t\trrs.setSelectForUpdate(true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tList<List<Condition>> mergedConditionList = new ArrayList<List<Condition>>();\n\t\tif(visitor.hasOrCondition()) {//包含or语句\n\t\t\t//TODO\n\t\t\t//根据or拆分\n\t\t\tmergedConditionList = visitor.splitConditions();\n\t\t} else {//不包含OR语句\n\t\t\tmergedConditionList.add(visitor.getConditions());\n\t\t}\n\t\t\n\t\tif(visitor.isHasChange()){\t// 在解析的过程中子查询被改写了.需要更新ctx.\n\t\t\tctx.setSql(stmt.toString());\n\t\t\trrs.setStatement(ctx.getSql());\n\t\t}\n\t\t\n\t\tbuildTableAliasMap(schema, visitor);\n\t\tctx.setRouteCalculateUnits(this.buildRouteCalculateUnits(visitor, mergedConditionList));\n\t}\n\n\tprivate Map<String, String> buildTableAliasMap(SchemaConfig schema, MycatSchemaStatVisitor visitor) {\n\t\tif (visitor.getAliasMap() == null) {\n\t\t\treturn null;\n\t\t}\n\t\tMap<String, String> tableAliasMap = new HashMap<>();\n\t\tfor (Map.Entry<String, String> entry : visitor.getAliasMap().entrySet()) {\n\t\t\tString key = entry.getKey();\n\t\t\tString value = entry.getValue();\n\t\t\tif (key != null) {\n\t\t\t\tint pos = key.indexOf(\".\");\n\t\t\t\tif (pos > 0) {\n\t\t\t\t\tkey = key.substring(pos + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (value != null) {\n\t\t\t\tint pos = value.indexOf(\".\");\n\t\t\t\tif (pos > 0) {\n\t\t\t\t\tvalue = value.substring(pos + 1);\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tif (key != null && key.charAt(0) == '`') {\n\t\t\t\tkey = key.substring(1, key.length() - 1);\n\t\t\t}\n\t\t\tif (value != null && value.charAt(0) == '`') {\n\t\t\t\tvalue = value.substring(1, value.length() - 1);\n\t\t\t}\n\t\t\t// remove database in database.table\n\t\t\tif (key != null) {\n               int pos = key.indexOf(\".\");\n\t\t\t\t\tif(pos> 0) {\n\t\t\t\t\t\tString schemaInSQL = key.substring(0, pos);\n\t\t\t\t\t\t// 只有sql里面的schema名称和逻辑schema相同时，才去掉schema，防止\"物理schema.物理表名\"被错判为同名的逻辑表，产生路由判断错误\n\t\t\t\t\t\tif (schemaInSQL.equalsIgnoreCase(schema.getName())) {\n\t\t\t\t\t\t\tkey = key.substring(pos + 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tString upperkey = key.toUpperCase();\n\t\t\t\t// String upperValue = value.toUpperCase();\n\n\t\t\t\tif (!tableAliasMap.containsKey(upperkey)) {\n\t\t\t\t\ttableAliasMap.put(upperkey, value);\n\t\t\t\t}\n\n//\t\t\t\tif (!ctx.getTables().contains(upperValue)) {\n//\t\t\t\t\tctx.addTable(upperValue);\n//\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\n\t\t// visitor.getAliasMap().putAll(tableAliasMap);\n        ctx.addTables(visitor.getTables(), schema.getName());\n\t\tctx.setTableAliasMap(visitor.getAliasMap());\n\t\treturn tableAliasMap;\n\t}\n\n\tprivate List<RouteCalculateUnit> buildRouteCalculateUnits(MycatSchemaStatVisitor visitor,\n\t\t\tList<List<Condition>> conditionList) {\n\t\tList<RouteCalculateUnit> retList = new ArrayList<RouteCalculateUnit>();\n\n\t\t//遍历condition ，找分片字段\n\t\tfor(int i = 0; i < conditionList.size(); i++) {\n\t\t\tRouteCalculateUnit routeCalculateUnit = new RouteCalculateUnit();\n\t\t\tfor(Condition condition : conditionList.get(i)) {\n\t\t\t\tList<Object> values = condition.getValues();\n\t\t\t\tif(values.size() == 0) {\n\t\t\t\t\tcontinue;  \n\t\t\t\t}\n\t\t\t\tif(checkConditionValues(values)) {\n\t\t\t\t\tString columnName = StringUtil.removeBackquote(condition.getColumn().getName().toUpperCase());\n\t\t\t\t\tString tableName = StringUtil.removeBackquote(condition.getColumn().getTable().toUpperCase());\n\t\t\t\t\tint index = 0;\n\n\t\t\t\t\t\tif(visitor.getAliasMap() != null && visitor.getAliasMap().get(tableName) != null\n\t\t\t\t\t\t\t&& !visitor.getAliasMap().get(tableName).equals(tableName)) {\n\t\t\t\t\t\ttableName = visitor.getAliasMap().get(tableName);\n\t\t\t\t\t}\n\t\t\t\t\t//处理schema.table的情况\n\t\t\t\t\tif ((index = tableName.indexOf(\".\")) != -1) {\n\t\t\t\t\t\ttableName = tableName.substring(index + 1);\n\t\t\t\t\t}\n\t\t\t\t\ttableName = tableName.toUpperCase();\n//\t\t\t\t\t//确保表名是大写\n//\t\t\t\t\tif(visitor.getAliasMap() != null && visitor.getAliasMap().get(tableName) == null) {//子查询的别名条件忽略掉,不参数路由计算，否则后面找不到表\n//\t\t\t\t\t\tcontinue;\n//\t\t\t\t\t}\n\t\t\t\t\tboolean noFindTable = true;\n\t\t\t\t\tif (visitor.getAliasMap() != null) {\n\t\t\t\t\t\tfor (String value : visitor.getAliasMap().values()) {\n\t\t\t\t\t\t\tif (value.equalsIgnoreCase(tableName)) {\n\t\t\t\t\t\t\t\tnoFindTable = false;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (noFindTable) {// 子查询的别名条件忽略掉,不参数路由计算，否则后面找不到表\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tString operator = condition.getOperator();\n\t\t\t\t\t\n\t\t\t\t\t//只处理between ,in和=3中操作符\n\t\t\t\t\tif(operator.equals(\"between\")) {\n\t\t\t\t\t\tRangeValue rv = new RangeValue(values.get(0), values.get(1), RangeValue.EE);\n\t\t\t\t\t\t\t\trouteCalculateUnit.addShardingExpr(tableName.toUpperCase(), columnName, rv);\n\t\t\t\t\t} else if(operator.equals(\"=\") || operator.toLowerCase().equals(\"in\")){ //只处理=号和in操作符,其他忽略\n\t\t\t\t\t\t\t\trouteCalculateUnit.addShardingExpr(tableName.toUpperCase(), columnName, values.toArray());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tretList.add(routeCalculateUnit);\n\t\t}\n\t\treturn retList;\n\t}\n\t\n\tprivate boolean checkConditionValues(List<Object> values) {\n\t\tfor(Object value : values) {\n\t\t\tif(value != null && !value.toString().equals(\"\")) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\tpublic DruidShardingParseInfo getCtx() {\n\t\treturn ctx;\n\t}\n\n\tpublic void setInvocationHandler(SqlMethodInvocationHandler invocationHandler) {\n\t\tthis.invocationHandler = invocationHandler;\n\t}\n\n\t/**\n\t * 尝试解析某些SQL函数，如now(), sysdate()等\n\t */\n\tprotected String tryInvokeSQLMethod(SQLMethodInvokeExpr expr) throws SQLNonTransientException {\n\t\treturn invocationHandler.invoke(expr);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/DruidAlterTableParser.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport java.sql.SQLNonTransientException;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLAlterTableStatement;\n\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\nimport io.mycat.util.StringUtil;\n\n/**\n * alter table 语句解析\n * @author wang.dw\n *\n */\npublic class DruidAlterTableParser extends DefaultDruidParser {\n\t@Override\n\tpublic void visitorParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt, MycatSchemaStatVisitor visitor)\n\t\t\tthrows SQLNonTransientException {\n\t\t\n\t}\n\t@Override\n\tpublic void statementParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt) throws SQLNonTransientException {\n        SQLAlterTableStatement alterTable = (SQLAlterTableStatement)stmt;\n\tString tableName = StringUtil.removeBackquote(alterTable.getTableSource().toString().toUpperCase());\n//\n\tctx.addTable(tableName);\n\t\t\n\t}\n\n//    public static void main(String[] args)\n//    {\n//        String s=\"SELECT Customer,SUM(OrderPrice) FROM Orders\\n\" +\n//                \"GROUP BY Customer\";\n//        SQLStatementParser parser = new MySqlStatementParser(s);\n//        SQLStatement statement = parser.parseStatement();\n//        System.out.println();\n//    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/DruidCreateTableParser.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport java.sql.SQLNonTransientException;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLCharExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLCharacterDataType;\nimport com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;\nimport com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;\n\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\nimport io.mycat.route.function.SlotFunction;\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\nimport io.mycat.util.StringUtil;\n\n\npublic class DruidCreateTableParser extends DefaultDruidParser {\n\n\t@Override\n\tpublic void visitorParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt,\n\t\t\tMycatSchemaStatVisitor visitor) {\n\t}\n\t\n\t@Override\n\tpublic void statementParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt) throws SQLNonTransientException {\n\t\tMySqlCreateTableStatement createStmt = (MySqlCreateTableStatement)stmt;\n\t\tif(createStmt.getQuery() != null) {\n\t\t\tString msg = \"create table from other table not supported :\" + stmt;\n\t\t\tLOGGER.warn(msg);\n\t\t\tthrow new SQLNonTransientException(msg);\n\t\t}\n\t\tString tableName = StringUtil.removeBackquote(createStmt.getTableSource().toString().toUpperCase());\n\t\tif(schema.getTables().containsKey(tableName)) {\n\t\t\tTableConfig tableConfig = schema.getTables().get(tableName);\n\t\t\tAbstractPartitionAlgorithm algorithm = tableConfig.getRule().getRuleAlgorithm();\n\t\t\tif(algorithm instanceof SlotFunction){\n\t\t\t\tSQLColumnDefinition column = new SQLColumnDefinition();\n\t\t\t\tcolumn.setDataType(new SQLCharacterDataType(\"int\"));\n\t\t\t\tcolumn.setName(new SQLIdentifierExpr(\"_slot\"));\n\t\t\t\tcolumn.setComment(new SQLCharExpr(\"自动迁移算法slot,禁止修改\"));\n\t\t\t\t((SQLCreateTableStatement)stmt).getTableElementList().add(column);\n\t\t\t\tString sql = createStmt.toString();\n\t\t\t\trrs.setStatement(sql);\n\t\t\t\tctx.setSql(sql);\n\t\t\t}\n\t\t}\n\t\tctx.addTable(tableName);\n\t\t\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/DruidDeleteParser.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport java.sql.SQLNonTransientException;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement;\n\nimport io.mycat.MycatServer;\nimport io.mycat.cache.CachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.util.StringUtil;\n\npublic class DruidDeleteParser extends DefaultDruidParser {\n\t@Override\n\tpublic void statementParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt) throws SQLNonTransientException {\n\t\tMySqlDeleteStatement delete = (MySqlDeleteStatement)stmt;\n\t\tString tableName = StringUtil.removeBackquote(delete.getTableName().getSimpleName().toUpperCase());\n\t\tif (!ctx.getTables().contains(tableName)) {\n\t\t\tctx.addTable(tableName);\n\t\t}\n\n\t\t//在解析SQL时清空该表的主键缓存\n\t\tTableConfig tableConfig = schema.getTables().get(tableName);\n\t\tif (tableConfig != null && !tableConfig.primaryKeyIsPartionKey()) {\n\t\t\tString cacheName = schema.getName() + \"_\" + tableName;\n\t\t\tcacheName = cacheName.toUpperCase();\n\t\t\tfor (CachePool value : MycatServer.getInstance().getCacheService().getAllCachePools().values()) {\n\t\t\t\tvalue.clearCache(cacheName);\n\t\t\t\tvalue.getCacheStatic().reset();\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/DruidInsertParser.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport java.sql.SQLNonTransientException;\nimport java.sql.SQLSyntaxErrorException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLCharExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLExprTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLInsertStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;\n\nimport io.mycat.backend.mysql.nio.handler.FetchStoreNodeOfChildTableHandler;\nimport io.mycat.backend.mysql.nio.handler.JDBCFetchStoreNodeOfChildTableHandler;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\nimport io.mycat.route.function.SlotFunction;\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\nimport io.mycat.route.parser.druid.RouteCalculateUnit;\nimport io.mycat.route.parser.util.ParseUtil;\nimport io.mycat.route.util.RouterUtil;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.util.StringUtil;\n\npublic class DruidInsertParser extends DefaultDruidParser {\n\t@Override\n\tpublic void visitorParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt, MycatSchemaStatVisitor visitor)\n\t\t\tthrows SQLNonTransientException {\n\t\t\n\t}\n\t\n\t/**\n\t * 考虑因素：isChildTable、批量、是否分片\n\t */\n\t@Override\n\tpublic void statementParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt) throws SQLNonTransientException {\n\t\tMySqlInsertStatement insert = (MySqlInsertStatement)stmt;\n\t\tString tableName = StringUtil.removeBackquote(insert.getTableName().getSimpleName()).toUpperCase();\n\n\t\tctx.addTable(tableName);\n\t\tif(RouterUtil.isNoSharding(schema,tableName)) {//整个schema都不分库或者该表不拆分\n\t\t\tRouterUtil.routeForTableMeta(rrs, schema, tableName, rrs.getStatement());\n\t\t\trrs.setFinishedRoute(true);\n\t\t\treturn;\n\t\t}\n\n\t\tTableConfig tc = schema.getTables().get(tableName);\n\t\tif(tc == null) {\n\t\t\tString msg = \"can't find table define in schema \"\n\t\t\t\t\t+ tableName + \" schema:\" + schema.getName();\n\t\t\tLOGGER.warn(msg);\n\t\t\tthrow new SQLNonTransientException(msg);\n\t\t} else {\n\t\t\t//childTable的insert直接在解析过程中完成路由\n\t\t\tif (tc.isChildTable()) {\n\t\t\t\tparserChildTable(schema, rrs, tableName, insert);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tString partitionColumn = tc.getPartitionColumn();\n\t\t\t\n\t\t\tif(partitionColumn != null) {//分片表\n\t\t\t\t//拆分表必须给出column list,否则无法寻找分片字段的值\n\t\t\t\tif(insert.getColumns() == null || insert.getColumns().size() == 0) {\n\t\t\t\t\tthrow new SQLSyntaxErrorException(\"partition table, insert must provide ColumnList\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//批量insert\n\t\t\t\tif(isMultiInsert(insert)) {\n//\t\t\t\t\tString msg = \"multi insert not provided\" ;\n//\t\t\t\t\tLOGGER.warn(msg);\n//\t\t\t\t\tthrow new SQLNonTransientException(msg);\n\t\t\t\t\tparserBatchInsert(schema, rrs, partitionColumn, tableName, insert);\n\t\t\t\t} else {\n\t\t\t\t\tparserSingleInsert(schema, rrs, partitionColumn, tableName, insert);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * 寻找joinKey的索引\n\t * @param columns\n\t * @param joinKey\n\t * @return -1表示没找到，>=0表示找到了\n\t */\n\tprivate int getJoinKeyIndex(List<SQLExpr> columns, String joinKey) {\n\t\tfor(int i = 0; i < columns.size(); i++) {\n\t\t\tString col = StringUtil.removeBackquote(columns.get(i).toString()).toUpperCase();\n\t\t\tif(col.equals(joinKey)) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\t\n\t/**\n\t * 是否为批量插入：insert into ...values (),()...或 insert into ...select.....\n\t * @param insertStmt\n\t * @return\n\t */\n\tprivate boolean isMultiInsert(MySqlInsertStatement insertStmt) {\n\t\treturn (insertStmt.getValuesList() != null && insertStmt.getValuesList().size() > 1) || insertStmt.getQuery() != null;\n\t}\n\t\n\tprivate RouteResultset parserChildTable(SchemaConfig schema, RouteResultset rrs,\n\t\t\tString tableName, MySqlInsertStatement insertStmt) throws SQLNonTransientException {\n\t\tTableConfig tc = schema.getTables().get(tableName);\n\t\t\n\t\tString joinKey = tc.getJoinKey();\n\t\tint joinKeyIndex = getJoinKeyIndex(insertStmt.getColumns(), joinKey);\n\t\tif(joinKeyIndex == -1) {\n\t\t\tString inf = \"joinKey not provided :\" + tc.getJoinKey()+ \",\" + insertStmt;\n\t\t\tLOGGER.warn(inf);\n\t\t\tthrow new SQLNonTransientException(inf);\n\t\t}\n\t\tif(isMultiInsert(insertStmt)) {\n\t\t\tString msg = \"ChildTable multi insert not provided\" ;\n\t\t\tLOGGER.warn(msg);\n\t\t\tthrow new SQLNonTransientException(msg);\n\t\t}\n\t\t\n\t\tString joinKeyVal = insertStmt.getValues().getValues().get(joinKeyIndex).toString();\n\n\t\t\n\t\tString sql = insertStmt.toString();\n\t\t\n\t\t// try to route by ER parent partion key\n\t\tRouteResultset theRrs = RouterUtil.routeByERParentKey(null,schema, ServerParse.INSERT,sql, rrs, tc,joinKeyVal);\n\t\tif (theRrs != null) {\n\t\t\trrs.setFinishedRoute(true);\n\t\t\treturn theRrs;\n\t\t}\n\n\t\t// route by sql query root parent's datanode\n\t\tString findRootTBSql = tc.getLocateRTableKeySql().toLowerCase() + joinKeyVal;\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"find root parent's node sql \"+ findRootTBSql);\n\t\t}\n\n\t\tString dn = null;\n\t\tif (tc.getRootParent().getFetchStoreNodeByJdbc()) {\n\t\t\tJDBCFetchStoreNodeOfChildTableHandler jdbcFetchHandler = new JDBCFetchStoreNodeOfChildTableHandler();\n\t\t\tdn = jdbcFetchHandler.execute(schema.getName(),findRootTBSql, tc.getRootParent().getDataNodes());\n\t\t} else {\n\t\t\tFetchStoreNodeOfChildTableHandler FetchHandler = new FetchStoreNodeOfChildTableHandler();\n\t\t\tFetchHandler.execute(schema.getName(),findRootTBSql, tc.getRootParent().getDataNodes());\n\t\t}\n\n\t\tif (dn == null) {\n\t\t\tthrow new SQLNonTransientException(\"can't find (root) parent sharding node for sql:\"+ sql);\n\t\t}\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"found partion node for child table to insert \"+ dn + \" sql :\" + sql);\n\t\t}\n\t\treturn RouterUtil.routeToSingleNode(rrs, dn, sql);\n\t}\n\t\n\t/**\n\t * 单条insert（非批量）\n\t * @param schema\n\t * @param rrs\n\t * @param partitionColumn\n\t * @param tableName\n\t * @param insertStmt\n\t * @throws SQLNonTransientException\n\t */\n\tprivate void parserSingleInsert(SchemaConfig schema, RouteResultset rrs, String partitionColumn,\n\t\t\tString tableName, MySqlInsertStatement insertStmt) throws SQLNonTransientException {\n\t\tboolean isFound = false;\n\t\tfor(int i = 0; i < insertStmt.getColumns().size(); i++) {\n\t\t\tif(partitionColumn.equalsIgnoreCase(StringUtil.removeBackquote(insertStmt.getColumns().get(i).toString()))) {//找到分片字段\n\t\t\t\tisFound = true;\n\t\t\t\tString column = StringUtil.removeBackquote(insertStmt.getColumns().get(i).toString());\n\n\t\t\t\tString shardingValue = StringUtil.removeBackquote(getShardingValue(insertStmt.getValues().getValues().get(i)));\n\t\t\t\tinsertStmt.getValues().getValues().set(i,new SQLCharExpr(shardingValue));\n\t\t\t\tctx.setSql(insertStmt.toString());\n\n\t\t\t\tRouteCalculateUnit routeCalculateUnit = new RouteCalculateUnit();\n\t\t\t\trouteCalculateUnit.addShardingExpr(tableName, column, shardingValue);\n\t\t\t\tctx.addRouteCalculateUnit(routeCalculateUnit);\n\t\t\t\t//mycat是单分片键，找到了就返回\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(!isFound) {//分片表的\n\t\t\tString msg = \"bad insert sql (sharding column:\"+ partitionColumn + \" not provided,\" + insertStmt;\n\t\t\tLOGGER.warn(msg);\n\t\t\tthrow new SQLNonTransientException(msg);\n\t\t}\n\t\t// insert into .... on duplicateKey \n\t\t//such as :INSERT INTO TABLEName (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE b=VALUES(b); \n\t\t//INSERT INTO TABLEName (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=c+1;\n\t\tif(insertStmt.getDuplicateKeyUpdate() != null) {\n\t\t\tList<SQLExpr> updateList = insertStmt.getDuplicateKeyUpdate();\n\t\t\tfor(SQLExpr expr : updateList) {\n\t\t\t\tSQLBinaryOpExpr opExpr = (SQLBinaryOpExpr)expr;\n\t\t\t\tString column = StringUtil.removeBackquote(opExpr.getLeft().toString().toUpperCase());\n\t\t\t\tif(column.equals(partitionColumn)) {\n\t\t\t\t\tString msg = \"Sharding column can't be updated: \" + tableName + \" -> \" + partitionColumn;\n\t\t\t\t\tLOGGER.warn(msg);\n\t\t\t\t\tthrow new SQLNonTransientException(msg);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * insert into .... select .... 或insert into table() values (),(),....\n\t * @param schema\n\t * @param rrs\n\t * @param insertStmt\n\t * @throws SQLNonTransientException\n\t */\n\tprivate void parserBatchInsert(SchemaConfig schema, RouteResultset rrs, String partitionColumn, \n\t\t\tString tableName, MySqlInsertStatement insertStmt) throws SQLNonTransientException {\n\t\t//insert into table() values (),(),....\n\t\tif(insertStmt.getValuesList().size() > 1) {\n\t\t\t//字段列数\n\t\t\tint columnNum = insertStmt.getColumns().size();\n\t\t\tint shardingColIndex = getShardingColIndex(insertStmt, partitionColumn);\n\t\t\tif(shardingColIndex == -1) {\n\t\t\t\tString msg = \"bad insert sql (sharding column:\"+ partitionColumn + \" not provided,\" + insertStmt;\n\t\t\t\tLOGGER.warn(msg);\n\t\t\t\tthrow new SQLNonTransientException(msg);\n\t\t\t} else {\n\t\t\t\tList<ValuesClause> valueClauseList = insertStmt.getValuesList();\n\t\t\t\t\n\t\t\t\tMap<Integer,List<ValuesClause>> nodeValuesMap = new HashMap<Integer,List<ValuesClause>>();\n\t\t\t\tMap<Integer,Integer> slotsMap = new HashMap<>();\n\t\t\t\tTableConfig tableConfig = schema.getTables().get(tableName);\n\t\t\t\tAbstractPartitionAlgorithm algorithm = tableConfig.getRule().getRuleAlgorithm();\n\t\t\t\tfor(ValuesClause valueClause : valueClauseList) {\n\t\t\t\t\tif(valueClause.getValues().size() != columnNum) {\n\t\t\t\t\t\tString msg = \"bad insert sql columnSize != valueSize:\"\n\t\t\t\t\t             + columnNum + \" != \" + valueClause.getValues().size() \n\t\t\t\t\t             + \"values:\" + valueClause;\n\t\t\t\t\t\tLOGGER.warn(msg);\n\t\t\t\t\t\tthrow new SQLNonTransientException(msg);\n\t\t\t\t\t}\n\t\t\t\t\tSQLExpr expr = valueClause.getValues().get(shardingColIndex);\n\t\t\t\t\tString shardingValue = StringUtil.removeBackquote(getShardingValue(expr));\n\t\t\t\t\tvalueClause.getValues().set(shardingColIndex, new SQLCharExpr(shardingValue));\n\n\t\t\t\t\tInteger nodeIndex = algorithm.calculate(StringUtil.removeBackquote(shardingValue));\n\t\t\t\t\tif(algorithm instanceof SlotFunction){\n\t\t\t\t\t\tslotsMap.put(nodeIndex,((SlotFunction) algorithm).slotValue()) ;\n\t\t\t\t\t}\n\t\t\t\t\t//没找到插入的分片\n\t\t\t\t\tif(nodeIndex == null) {\n\t\t\t\t\t\tString msg = \"can't find any valid datanode :\" + tableName \n\t\t\t\t\t\t\t\t+ \" -> \" + partitionColumn + \" -> \" + shardingValue;\n\t\t\t\t\t\tLOGGER.warn(msg);\n\t\t\t\t\t\tthrow new SQLNonTransientException(msg);\n\t\t\t\t\t}\n\t\t\t\t\tif(nodeValuesMap.get(nodeIndex) == null) {\n\t\t\t\t\t\tnodeValuesMap.put(nodeIndex, new ArrayList<ValuesClause>());\n\t\t\t\t\t}\n\t\t\t\t\tnodeValuesMap.get(nodeIndex).add(valueClause);\n\t\t\t\t}\n\n\n\t\t\t\tRouteResultsetNode[] nodes = new RouteResultsetNode[nodeValuesMap.size()];\n\t\t\t\tint count = 0;\n\t\t\t\tfor(Map.Entry<Integer,List<ValuesClause>> node : nodeValuesMap.entrySet()) {\n\t\t\t\t\tInteger nodeIndex = node.getKey();\n\t\t\t\t\tList<ValuesClause> valuesList = node.getValue();\n\t\t\t\t\tinsertStmt.getValuesList().clear();\n\t\t\t\t\tinsertStmt.getValuesList().addAll(valuesList);\n\t\t\t\t\t// insertStmt.setValuesList(valuesList);\n\t\t\t\t\tif(tableConfig.isDistTable()) {\n\t\t\t\t\t\tnodes[count] = new RouteResultsetNode(tableConfig.getDataNodes().get(0),\n\t\t\t\t\t\t\t\trrs.getSqlType(),insertStmt.toString());\n\t\t\t\t\t\tif(tableConfig.getDistTables()==null){\n\t\t\t\t\t\t\tString msg = \" sub table not exists for \" + nodes[count].getName() + \" on \" + tableName;\n\t\t\t\t\t\t\tLOGGER.error(\"DruidMycatRouteStrategyError \" + msg);\n\t\t\t\t\t\t\tthrow new SQLSyntaxErrorException(msg);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tString subTableName = tableConfig.getDistTables().get(nodeIndex);\n\t\t\t\t\t\t\n\t\t\t\t\t\tnodes[count].setSubTableName(subTableName);\n\t\t\t\t\t\tSQLInsertStatement insertStatement = (SQLInsertStatement) insertStmt;\n\t\t\t\t\t\tSQLExprTableSource tableSource = insertStatement.getTableSource();\n\t\t\t\t\t\t//getDisTable 修改表名称\n\t\t\t\t\t\tSQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr();\n\t\t\t\t\t\tsqlIdentifierExpr.setParent(tableSource.getParent());\n\t\t\t\t\t\tsqlIdentifierExpr.setName(subTableName);\n\t\t\t\t\t\tSQLExprTableSource from2 = new SQLExprTableSource(sqlIdentifierExpr);\n\t\t\t\t\t\tinsertStatement.setTableSource(from2);\n\t\t\t\t\t\tnodes[count].setStatement(insertStatement.toString());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnodes[count] = new RouteResultsetNode(tableConfig.getDataNodes().get(nodeIndex),\n\t\t\t\t\t\t\t\trrs.getSqlType(),insertStmt.toString());\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif(algorithm instanceof SlotFunction) {\n\t\t\t\t\t\tnodes[count].setSlot(slotsMap.get(nodeIndex));\n\t\t\t\t\t\tnodes[count].setStatement(ParseUtil.changeInsertAddSlot(nodes[count].getStatement(),nodes[count].getSlot()));\n\t\t\t\t\t}\n\t\t\t\t\tnodes[count++].setSource(rrs);\n\n\t\t\t\t}\n\t\t\t\trrs.setNodes(nodes);\n\t\t\t\trrs.setFinishedRoute(true);\n\n\t\t\t}\n\t\t} else if(insertStmt.getQuery() != null) { // insert into .... select ....\n\t\t\tString msg = \"TODO:insert into .... select .... not supported!\";\n\t\t\tLOGGER.warn(msg);\n\t\t\tthrow new SQLNonTransientException(msg);\n\t\t}\n\t}\n\n\tprivate String getShardingValue(SQLExpr expr) throws SQLNonTransientException {\n\t\tString shardingValue = null;\n\t\tif(expr instanceof SQLIntegerExpr) {\n\t\t\tSQLIntegerExpr intExpr = (SQLIntegerExpr)expr;\n\t\t\tshardingValue = intExpr.getNumber() + \"\";\n\t\t} else if (expr instanceof SQLCharExpr) {\n\t\t\tSQLCharExpr charExpr = (SQLCharExpr)expr;\n\t\t\tshardingValue = charExpr.getText();\n\t\t} else if (expr instanceof SQLMethodInvokeExpr) {\n\t\t\tSQLMethodInvokeExpr methodInvokeExpr = (SQLMethodInvokeExpr)expr;\n\t\t\ttry {\n\t\t\t\tshardingValue = tryInvokeSQLMethod(methodInvokeExpr);\n\t\t\t}catch (Exception e){\n\t\t\t\tLOGGER.error(\"\",e);\n\t\t\t}\n\t\t\tif (shardingValue == null){\n\t\t\t\tshardingValue = expr.toString();\n\t\t\t}\n\t\t} else {\n\t\t\tshardingValue = expr.toString();\n\t\t}\n\t\treturn shardingValue;\n\t}\n\t/**\n\t * 寻找拆分字段在 columnList中的索引\n\t * @param insertStmt\n\t * @param partitionColumn\n\t * @return\n\t */\n\tprivate int getShardingColIndex(MySqlInsertStatement insertStmt,String partitionColumn) {\n\t\tint shardingColIndex = -1;\n\t\tfor(int i = 0; i < insertStmt.getColumns().size(); i++) {\n\t\t\tif(partitionColumn.equalsIgnoreCase(StringUtil.removeBackquote(insertStmt.getColumns().get(i).toString()))) {//找到分片字段\n\t\t\t\tshardingColIndex = i;\n\t\t\t\treturn shardingColIndex;\n\t\t\t}\n\t\t}\n\t\treturn shardingColIndex;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/DruidLockTableParser.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.List;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLockTableStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLockTableStatement.LockType;\n\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.route.parser.druid.DruidParser;\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.util.SplitUtil;\n\n/**\n * lock tables [table] [write|read]语句解析器\n * @author songdabin\n */\npublic class DruidLockTableParser extends DefaultDruidParser implements DruidParser {\n\t@Override\n\tpublic void statementParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt)\n\t\t\tthrows SQLNonTransientException {\n\t\tMySqlLockTableStatement lockTableStat = (MySqlLockTableStatement)stmt;\n\t\tString table = lockTableStat.getItems().get(0).getTableSource().toString().toUpperCase();\n\t\tTableConfig tableConfig = schema.getTables().get(table);\n\t\tif (tableConfig == null) {\n\t\t\tString msg = \"can't find table define of \" + table + \" in schema:\" + schema.getName();\n\t\t\tLOGGER.warn(msg);\n\t\t\tthrow new SQLNonTransientException(msg);\n\t\t}\n\t\tLockType lockType = lockTableStat.getItems().get(0).getLockType();\n\t\tif (LockType.WRITE != lockType && LockType.READ != lockType) {\n\t\t\tString msg = \"lock type must be write or read\";\n\t\t\tLOGGER.warn(msg);\n\t\t\tthrow new SQLNonTransientException(msg);\n\t\t}\n\t\tList<String> dataNodes = tableConfig.getDataNodes();\n\t\tRouteResultsetNode[] nodes = new RouteResultsetNode[dataNodes.size()];\n\t\tfor (int i = 0; i < dataNodes.size(); i ++) {\n\t\t\tnodes[i] = new RouteResultsetNode(dataNodes.get(i), ServerParse.LOCK, stmt.toString());\n\t\t}\n\t\trrs.setNodes(nodes);\n\t\trrs.setFinishedRoute(true);\n\t}\n\t\n\t@Override\n\tpublic void visitorParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt, MycatSchemaStatVisitor visitor)\n\t\t\tthrows SQLNonTransientException {\n\t\t// 对于lock tables table1 write, table2 read类型的多表锁语句，DruidParser只能解析出table1，\n\t\t// 由于多表锁在分布式场景处理逻辑繁琐，且应用场景较少，因此在此处对这种锁表语句进行拦截。\n\t\t// 多表锁的语句在语义上会有\",\"，这里以此为判断依据\n\t\tString sql = rrs.getStatement();\n\t\tsql = sql.replaceAll(\"\\n\", \" \").replaceAll(\"\\t\", \" \");\n\t\tString[] stmts = SplitUtil.split(sql, ',', true);\n\t\t// 如果命令中存在\",\"，则按多表锁的语句来处理\n\t\tif (stmts.length > 1) {\n\t\t\tString tmpStmt = null;\n\t\t\tString tmpWords[] = null;\n\t\t\tfor (int i = 1; i < stmts.length; i ++) {\n\t\t\t\ttmpStmt = stmts[i];\n\t\t\t\ttmpWords = SplitUtil.split(tmpStmt, ' ', true);\n\t\t\t\tif (tmpWords.length==2 && (\"READ\".equalsIgnoreCase(tmpWords[1]) || \"WRITE\".equalsIgnoreCase(tmpWords[1]))) {\n\t\t\t\t\t// 如果符合多表锁的语法，则继续，并在最后提示不能多表锁！\n\t\t\t\t\tcontinue;\n\t\t\t\t} else {\n\t\t\t\t\t// 如果不符合多表锁的语法，则提示语法错误和不能多表锁！\n\t\t\t\t\tthrow new SQLNonTransientException(\"You have an error in your SQL syntax, don't support lock multi tables!\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tLOGGER.error(\"can't lock multi-table\");\n\t\t\tthrow new SQLNonTransientException(\"can't lock multi-table\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/DruidSelectDb2Parser.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.SQLOrderBy;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLAggregateExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;\nimport com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectItem;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQuery;\nimport com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLTableSource;\nimport com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock;\nimport com.alibaba.druid.util.JdbcConstants;\n\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.route.RouteResultset;\n\n\n/**\n * 由于druid的db2解析部分不够完整，且使用oracle的解析基本能满足需求\n * 所以基于oracle的扩展\n */\npublic class DruidSelectDb2Parser extends DruidSelectOracleParser\n{\n\n    protected void parseNativePageSql(SQLStatement stmt, RouteResultset rrs, OracleSelectQueryBlock mysqlSelectQuery, SchemaConfig schema)\n    {\n        //第一层子查询\n        SQLExpr where=  mysqlSelectQuery.getWhere();\n        SQLTableSource from= mysqlSelectQuery.getFrom();\n        if(where instanceof SQLBinaryOpExpr &&from instanceof SQLSubqueryTableSource)\n        {\n\n            SQLBinaryOpExpr one= (SQLBinaryOpExpr) where;\n            SQLExpr left=one.getLeft();\n            SQLBinaryOperator operator =one.getOperator();\n\n                    SQLSelectQuery subSelect = ((SQLSubqueryTableSource) from).getSelect().getQuery();\n                    SQLOrderBy orderBy=null;\n                    if (subSelect instanceof OracleSelectQueryBlock)\n                    {\n                        boolean hasRowNumber=false;\n                        OracleSelectQueryBlock subSelectOracle = (OracleSelectQueryBlock) subSelect;\n                        List<SQLSelectItem> sqlSelectItems=    subSelectOracle.getSelectList();\n                        for (SQLSelectItem sqlSelectItem : sqlSelectItems)\n                        {\n                            SQLExpr sqlExpr=  sqlSelectItem.getExpr()   ;\n                            if(sqlExpr instanceof  SQLAggregateExpr )\n                            {\n                                SQLAggregateExpr agg= (SQLAggregateExpr) sqlExpr;\n                                if(\"row_number\".equalsIgnoreCase(agg.getMethodName())&&agg.getOver()!=null)\n                                {\n                                    hasRowNumber=true;\n                                    orderBy= agg.getOver().getOrderBy();\n                                }\n\n                            }\n                        }\n\n                        if(hasRowNumber)\n                        {\n                            if((operator==SQLBinaryOperator.LessThan||operator==SQLBinaryOperator.LessThanOrEqual) && one.getRight() instanceof SQLIntegerExpr )\n                            {\n                                SQLIntegerExpr right = (SQLIntegerExpr) one.getRight();\n                                int firstrownum = right.getNumber().intValue();\n                                if (operator == SQLBinaryOperator.LessThan&&firstrownum!=0) {\n                                    firstrownum = firstrownum - 1;\n                                }\n                                if (subSelect instanceof OracleSelectQueryBlock)\n                                {\n                                    rrs.setLimitStart(0);\n                                    rrs.setLimitSize(firstrownum);\n                                    mysqlSelectQuery = (OracleSelectQueryBlock) subSelect;    //为了继续解出order by 等\n                                    if(orderBy!=null)\n                                    {\n\t\t\t\t\t\t\t\t\t\tSQLSelect oracleSelect = (SQLSelect) subSelect.getParent();\n                                        oracleSelect.setOrderBy(orderBy);\n                                    }\n                                    parseOrderAggGroupOracle(stmt,rrs, mysqlSelectQuery, schema);\n                                    isNeedParseOrderAgg=false;\n                                }\n                            }\n                            else\n                            if(operator==SQLBinaryOperator.BooleanAnd && left instanceof SQLBinaryOpExpr&&one.getRight() instanceof SQLBinaryOpExpr )\n                            {\n                                SQLBinaryOpExpr leftE= (SQLBinaryOpExpr) left;\n                                SQLBinaryOpExpr rightE= (SQLBinaryOpExpr) one.getRight();\n                                SQLBinaryOpExpr small=null ;\n                                SQLBinaryOpExpr larger=null ;\n                                int firstrownum =0;\n                                int lastrownum =0;\n                                if(leftE.getRight() instanceof SQLIntegerExpr&&(leftE.getOperator()==SQLBinaryOperator.GreaterThan||leftE.getOperator()==SQLBinaryOperator.GreaterThanOrEqual))\n                                {\n                                    small=leftE;\n                                    firstrownum=((SQLIntegerExpr) leftE.getRight()).getNumber().intValue();\n                                    if(leftE.getOperator()==SQLBinaryOperator.GreaterThanOrEqual &&firstrownum!=0) {\n                                        firstrownum = firstrownum - 1;\n                                    }\n                                } else\n                                if(leftE.getRight() instanceof SQLIntegerExpr&&(leftE.getOperator()==SQLBinaryOperator.LessThan||leftE.getOperator()==SQLBinaryOperator.LessThanOrEqual))\n                                {\n                                    larger=leftE;\n                                    lastrownum=((SQLIntegerExpr) leftE.getRight()).getNumber().intValue();\n                                    if(leftE.getOperator()==SQLBinaryOperator.LessThan&&lastrownum!=0) {\n                                        lastrownum = lastrownum - 1;\n                                    }\n                                }\n\n                                if(rightE.getRight() instanceof SQLIntegerExpr&&(rightE.getOperator()==SQLBinaryOperator.GreaterThan||rightE.getOperator()==SQLBinaryOperator.GreaterThanOrEqual))\n                                {\n                                    small=rightE;\n                                    firstrownum=((SQLIntegerExpr) rightE.getRight()).getNumber().intValue();\n                                    if(rightE.getOperator()==SQLBinaryOperator.GreaterThanOrEqual&&firstrownum!=0) {\n                                        firstrownum = firstrownum - 1;\n                                    }\n                                } else\n                                if(rightE.getRight() instanceof SQLIntegerExpr&&(rightE.getOperator()==SQLBinaryOperator.LessThan||rightE.getOperator()==SQLBinaryOperator.LessThanOrEqual))\n                                {\n                                    larger=rightE;\n                                    lastrownum=((SQLIntegerExpr) rightE.getRight()).getNumber().intValue();\n                                    if(rightE.getOperator()==SQLBinaryOperator.LessThan&&lastrownum!=0) {\n                                        lastrownum = lastrownum - 1;\n                                    }\n                                }\n                                if(small!=null&&larger!=null)\n                                {\n                                    setLimitIFChange(stmt, rrs, schema, small, firstrownum, lastrownum);\n\t\t\t\t\t\t\t\t\tif (orderBy != null) {\n\t\t\t\t\t\t\t\t\t\tSQLSelect oracleSelect = (SQLSelect) subSelect.getParent();\n\t\t\t\t\t\t\t\t\t\toracleSelect.setOrderBy(orderBy);\n\t\t\t\t\t\t\t\t\t}\n                                    parseOrderAggGroupOracle(stmt,rrs, (OracleSelectQueryBlock) subSelect, schema);\n                                    isNeedParseOrderAgg=false;\n                                }\n\n                            }\n\n\n                        } else\n                        {\n                            parseNativeSql(stmt,rrs,mysqlSelectQuery,schema);\n                        }\n                    }\n        }\n        else\n        {\n            parseNativeSql(stmt,rrs,mysqlSelectQuery,schema);\n        }\n        if(isNeedParseOrderAgg)\n        {\n            parseOrderAggGroupOracle(stmt,rrs,  mysqlSelectQuery, schema);\n        }\n    }\n\n\n    private static final  Pattern pattern = Pattern.compile(\"FETCH(?:\\\\s)+FIRST(?:\\\\s)+(\\\\d+)(?:\\\\s)+ROWS(?:\\\\s)+ONLY\",Pattern.CASE_INSENSITIVE);\n    protected void parseNativeSql(SQLStatement stmt,RouteResultset rrs, OracleSelectQueryBlock mysqlSelectQuery,SchemaConfig schema)\n    {\n\n        Matcher matcher = pattern.matcher(getCtx().getSql());\n        while (matcher.find())\n        {\n\n          String  row=    matcher.group(1);\n            rrs.setLimitStart(0);\n            rrs.setLimitSize(Integer.parseInt(row));\n        }\n    }\n\n    protected String getCurentDbType()\n    {\n\t\treturn JdbcConstants.DB2.name();\n    }\n\n\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/DruidSelectOracleParser.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.SQLLimit;\nimport com.alibaba.druid.sql.ast.SQLOrderBy;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLAggregateExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;\nimport com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectItem;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQuery;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLTableSource;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\nimport com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock;\nimport com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser;\nimport com.alibaba.druid.util.JdbcConstants;\n\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.route.RouteResultset;\n\npublic class DruidSelectOracleParser extends DruidSelectParser {\n\n    @Override\n\tpublic void statementParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt) {\n\t\tSQLSelectStatement selectStmt = (SQLSelectStatement)stmt;\n\t\tSQLSelectQuery sqlSelectQuery = selectStmt.getSelect().getQuery();\n       //从mysql解析过来\n\t\tif(sqlSelectQuery instanceof MySqlSelectQueryBlock) {\n\t\t\tMySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock)selectStmt.getSelect().getQuery();\n\t\t\tSQLLimit limit = mysqlSelectQuery.getLimit();\n\t\t\tif(limit==null)\n\t\t\t{\n\t\t\t\t  //使用oracle的解析，否则会有部分oracle语法识别错误\n\t\t\t\t  OracleStatementParser oracleParser = new OracleStatementParser(getCtx().getSql());\n\t\t\t\t  SQLSelectStatement oracleStmt = (SQLSelectStatement) oracleParser.parseStatement();\n                selectStmt= oracleStmt;\n\t\t\t\t  SQLSelectQuery oracleSqlSelectQuery = oracleStmt.getSelect().getQuery();\n\t\t\t\t  if(oracleSqlSelectQuery instanceof OracleSelectQueryBlock)\n\t\t\t\t  {\n\t\t\t\t\t  parseNativePageSql(oracleStmt, rrs, (OracleSelectQueryBlock) oracleSqlSelectQuery, schema);\n\t\t\t\t  }\n\n\n\n\t\t\t  }\n\t\t\tif(isNeedParseOrderAgg)\n\t\t\t{\n\t\t\t\tparseOrderAggGroupMysql(schema, selectStmt,rrs, mysqlSelectQuery);\n\t\t\t\t//更改canRunInReadDB属性\n\t\t\t\tif ((mysqlSelectQuery.isForUpdate() || mysqlSelectQuery.isLockInShareMode()) && rrs.isAutocommit() == false)\n\t\t\t\t{\n\t\t\t\t\trrs.setCanRunInReadDB(false);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\n\t}\n\n\n\tprotected void parseOrderAggGroupOracle(SQLStatement stmt, RouteResultset rrs, OracleSelectQueryBlock mysqlSelectQuery, SchemaConfig schema)\n\t{\n\t\tMap<String, String> aliaColumns = parseAggGroupCommon(schema, stmt,rrs, mysqlSelectQuery);\n\n\t\tSQLSelect oracleSelect = (SQLSelect) mysqlSelectQuery.getParent();\n\t\tif(oracleSelect.getOrderBy() != null) {\n\t\t\tList<SQLSelectOrderByItem> orderByItems = oracleSelect.getOrderBy().getItems();\n\t\t\trrs.setOrderByCols(buildOrderByCols(orderByItems,aliaColumns));\n\t\t}\n        isNeedParseOrderAgg=false;\n\t}\n\n\n\tprotected void parseNativePageSql(SQLStatement stmt, RouteResultset rrs, OracleSelectQueryBlock mysqlSelectQuery, SchemaConfig schema)\n\t{\n\t\t//第一层子查询\n\t\tSQLExpr where=  mysqlSelectQuery.getWhere();\n\t\tSQLTableSource from= mysqlSelectQuery.getFrom();\n\t\tif(where instanceof SQLBinaryOpExpr &&from instanceof SQLSubqueryTableSource)\n        {\n\n            SQLBinaryOpExpr one= (SQLBinaryOpExpr) where;\n            SQLExpr left=one.getLeft();\n            SQLBinaryOperator operator =one.getOperator();\n\n              //解析只有一层rownum限制大小\n\t\t\tif(one.getRight() instanceof SQLIntegerExpr &&\"rownum\".equalsIgnoreCase(left.toString())\n\t\t\t\t\t&&(operator==SQLBinaryOperator.LessThanOrEqual||operator==SQLBinaryOperator.LessThan))\n\t\t\t{\n\t\t\t\tSQLIntegerExpr right = (SQLIntegerExpr) one.getRight();\n\t\t\t\tint firstrownum = right.getNumber().intValue();\n\t\t\t\tif (operator == SQLBinaryOperator.LessThan&&firstrownum!=0) {\n\t\t\t\t\tfirstrownum = firstrownum - 1;\n\t\t\t\t}\n\t\t\t\tSQLSelectQuery subSelect = ((SQLSubqueryTableSource) from).getSelect().getQuery();\n\t\t\t\tif (subSelect instanceof OracleSelectQueryBlock)\n\t\t\t\t{\n\t\t\t\t\trrs.setLimitStart(0);\n\t\t\t\t\trrs.setLimitSize(firstrownum);\n\t\t\t\t\tmysqlSelectQuery = (OracleSelectQueryBlock) subSelect;    //为了继续解出order by 等\n\t\t\t\t\tparseOrderAggGroupOracle(stmt,rrs, mysqlSelectQuery, schema);\n\t\t\t\t\tisNeedParseOrderAgg=false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse //解析oracle三层嵌套分页\n            if(one.getRight() instanceof SQLIntegerExpr &&!\"rownum\".equalsIgnoreCase(left.toString())\n                    &&(operator==SQLBinaryOperator.GreaterThan||operator==SQLBinaryOperator.GreaterThanOrEqual))\n           {\n\t\t\t   parseThreeLevelPageSql(stmt, rrs, schema, (SQLSubqueryTableSource) from, one, operator);\n\t\t\t   }\n            else //解析oracle rownumber over分页\n\t\t\t{\n\n                    SQLSelectQuery subSelect = ((SQLSubqueryTableSource) from).getSelect().getQuery();\n\t\t\t\t\tSQLOrderBy orderBy=null;\n                    if (subSelect instanceof OracleSelectQueryBlock)\n                    {\n                        boolean hasRowNumber=false;\n                        OracleSelectQueryBlock subSelectOracle = (OracleSelectQueryBlock) subSelect;\n                        List<SQLSelectItem> sqlSelectItems=    subSelectOracle.getSelectList();\n                        for (SQLSelectItem sqlSelectItem : sqlSelectItems)\n                        {\n                            SQLExpr sqlExpr=  sqlSelectItem.getExpr()   ;\n                            if(sqlExpr instanceof  SQLAggregateExpr )\n                            {\n                                SQLAggregateExpr agg= (SQLAggregateExpr) sqlExpr;\n                                if(\"row_number\".equalsIgnoreCase(agg.getMethodName())&&agg.getOver()!=null)\n                                {\n                                    hasRowNumber=true;\n                                     orderBy= agg.getOver().getOrderBy();\n                                }\n\n                            }\n                        }\n\n                        if(hasRowNumber)\n                        {\n\t\t\t\t\t\t\tif((operator==SQLBinaryOperator.LessThan||operator==SQLBinaryOperator.LessThanOrEqual) && one.getRight() instanceof SQLIntegerExpr )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSQLIntegerExpr right = (SQLIntegerExpr) one.getRight();\n\t\t\t\t\t\t\t\tint firstrownum = right.getNumber().intValue();\n\t\t\t\t\t\t\t\tif (operator == SQLBinaryOperator.LessThan&&firstrownum!=0) {\n\t\t\t\t\t\t\t\t\tfirstrownum = firstrownum - 1;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (subSelect instanceof OracleSelectQueryBlock)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\trrs.setLimitStart(0);\n\t\t\t\t\t\t\t\t\trrs.setLimitSize(firstrownum);\n\t\t\t\t\t\t\t\t\tmysqlSelectQuery = (OracleSelectQueryBlock) subSelect;\n\t\t\t\t\t\t\t\t\tif(orderBy!=null)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tSQLSelect oracleSelect = (SQLSelect) subSelect.getParent();\n\t\t\t\t\t\t\t\t\t\toracleSelect.setOrderBy(orderBy);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tparseOrderAggGroupOracle(stmt,rrs, mysqlSelectQuery, schema);\n\t\t\t\t\t\t\t\t\tisNeedParseOrderAgg=false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t  else\n\t\t\t\t\t\t\tif(operator==SQLBinaryOperator.BooleanAnd && left instanceof SQLBinaryOpExpr&&one.getRight() instanceof SQLBinaryOpExpr )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSQLBinaryOpExpr leftE= (SQLBinaryOpExpr) left;\n\t\t\t\t\t\t\tSQLBinaryOpExpr rightE= (SQLBinaryOpExpr) one.getRight();\n\t\t\t\t\t\t\tSQLBinaryOpExpr small=null ;\n\t\t\t\t\t\t\tSQLBinaryOpExpr larger=null ;\n\t\t\t\t\t\t\tint firstrownum =0;\n\t\t\t\t\t\t\tint lastrownum =0;\n\t\t\t\t\t\t\tif(leftE.getRight() instanceof SQLIntegerExpr&&(leftE.getOperator()==SQLBinaryOperator.GreaterThan||leftE.getOperator()==SQLBinaryOperator.GreaterThanOrEqual))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsmall=leftE;\n\t\t\t\t\t\t\t\tfirstrownum=((SQLIntegerExpr) leftE.getRight()).getNumber().intValue();\n\t\t\t\t\t\t\t\tif(leftE.getOperator()==SQLBinaryOperator.GreaterThanOrEqual &&firstrownum!=0) {\n\t\t\t\t\t\t\t\t\tfirstrownum = firstrownum - 1;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else\n\t\t\t\t\t\t\tif(leftE.getRight() instanceof SQLIntegerExpr&&(leftE.getOperator()==SQLBinaryOperator.LessThan||leftE.getOperator()==SQLBinaryOperator.LessThanOrEqual))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tlarger=leftE;\n\t\t\t\t\t\t\t\tlastrownum=((SQLIntegerExpr) leftE.getRight()).getNumber().intValue();\n\t\t\t\t\t\t\t\tif(leftE.getOperator()==SQLBinaryOperator.LessThan&&lastrownum!=0) {\n\t\t\t\t\t\t\t\t\tlastrownum = lastrownum - 1;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif(rightE.getRight() instanceof SQLIntegerExpr&&(rightE.getOperator()==SQLBinaryOperator.GreaterThan||rightE.getOperator()==SQLBinaryOperator.GreaterThanOrEqual))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsmall=rightE;\n\t\t\t\t\t\t\t\tfirstrownum=((SQLIntegerExpr) rightE.getRight()).getNumber().intValue();\n\t\t\t\t\t\t\t\tif(rightE.getOperator()==SQLBinaryOperator.GreaterThanOrEqual&&firstrownum!=0) {\n\t\t\t\t\t\t\t\t\tfirstrownum = firstrownum - 1;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else\n\t\t\t\t\t\t\tif(rightE.getRight() instanceof SQLIntegerExpr&&(rightE.getOperator()==SQLBinaryOperator.LessThan||rightE.getOperator()==SQLBinaryOperator.LessThanOrEqual))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tlarger=rightE;\n\t\t\t\t\t\t\t\tlastrownum=((SQLIntegerExpr) rightE.getRight()).getNumber().intValue();\n\t\t\t\t\t\t\t\tif(rightE.getOperator()==SQLBinaryOperator.LessThan&&lastrownum!=0) {\n\t\t\t\t\t\t\t\t\tlastrownum = lastrownum - 1;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif(small!=null&&larger!=null)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsetLimitIFChange(stmt, rrs, schema, small, firstrownum, lastrownum);\n\t\t\t\t\t\t\t\tif(orderBy!=null)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tSQLSelect oracleSelect = (SQLSelect) subSelect.getParent();\n\t\t\t\t\t\t\t\t\toracleSelect.setOrderBy(orderBy);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tparseOrderAggGroupOracle(stmt,rrs, (OracleSelectQueryBlock) subSelect, schema);\n\t\t\t\t\t\t\t\tisNeedParseOrderAgg=false;\n\t\t\t\t\t\t\t}\n\n                        }\n\n\n                    } else\n                        {\n                            parseNativeSql(stmt,rrs,mysqlSelectQuery,schema);\n                        }\n\n\n\n                }\n\n        }\n\n\t\t}\n        else\n        {\n            parseNativeSql(stmt,rrs,mysqlSelectQuery,schema);\n        }\n        if(isNeedParseOrderAgg)\n        {\n            parseOrderAggGroupOracle(stmt,rrs,  mysqlSelectQuery, schema);\n        }\n    }\n\n\tprotected String getCurentDbType()\n\t{\n\t\treturn JdbcConstants.ORACLE.name();\n\t}\n    protected void parseNativeSql(SQLStatement stmt,RouteResultset rrs, OracleSelectQueryBlock mysqlSelectQuery,SchemaConfig schema)\n    {\n\t\t //解析分页以外的语法\n    }\n\n\tprivate void parseThreeLevelPageSql(SQLStatement stmt, RouteResultset rrs, SchemaConfig schema, SQLSubqueryTableSource from, SQLBinaryOpExpr one, SQLBinaryOperator operator)\n\t{\n        SQLIntegerExpr right = (SQLIntegerExpr) one.getRight();\n\t\tint firstrownum = right.getNumber().intValue();\n\t\tif (operator == SQLBinaryOperator.GreaterThanOrEqual&&firstrownum!=0) {\n\t\t\tfirstrownum = firstrownum - 1;\n\t\t}\n\t\tSQLSelectQuery subSelect = from.getSelect().getQuery();\n\t\tif (subSelect instanceof OracleSelectQueryBlock)\n        {  //第二层子查询\n            OracleSelectQueryBlock twoSubSelect = (OracleSelectQueryBlock) subSelect;\n            if (twoSubSelect.getWhere() instanceof SQLBinaryOpExpr && twoSubSelect.getFrom() instanceof SQLSubqueryTableSource)\n            {\n                SQLBinaryOpExpr twoWhere = (SQLBinaryOpExpr) twoSubSelect.getWhere();\n                boolean isRowNum = \"rownum\".equalsIgnoreCase(twoWhere.getLeft().toString());\n                boolean isLess = twoWhere.getOperator() == SQLBinaryOperator.LessThanOrEqual || twoWhere.getOperator() == SQLBinaryOperator.LessThan;\n                if (isRowNum && twoWhere.getRight() instanceof SQLIntegerExpr && isLess)\n                {\n                    int lastrownum = ((SQLIntegerExpr) twoWhere.getRight()).getNumber().intValue();\n                    if (operator == SQLBinaryOperator.LessThan&&lastrownum!=0) {\n\t\t\t\t\t\tlastrownum = lastrownum - 1;\n\t\t\t\t\t}\n                    SQLSelectQuery finalQuery = ((SQLSubqueryTableSource) twoSubSelect.getFrom()).getSelect().getQuery();\n                    if (finalQuery instanceof OracleSelectQueryBlock)\n                    {\n\t\t\t\t\t\tsetLimitIFChange(stmt, rrs, schema, one, firstrownum, lastrownum);\n                        parseOrderAggGroupOracle(stmt,rrs, (OracleSelectQueryBlock) finalQuery, schema);\n                        isNeedParseOrderAgg=false;\n                    }\n\n                }\n\n            }\n\n        }\n\t}\n\n\n\n\n\n\t\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/DruidSelectParser.java",
    "content": "package io.mycat.route.parser.druid.impl;\r\n\r\nimport java.sql.SQLNonTransientException;\r\nimport java.sql.SQLSyntaxErrorException;\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.HashMap;\r\nimport java.util.Iterator;\r\nimport java.util.LinkedHashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.Set;\r\nimport java.util.SortedSet;\r\nimport java.util.TreeSet;\r\n\r\nimport org.apache.logging.log4j.util.Strings;\r\n\r\nimport com.alibaba.druid.sql.SQLUtils;\r\nimport com.alibaba.druid.sql.ast.SQLExpr;\r\nimport com.alibaba.druid.sql.ast.SQLLimit;\r\nimport com.alibaba.druid.sql.ast.SQLName;\r\nimport com.alibaba.druid.sql.ast.SQLOrderingSpecification;\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\nimport com.alibaba.druid.sql.ast.expr.SQLAggregateExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;\r\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLNumericLiteralExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLTextLiteralExpr;\r\nimport com.alibaba.druid.sql.ast.statement.SQLExprTableSource;\r\nimport com.alibaba.druid.sql.ast.statement.SQLJoinTableSource;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectItem;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQuery;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\r\nimport com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource;\r\nimport com.alibaba.druid.sql.ast.statement.SQLTableSource;\r\nimport com.alibaba.druid.sql.ast.statement.SQLUnionQuery;\r\nimport com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock;\r\nimport com.alibaba.druid.sql.dialect.db2.visitor.DB2OutputVisitor;\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOrderingExpr;\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\r\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\r\nimport com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor;\r\nimport com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;\r\nimport com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock;\r\nimport com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor;\r\nimport com.alibaba.druid.sql.dialect.postgresql.ast.stmt.PGSelectQueryBlock;\r\nimport com.alibaba.druid.sql.dialect.postgresql.visitor.PGOutputVisitor;\r\nimport com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock;\r\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\r\nimport com.alibaba.druid.sql.visitor.SQLASTOutputVisitor;\r\nimport com.alibaba.druid.util.JdbcConstants;\r\nimport com.alibaba.druid.wall.spi.WallVisitorUtils;\r\n\r\nimport io.mycat.MycatServer;\r\nimport io.mycat.cache.LayerCachePool;\r\nimport io.mycat.config.ErrorCode;\r\nimport io.mycat.config.model.SchemaConfig;\r\nimport io.mycat.config.model.TableConfig;\r\nimport io.mycat.route.RouteResultset;\r\nimport io.mycat.route.RouteResultsetNode;\r\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\r\nimport io.mycat.route.parser.druid.MycatStatementParser;\r\nimport io.mycat.route.parser.druid.RouteCalculateUnit;\r\nimport io.mycat.route.parser.util.PageSQLUtil;\r\nimport io.mycat.route.parser.util.WildcardUtil;\r\nimport io.mycat.route.util.RouterUtil;\r\nimport io.mycat.sqlengine.mpp.ColumnRoutePair;\r\nimport io.mycat.sqlengine.mpp.HavingCols;\r\nimport io.mycat.sqlengine.mpp.MergeCol;\r\nimport io.mycat.sqlengine.mpp.OrderCol;\r\nimport io.mycat.util.ObjectUtil;\r\nimport io.mycat.util.StringUtil;\r\n\r\npublic class DruidSelectParser extends DefaultDruidParser {\r\n\r\n\r\n\tprotected boolean isNeedParseOrderAgg=true;\r\n\r\n\t@Override\r\n\tpublic void statementParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt) {\r\n\t\tSQLSelectStatement selectStmt = (SQLSelectStatement)stmt;\r\n\t\tSQLSelectQuery sqlSelectQuery = selectStmt.getSelect().getQuery();\r\n\t\tif(sqlSelectQuery instanceof MySqlSelectQueryBlock) {\r\n\t\t\tMySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock)selectStmt.getSelect().getQuery();\r\n\r\n\t\t\tparseOrderAggGroupMysql(schema, stmt,rrs, mysqlSelectQuery);\r\n\t\t\t//更改canRunInReadDB属性\r\n\t\t\tif ((mysqlSelectQuery.isForUpdate() || mysqlSelectQuery.isLockInShareMode()) && rrs.isAutocommit() == false)\r\n\t\t\t{\r\n\t\t\t\trrs.setCanRunInReadDB(false);\r\n\t\t\t}\r\n\r\n\t\t} else if (sqlSelectQuery instanceof SQLUnionQuery) {\r\n//\t\t\tMySqlUnionQuery unionQuery = (MySqlUnionQuery)sqlSelectQuery;\r\n//\t\t\tMySqlSelectQueryBlock left = (MySqlSelectQueryBlock)unionQuery.getLeft();\r\n//\t\t\tMySqlSelectQueryBlock right = (MySqlSelectQueryBlock)unionQuery.getLeft();\r\n//\t\t\tSystem.out.println();\r\n\t\t}\r\n\t}\r\n\tprotected void parseOrderAggGroupMysql(SchemaConfig schema, SQLStatement stmt, RouteResultset rrs, MySqlSelectQueryBlock mysqlSelectQuery)\r\n\t{\r\n\t\tMySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();\r\n\t\tstmt.accept(visitor);\r\n//\t\trrs.setGroupByCols((String[])visitor.getGroupByColumns().toArray());\r\n\t\tif(!isNeedParseOrderAgg)\r\n\t\t{\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tMap<String, String> aliaColumns = parseAggGroupCommon(schema, stmt, rrs, mysqlSelectQuery);\r\n\r\n\t\t//setOrderByCols\r\n\t\tif(mysqlSelectQuery.getOrderBy() != null) {\r\n\t\t\tList<SQLSelectOrderByItem> orderByItems = mysqlSelectQuery.getOrderBy().getItems();\r\n\t\t\trrs.setOrderByCols(buildOrderByCols(orderByItems,aliaColumns));\r\n\t\t}\r\n\t\tisNeedParseOrderAgg=false;\r\n\t}\r\n\tprotected Map<String, String> parseAggGroupCommon(SchemaConfig schema, SQLStatement stmt, RouteResultset rrs, SQLSelectQueryBlock mysqlSelectQuery)\r\n\t{\r\n\t\tMap<String, String> aliaColumns = new HashMap<String, String>();\r\n\t\tMap<String, Integer> aggrColumns = new HashMap<String, Integer>();\r\n\t\t// Added by winbill, 20160314, for having clause, Begin ==>\r\n\t\tList<String> havingColsName = new ArrayList<String>();\r\n\t\t// Added by winbill, 20160314, for having clause, End  <==\r\n\t\tList<SQLSelectItem> selectList = mysqlSelectQuery.getSelectList();\r\n\t\tboolean isNeedChangeSql=false;\r\n\t\tint size = selectList.size();\r\n\t\tboolean isDistinct=mysqlSelectQuery.getDistionOption()==2;\r\n\t\tfor (int i = 0; i < size; i++)\r\n\t\t{\r\n\t\t\tSQLSelectItem item = selectList.get(i);\r\n\r\n\t\t\tif (item.getExpr() instanceof SQLAggregateExpr)\r\n\t\t\t{\r\n\t\t\t\tSQLAggregateExpr expr = (SQLAggregateExpr) item.getExpr();\r\n\t\t\t\tString method = expr.getMethodName().toUpperCase();\r\n\t\t\t\tboolean isHasArgument=!expr.getArguments().isEmpty();\r\n\t\t\t\tif(isHasArgument)\r\n\t\t\t\t{\r\n\t\t\t\t\tString aggrColName = method + \"(\" + expr.getArguments().get(0) + \")\";   // Added by winbill, 20160314, for having clause\r\n\t\t\t\t\thavingColsName.add(aggrColName);       // Added by winbill, 20160314, for having clause\r\n\t\t\t\t}\r\n\t\t\t\t//只处理有别名的情况，无别名添加别名，否则某些数据库会得不到正确结果处理\r\n\t\t\t\tint mergeType = MergeCol.getMergeType(method);\r\n\t\t\t\tif (MergeCol.MERGE_AVG == mergeType&&isRoutMultiNode(schema,rrs))\r\n\t\t\t\t{    //跨分片avg需要特殊处理，直接avg结果是不对的\r\n\t\t\t\t\tString colName = item.getAlias() != null ? item.getAlias() : method + i;\r\n\t\t\t\t\tSQLSelectItem sum =new SQLSelectItem();\r\n\t\t\t\t\tString sumColName = colName + \"SUM\";\r\n\t\t\t\t\tsum.setAlias(sumColName);\r\n\t\t\t\t\tSQLAggregateExpr sumExp =new SQLAggregateExpr(\"SUM\");\r\n\t\t\t\t\tObjectUtil.copyProperties(expr,sumExp);\r\n\t\t\t\t\tsumExp.getArguments().addAll(expr.getArguments());\r\n\t\t\t\t\tsumExp.setMethodName(\"SUM\");\r\n\t\t\t\t\tsum.setExpr(sumExp);\r\n\t\t\t\t\tselectList.set(i, sum);\r\n\t\t\t\t\taggrColumns.put(sumColName, MergeCol.MERGE_SUM);\r\n\t\t\t\t\thavingColsName.add(sumColName);    // Added by winbill, 20160314, for having clause\r\n\t\t\t\t\thavingColsName.add(item.getAlias() != null ? item.getAlias() : \"\");    // Added by winbill, 20160314, two aliases for AVG\r\n\r\n\t\t\t\t\tSQLSelectItem count =new SQLSelectItem();\r\n\t\t\t\t\tString countColName = colName + \"COUNT\";\r\n\t\t\t\t\tcount.setAlias(countColName);\r\n\t\t\t\t\tSQLAggregateExpr countExp = new SQLAggregateExpr(\"COUNT\");\r\n\t\t\t\t\tObjectUtil.copyProperties(expr,countExp);\r\n\t\t\t\t\tcountExp.getArguments().addAll(expr.getArguments());\r\n\t\t\t\t\tcountExp.setMethodName(\"COUNT\");\r\n\t\t\t\t\tcount.setExpr(countExp);\r\n\t\t\t\t\tselectList.add(count);\r\n\t\t\t\t\taggrColumns.put(countColName, MergeCol.MERGE_COUNT);\r\n\r\n\t\t\t\t\tisNeedChangeSql=true;\r\n\t\t\t\t\taggrColumns.put(colName, mergeType);\r\n\t\t\t\t\trrs.setHasAggrColumn(true);\r\n\t\t\t\t} else if (MergeCol.MERGE_UNSUPPORT != mergeType){\r\n\t\t\t\t\tString aggColName = null;\r\n\t\t\t\t\tStringBuilder sb = new StringBuilder();\r\n\t\t\t\t\tif(mysqlSelectQuery instanceof MySqlSelectQueryBlock) {\r\n\t\t\t\t\t\texpr.accept(new MySqlOutputVisitor(sb));\r\n\t\t\t\t\t} else if(mysqlSelectQuery instanceof OracleSelectQueryBlock) {\r\n\t\t\t\t\t\texpr.accept(new OracleOutputVisitor(sb));\r\n\t\t\t\t\t} else if(mysqlSelectQuery instanceof PGSelectQueryBlock){\r\n\t\t\t\t\t\texpr.accept(new PGOutputVisitor(sb));\r\n\t\t\t\t\t} else if(mysqlSelectQuery instanceof SQLServerSelectQueryBlock) {\r\n\t\t\t\t\t\texpr.accept(new SQLASTOutputVisitor(sb));\r\n\t\t\t\t\t} else if(mysqlSelectQuery instanceof DB2SelectQueryBlock) {\r\n\t\t\t\t\t\texpr.accept(new DB2OutputVisitor(sb));\r\n\t\t\t\t\t}\r\n\t\t\t\t\taggColName = sb.toString();\r\n\r\n\t\t\t\t\tif (item.getAlias() != null && item.getAlias().length() > 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\taggrColumns.put(item.getAlias(), mergeType);\r\n\t\t\t\t\t\taliaColumns.put(aggColName,item.getAlias());\r\n\t\t\t\t\t} else\r\n\t\t\t\t\t{   //如果不加，jdbc方式时取不到正确结果   ;修改添加别名\r\n\t\t\t\t\t\titem.setAlias(method + i);\r\n\t\t\t\t\t\taggrColumns.put(method + i, mergeType);\r\n\t\t\t\t\t\taliaColumns.put(aggColName, method + i);\r\n\t\t\t\t\t\tisNeedChangeSql=true;\r\n\t\t\t\t\t}\r\n\t\t\t\t\trrs.setHasAggrColumn(true);\r\n\t\t\t\t\thavingColsName.add(item.getAlias());   // Added by winbill, 20160314, for having clause\r\n\t\t\t\t\thavingColsName.add(\"\");                // Added by winbill, 20160314, one alias for non-AVG\r\n\t\t\t\t}\r\n\t\t\t} else\r\n\t\t\t{\r\n\t\t\t\tif (!(item.getExpr() instanceof SQLAllColumnExpr))\r\n\t\t\t\t{\r\n\t\t\t\t\tString alia = item.getAlias();\r\n\t\t\t\t\tString field = getFieldName(item);\r\n\t\t\t\t\tif (alia == null)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\talia = field;\r\n\t\t\t\t\t}\r\n\t\t\t\t\taliaColumns.put(field, alia);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t}\r\n\t\tif(aggrColumns.size() > 0) {\r\n\t\t\trrs.setMergeCols(aggrColumns);\r\n\t\t}\r\n\r\n\t\t//通过优化转换成group by来实现\r\n\t\tif(isDistinct)\r\n\t\t{\r\n\t\t\tmysqlSelectQuery.setDistionOption(0);\r\n\t\t\tSQLSelectGroupByClause   groupBy=new SQLSelectGroupByClause();\r\n\t\t\tfor (String fieldName : aliaColumns.keySet())\r\n\t\t\t{\r\n\t\t\t\tgroupBy.addItem(new SQLIdentifierExpr(fieldName));\r\n\t\t\t}\r\n\t\t\tmysqlSelectQuery.setGroupBy(groupBy);\r\n\t\t\tisNeedChangeSql=true;\r\n\t\t}\r\n\r\n\r\n\t\t//setGroupByCols\r\n\t\tif(mysqlSelectQuery.getGroupBy() != null) {\r\n\t\t\tList<SQLExpr> groupByItems = mysqlSelectQuery.getGroupBy().getItems();\r\n\t\t\tString[] groupByCols = buildGroupByCols(groupByItems,aliaColumns);\r\n\t\t\trrs.setGroupByCols(groupByCols);\r\n\t\t\trrs.setHavings(buildGroupByHaving(mysqlSelectQuery.getGroupBy().getHaving(),aliaColumns));\r\n\t\t\trrs.setHasAggrColumn(true);\r\n\t\t\trrs.setHavingColsName(havingColsName.toArray()); // Added by winbill, 20160314, for having clause\r\n\t\t}\r\n\r\n\r\n\t\tif (isNeedChangeSql)\r\n\t\t{\r\n\t\t\tString sql = stmt.toString();\r\n\t\t\trrs.changeNodeSqlAfterAddLimit(schema,getCurentDbType(),sql,0,-1, false);\r\n\t\t\tgetCtx().setSql(sql);\r\n\t\t}\r\n\t\treturn aliaColumns;\r\n\t}\r\n\r\n\tprivate HavingCols buildGroupByHaving(SQLExpr having,Map<String, String> aliaColumns ){\r\n\t\tif (having == null) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\tSQLBinaryOpExpr expr  = ((SQLBinaryOpExpr) having);\r\n\t\tSQLExpr left = expr.getLeft();\r\n\t\tSQLBinaryOperator operator = expr.getOperator();\r\n\t\tSQLExpr right = expr.getRight();\r\n\r\n\t\tString leftValue = null;;\r\n\t\tif (left instanceof SQLAggregateExpr) {\r\n\t\t\tleftValue = ((SQLAggregateExpr) left).getMethodName() + \"(\"\r\n\t\t\t\t\t+ ((SQLAggregateExpr) left).getArguments().get(0) + \")\";\r\n\t\t\tString aggrColumnAlias = getAliaColumn(aliaColumns,leftValue);\r\n\t\t\tif(aggrColumnAlias != null) { // having聚合函数存在别名\r\n\t\t\t\texpr.setLeft(new SQLIdentifierExpr(aggrColumnAlias));\r\n\t\t\t\tleftValue = aggrColumnAlias;\r\n\t\t\t}\r\n\t\t} else if (left instanceof SQLIdentifierExpr) {\r\n\t\t\tleftValue = ((SQLIdentifierExpr) left).getName();\r\n\t\t}\r\n\r\n\t\tString rightValue = null;\r\n\t\tif (right instanceof  SQLNumericLiteralExpr) {\r\n\t\t\trightValue = right.toString();\r\n\t\t}else if(right instanceof SQLTextLiteralExpr){\r\n\t\t\trightValue = StringUtil.removeBackquote(right.toString());\r\n\t\t}\r\n\r\n\t\treturn new HavingCols(leftValue,rightValue,operator.getName());\r\n\t}\r\n\r\n\tprivate boolean isRoutMultiNode(SchemaConfig schema,  RouteResultset rrs)\r\n\t{\r\n\t\tif(rrs.getNodes()!=null&&rrs.getNodes().length>1)\r\n\t\t{\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tLayerCachePool tableId2DataNodeCache = (LayerCachePool) MycatServer.getInstance().getCacheService().getCachePool(\"TableID2DataNodeCache\");\r\n\t\ttry\r\n\t\t{\r\n\t\t\ttryRoute(schema, rrs, tableId2DataNodeCache);\r\n\t\t\tif(rrs.getNodes()!=null&&rrs.getNodes().length>1)\r\n\t\t\t{\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t} catch (SQLNonTransientException e)\r\n\t\t{\r\n\t\t\tthrow new RuntimeException(e);\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\tprivate String getFieldName(SQLSelectItem item){\r\n\t\tif ((item.getExpr() instanceof SQLPropertyExpr)||(item.getExpr() instanceof SQLMethodInvokeExpr)\r\n\t\t\t\t|| (item.getExpr() instanceof SQLIdentifierExpr) || item.getExpr() instanceof SQLBinaryOpExpr) {\r\n\t\t\treturn item.getExpr().toString();//字段别名\r\n\t\t}\r\n\t\telse if (!StringUtil.isEmpty(item.getAlias())) { // add by hehuang 20181205 如果SelectItem存在别名，则认为表达式为字段名，sql语法支持常量作为字段\r\n\t\t\treturn item.getExpr().toString();\r\n\t\t} else {\r\n\t\t\treturn item.toString();\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 现阶段目标为 有一个只涉及到一张表的子查询时,先执行子查询,获得返回结果后,改写原有sql继续执行,得到最终结果.\r\n\t * 在这种情况下,原sql不需要继续解析.\r\n\t * 使用catlet 的情况也不再继续解析.\r\n\t */\r\n\t@Override\r\n\tpublic boolean afterVisitorParser(RouteResultset rrs, SQLStatement stmt, MycatSchemaStatVisitor visitor) {\r\n\t\tint subQuerySize = visitor.getSubQuerys().size();\r\n\r\n\t\tif(subQuerySize==0&&ctx.getTables().size()==2){ //两表关联,考虑使用catlet\r\n\t\t\tif(ctx.getVisitor().getConditions() !=null && ctx.getVisitor().getConditions().size()>0){\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}else if(subQuerySize==1){     //只涉及一张表的子查询,使用  MiddlerResultHandler 获取中间结果后,改写原有 sql 继续执行 TODO 后期可能会考虑多个.\r\n\t\t\tSQLSelectQuery sqlSelectQuery = visitor.getSubQuerys().iterator().next().getQuery();\r\n\t\t\tif(((MySqlSelectQueryBlock)sqlSelectQuery).getFrom() instanceof SQLExprTableSource) {\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn super.afterVisitorParser(rrs, stmt, visitor);\r\n\t}\r\n\r\n\t/**\r\n\t * 改写sql：需要加limit的加上\r\n\t */\r\n\t@Override\r\n\tpublic void changeSql(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt,LayerCachePool cachePool) throws SQLNonTransientException {\r\n\r\n\t\ttryRoute(schema, rrs, cachePool);\r\n\r\n\t\trrs.copyLimitToNodes();\r\n\r\n\t\tSQLSelectStatement selectStmt = (SQLSelectStatement)stmt;\r\n\t\tSQLSelectQuery sqlSelectQuery = selectStmt.getSelect().getQuery();\r\n\t\tif(sqlSelectQuery instanceof MySqlSelectQueryBlock) {\r\n\t\t\tMySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock)selectStmt.getSelect().getQuery();\r\n\t\t\tint limitStart = 0;\r\n\t\t\tint limitSize = schema.getDefaultMaxLimit();\r\n\r\n\t\t\t//clear group having\r\n\t\t\tSQLSelectGroupByClause groupByClause = mysqlSelectQuery.getGroupBy();\r\n\t\t\t// Modified by winbill, 20160614, do NOT include having clause when routing to multiple nodes\r\n\t\t\tif(groupByClause != null && groupByClause.getHaving() != null && isRoutMultiNode(schema,rrs)){\r\n\t\t\t\tgroupByClause.setHaving(null);\r\n\t\t\t}\r\n\r\n\t\t\tMap<String, Map<String, Set<ColumnRoutePair>>> allConditions = getAllConditions();\r\n\t\t\tboolean isNeedAddLimit = isNeedAddLimit(schema, rrs, mysqlSelectQuery, allConditions);\r\n\t\t\tif(isNeedAddLimit) {\r\n\t\t\t\tSQLLimit limit = new SQLLimit();\r\n\t\t\t\tlimit.setRowCount(new SQLIntegerExpr(limitSize));\r\n\t\t\t\tmysqlSelectQuery.setLimit(limit);\r\n\t\t\t\trrs.setLimitSize(limitSize);\r\n\t\t\t\tString sql= getSql(rrs, stmt, isNeedAddLimit);\r\n\t\t\t\trrs.changeNodeSqlAfterAddLimit(schema, getCurentDbType(), sql, 0, limitSize, true);\r\n\r\n\t\t\t}\r\n\t\t\tSQLLimit limit = mysqlSelectQuery.getLimit();\r\n\t\t\tif(limit != null&&!isNeedAddLimit) {\r\n\t\t\t\tSQLIntegerExpr offset = (SQLIntegerExpr)limit.getOffset();\r\n\t\t\t\tSQLIntegerExpr count = (SQLIntegerExpr)limit.getRowCount();\r\n\t\t\t\tif(offset != null) {\r\n\t\t\t\t\tlimitStart = offset.getNumber().intValue();\r\n\t\t\t\t\trrs.setLimitStart(limitStart);\r\n\t\t\t\t}\r\n\t\t\t\tif(count != null) {\r\n\t\t\t\t\tlimitSize = count.getNumber().intValue();\r\n\t\t\t\t\trrs.setLimitSize(limitSize);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(isNeedChangeLimit(rrs)) {\r\n\t\t\t\t\tSQLLimit changedLimit = new SQLLimit();\r\n\t\t\t\t\tchangedLimit.setRowCount(new SQLIntegerExpr(limitStart + limitSize));\r\n\r\n\t\t\t\t\tif(offset != null) {\r\n\t\t\t\t\t\tif(limitStart < 0) {\r\n\t\t\t\t\t\t\tString msg = \"You have an error in your SQL syntax; check the manual that \" +\r\n\t\t\t\t\t\t\t\t\t\"corresponds to your MySQL server version for the right syntax to use near '\" + limitStart + \"'\";\r\n\t\t\t\t\t\t\tthrow new SQLNonTransientException(ErrorCode.ER_PARSE_ERROR + \" - \" + msg);\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\tchangedLimit.setOffset(new SQLIntegerExpr(0));\r\n\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tmysqlSelectQuery.setLimit(changedLimit);\r\n\r\n\t\t\t\t\tString sql= getSql(rrs, stmt, isNeedAddLimit);\r\n\t\t\t\t\trrs.changeNodeSqlAfterAddLimit(schema,getCurentDbType(),sql,0, limitStart + limitSize, true);\r\n\r\n\t\t\t\t\t//设置改写后的sql\r\n\t\t\t\t\tctx.setSql(sql);\r\n\r\n\t\t\t\t}   else\r\n\t\t\t\t{\r\n\r\n\t\t\t\t\trrs.changeNodeSqlAfterAddLimit(schema,getCurentDbType(),getCtx().getSql(),rrs.getLimitStart(), rrs.getLimitSize(), true);\r\n\t\t\t\t\t//\tctx.setSql(nativeSql);\r\n\r\n\t\t\t\t}\r\n\r\n\r\n\t\t\t}\r\n\r\n\t\t\tif(rrs.isDistTable()){\r\n              \r\n                for (RouteResultsetNode node : rrs.getNodes()) {\r\n                    String sql = selectStmt.toString();\r\n                    SQLStatementParser parser = null;\r\n                    if (schema.isNeedSupportMultiDBType()) {\r\n                        parser = new MycatStatementParser(sql);\r\n                    } else {\r\n                        parser = new MySqlStatementParser(sql); \r\n                    }\r\n                    \r\n                    SQLSelectStatement newStmt = null;\r\n                    try {\r\n                        newStmt = (SQLSelectStatement) parser.parseStatement();\r\n                    } catch (Exception t) {\r\n                        LOGGER.error(\"DruidMycatRouteStrategyError\", t);\r\n                        throw new SQLSyntaxErrorException(t);\r\n                    }\r\n                    \r\n                    MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) newStmt.getSelect().getQuery();\r\n                    SQLTableSource from2 = query.getFrom();\r\n                    if (from2 instanceof SQLSubqueryTableSource) {\r\n                        SQLSubqueryTableSource from = (SQLSubqueryTableSource) from2;\r\n                        MySqlSelectQueryBlock query2 = (MySqlSelectQueryBlock) from.getSelect().getQuery();\r\n                        repairExpr(query2.getFrom(), node);\r\n                        node.setStatement(newStmt.toString());\r\n                    } else {\r\n                        repairExpr(from2, node);\r\n                        node.setStatement(newStmt.toString());\r\n                    }\r\n                }\r\n                \r\n            }\r\n\r\n\t\t\trrs.setCacheAble(isNeedCache(schema, rrs, mysqlSelectQuery, allConditions));\r\n\t\t}\r\n\r\n\t}\r\n\t\r\n\tprivate void repairExpr(SQLTableSource source, RouteResultsetNode node) throws SQLNonTransientException {\r\n        Map<String, String> subTableNames = node.getSubTableNames();\r\n        \r\n        if (source instanceof SQLJoinTableSource) {\r\n            SQLJoinTableSource joinsource = (SQLJoinTableSource) source;\r\n            SQLTableSource right = joinsource.getRight();\r\n            String alias = right.getAlias();\r\n            \r\n            if(right instanceof SQLExprTableSource) {\r\n                SQLIdentifierExpr expr = (SQLIdentifierExpr) ((SQLExprTableSource) right).getExpr();\r\n                alias = Strings.isBlank(alias) ? expr.getName() : alias;\r\n                String subTableName = subTableNames.get(WildcardUtil.wildcard(expr.getName().toUpperCase()));\r\n                if (Strings.isNotBlank(subTableName)) {\r\n                    String tableName = expr.getName();\r\n                    alias = Strings.isBlank(alias) ? tableName : alias;\r\n                    right.setAlias(alias);\r\n                    \r\n                    expr.setName(subTableName);\r\n                }\r\n            } \r\n            \r\n            if (right instanceof SQLSubqueryTableSource) {\r\n                SQLSubqueryTableSource from = (SQLSubqueryTableSource) right;\r\n                MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) from.getSelect().getQuery();\r\n                repairExpr(query.getFrom(), node);\r\n            }\r\n            \r\n            repairExpr(joinsource.getLeft(), node);\r\n        } else if (source instanceof SQLExprTableSource) {\r\n            SQLExprTableSource exprTableSource = (SQLExprTableSource) source;\r\n            String alias = exprTableSource.getAlias();\r\n            \r\n            SQLIdentifierExpr expr = (SQLIdentifierExpr) exprTableSource.getExpr();\r\n            alias = Strings.isBlank(alias) ? expr.getName() : alias;\r\n            String subTableName = subTableNames.get(WildcardUtil.wildcard(expr.getName().toUpperCase()));\r\n            if (Strings.isNotBlank(subTableName)) {\r\n                String tableName = expr.getName();\r\n                alias = Strings.isBlank(alias) ? tableName : alias;\r\n                exprTableSource.setAlias(alias);\r\n                expr.setName(subTableName);\r\n            }\r\n            \r\n        } else if (source instanceof  SQLSubqueryTableSource) {\r\n            SQLSelect select = ((SQLSubqueryTableSource) source).getSelect();\r\n            MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) select.getQuery();\r\n            repairExpr(query.getFrom(), node);\r\n        }\r\n    }\r\n\r\n\tprivate void fixLimit(MySqlSelectQueryBlock mysqlSelectQuery, RouteResultsetNode node) {\r\n\t\tif(!getCurentDbType().equalsIgnoreCase(\"mysql\")) {\r\n\t\t\tSQLLimit _limit = mysqlSelectQuery.getLimit();\r\n\t\t\tif (_limit != null) {\r\n\t\t\t\tSQLIntegerExpr offset = (SQLIntegerExpr) _limit.getOffset();\r\n\t\t\t\tSQLIntegerExpr count = (SQLIntegerExpr) _limit.getRowCount();\r\n\t\t\t\tif (offset != null && count != null) {\r\n\t\t\t\t\tString nativeSql = PageSQLUtil\r\n\t\t\t\t\t\t\t.convertLimitToNativePageSql(getCurentDbType(), node.getStatement(),\r\n\t\t\t\t\t\t\t\t\toffset.getNumber().intValue(), count.getNumber().intValue());\r\n\t\t\t\t\tnode.setStatement(nativeSql);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 获取所有的条件：因为可能被or语句拆分成多个RouteCalculateUnit，条件分散了\r\n\t * @return\r\n\t */\r\n\tprivate Map<String, Map<String, Set<ColumnRoutePair>>> getAllConditions() {\r\n\t\tMap<String, Map<String, Set<ColumnRoutePair>>> map = new HashMap<String, Map<String, Set<ColumnRoutePair>>>();\r\n\t\tfor(RouteCalculateUnit unit : ctx.getRouteCalculateUnits()) {\r\n\t\t\tif(unit != null && unit.getTablesAndConditions() != null) {\r\n\t\t\t\tmap.putAll(unit.getTablesAndConditions());\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn map;\r\n\t}\r\n\r\n\tprivate void tryRoute(SchemaConfig schema, RouteResultset rrs, LayerCachePool cachePool) throws SQLNonTransientException {\r\n\t\tif(rrs.isFinishedRoute())\r\n\t\t{\r\n\t\t\treturn;//避免重复路由\r\n\t\t}\r\n\r\n\t\t//无表的select语句直接路由带任一节点\r\n\t\tif((ctx.getTables() == null || ctx.getTables().size() == 0)&&(ctx.getTableAliasMap()==null||ctx.getTableAliasMap().isEmpty())) {\r\n\t\t\trrs = RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), ctx.getSql());\r\n\t\t\trrs.setFinishedRoute(true);\r\n\t\t\treturn;\r\n\t\t}\r\n//\t\tRouterUtil.tryRouteForTables(schema, ctx, rrs, true, cachePool);\r\n\t\tSortedSet<RouteResultsetNode> nodeSet = new TreeSet<RouteResultsetNode>();\r\n\t\tboolean isAllGlobalTable = RouterUtil.isAllGlobalTable(ctx, schema);\r\n\t\tfor (RouteCalculateUnit unit : ctx.getRouteCalculateUnits()) {\r\n\t\t\tRouteResultset rrsTmp = RouterUtil.tryRouteForTables(schema, ctx, unit, rrs, true, cachePool);\r\n\t\t\tif (rrsTmp != null&&rrsTmp.getNodes()!=null) {\r\n\t\t\t\tfor (RouteResultsetNode node : rrsTmp.getNodes()) {\r\n\t\t\t\t\tnodeSet.add(node);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif(isAllGlobalTable) {//都是全局表时只计算一遍路由\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif(nodeSet.size() == 0) {\r\n\r\n\t\t\tCollection<String> stringCollection= ctx.getTableAliasMap().values() ;\r\n\t\t\tfor (String table : stringCollection)\r\n\t\t\t{\r\n\t\t\t\tif(table!=null&&table.toLowerCase().contains(\"information_schema.\"))\r\n\t\t\t\t{\r\n\t\t\t\t\trrs = RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), ctx.getSql());\r\n\t\t\t\t\trrs.setFinishedRoute(true);\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t//add@byron 支持通过表指定schema路由到名称相同的datanode\r\n\t\t\tif(ctx.getTableAliasMap().size()>0){\r\n\t\t\t\tfor(String key: ctx.getTableAliasMap().keySet()){\r\n\t\t\t\t\tTableConfig tb_config = schema.getTables().get(key.toUpperCase());\r\n\t\t\t\t\tif(tb_config!=null){\r\n\t\t\t\t\t\tString table_name = ctx.getTableAliasMap().get(key);\r\n\t\t\t\t\t\tfor(String dataNode:tb_config.getDataNodes()){\r\n\t\t\t\t\t\t\tif(tb_config.getDataNodes().size()==1 || table_name.startsWith(dataNode+'.')){\r\n\t\t\t\t\t\t\t\trrs = RouterUtil.routeToSingleNode(rrs, dataNode, ctx.getSql());\r\n\t\t\t\t\t\t\t\trrs.setFinishedRoute(true);\r\n\t\t\t\t\t\t\t\treturn;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t//end@byron\r\n\r\n\t\t\tString msg = \" find no Route:\" + ctx.getSql();\r\n\t\t\tLOGGER.warn(msg);\r\n\t\t\tthrow new SQLNonTransientException(msg);\r\n\t\t}\r\n\r\n\t\tRouteResultsetNode[] nodes = new RouteResultsetNode[nodeSet.size()];\r\n\t\tint i = 0;\r\n\t\tfor (Iterator<RouteResultsetNode> iterator = nodeSet.iterator(); iterator.hasNext();) {\r\n\t\t\tnodes[i] = (RouteResultsetNode) iterator.next();\r\n\t\t\ti++;\r\n\r\n\t\t}\r\n\r\n\t\trrs.setNodes(nodes);\r\n\t\trrs.setFinishedRoute(true);\r\n\t}\r\n\r\n\r\n\tprotected String getCurentDbType()\r\n\t{\r\n\t\treturn JdbcConstants.MYSQL.name();\r\n\t}\r\n\r\n\r\n\r\n\r\n\tprotected String getSql( RouteResultset rrs,SQLStatement stmt, boolean isNeedAddLimit)\r\n\t{\r\n\t\tif(getCurentDbType().equalsIgnoreCase(\"mysql\")&&(isNeedChangeLimit(rrs)||isNeedAddLimit))\r\n\t\t{\r\n\r\n\t\t\treturn stmt.toString();\r\n\r\n\t\t}\r\n\r\n\t\treturn getCtx().getSql();\r\n\t}\r\n\r\n\r\n\r\n\tprotected boolean isNeedChangeLimit(RouteResultset rrs) {\r\n\t\tif(rrs.getNodes() == null) {\r\n\t\t\treturn false;\r\n\t\t} else {\r\n\t\t\tif(rrs.getNodes().length > 1) {\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t\treturn false;\r\n\r\n\t\t}\r\n\t}\r\n\r\n\tprivate boolean isNeedCache(SchemaConfig schema, RouteResultset rrs,\r\n\t\t\t\t\t\t\t\tMySqlSelectQueryBlock mysqlSelectQuery, Map<String, Map<String, Set<ColumnRoutePair>>> allConditions) {\r\n\t\tif(ctx.getTables() == null || ctx.getTables().size() == 0 ) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tTableConfig tc = schema.getTables().get(ctx.getTables().get(0));\r\n\t\tif(tc==null ||(ctx.getTables().size() == 1 && tc.isGlobalTable())\r\n\t\t\t\t) {//|| (ctx.getTables().size() == 1) && tc.getRule() == null && tc.getDataNodes().size() == 1\r\n\t\t\treturn false;\r\n\t\t} else {\r\n\t\t\t//单表主键查询\r\n\t\t\tif(ctx.getTables().size() == 1) {\r\n\t\t\t\tString tableName = ctx.getTables().get(0);\r\n\t\t\t\tString primaryKey = schema.getTables().get(tableName).getPrimaryKey();\r\n//\t\t\t\tschema.getTables().get(ctx.getTables().get(0)).getParentKey() != null;\r\n\t\t\t\tif(ctx.getRouteCalculateUnit().getTablesAndConditions().get(tableName) != null\r\n\t\t\t\t\t\t&& ctx.getRouteCalculateUnit().getTablesAndConditions().get(tableName).get(primaryKey) != null\r\n\t\t\t\t\t\t&& tc.getDataNodes().size() > 1) {//有主键条件\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t\t//全局表不缓存\r\n\t\t\t}else if(RouterUtil.isAllGlobalTable(ctx, schema)){\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 单表且是全局表\r\n\t * 单表且rule为空且nodeNodes只有一个\r\n\t * @param schema\r\n\t * @param rrs\r\n\t * @param mysqlSelectQuery\r\n\t * @return\r\n\t */\r\n\tprivate boolean isNeedAddLimit(SchemaConfig schema, RouteResultset rrs,\r\n\t\t\t\t\t\t\t\t   MySqlSelectQueryBlock mysqlSelectQuery, Map<String, Map<String, Set<ColumnRoutePair>>> allConditions) {\r\n//\t\tctx.getTablesAndConditions().get(key))\r\n\t\tif(rrs.getLimitSize()>-1)\r\n\t\t{\r\n\t\t\treturn false;\r\n\t\t}else\r\n\t\tif(schema.getDefaultMaxLimit() == -1) {\r\n\t\t\treturn false;\r\n\t\t} else if (mysqlSelectQuery.getLimit() != null) {//语句中已有limit\r\n\t\t\treturn false;\r\n\t\t} else if(ctx.getTables().size() == 1) {\r\n\t\t\tString tableName = ctx.getTables().get(0);\r\n\t\t\tTableConfig tableConfig = schema.getTables().get(tableName);\r\n\t\t\tif(tableConfig==null)\r\n\t\t\t{\r\n\t\t\t\treturn    schema.getDefaultMaxLimit() > -1;   //   找不到则取schema的配置\r\n\t\t\t}\r\n\r\n\t\t\tboolean isNeedAddLimit= tableConfig.isNeedAddLimit();\r\n\t\t\tif(!isNeedAddLimit)\r\n\t\t\t{\r\n\t\t\t\treturn false;//优先从配置文件取\r\n\t\t\t}\r\n\r\n\t\t\tif(schema.getTables().get(tableName).isGlobalTable()) {\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\r\n\t\t\tString primaryKey = schema.getTables().get(tableName).getPrimaryKey();\r\n\r\n//\t\t\tschema.getTables().get(ctx.getTables().get(0)).getParentKey() != null;\r\n\t\t\tif(allConditions.get(tableName) == null) {//无条件\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\r\n\t\t\tif (allConditions.get(tableName).get(primaryKey) != null) {//条件中带主键\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\treturn true;\r\n\t\t} else if(rrs.hasPrimaryKeyToCache() && ctx.getTables().size() == 1){//只有一个表且条件中有主键,不需要limit了,因为主键只能查到一条记录\r\n\t\t\treturn false;\r\n\t\t} else {//多表或无表\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t}\r\n\tprivate String getAliaColumn(Map<String, String> aliaColumns,String column ){\r\n\t\tString alia=aliaColumns.get(column);\r\n\t\tif (alia==null){\r\n\t\t\tif(column.indexOf(\".\") < 0) {\r\n\t\t\t\tString col = \".\" + column;\r\n\t\t\t\tString col2 = \".`\" + column+\"`\";\r\n\t\t\t\t//展开aliaColumns，将<c.name,cname>之类的键值对展开成<c.name,cname>和<name,cname>\r\n\t\t\t\tfor(Map.Entry<String, String> entry : aliaColumns.entrySet()) {\r\n\t\t\t\t\tif(entry.getKey().endsWith(col)||entry.getKey().endsWith(col2)) {\r\n\t\t\t\t\t\tif(entry.getValue() != null && entry.getValue().indexOf(\".\") > 0) {\r\n\t\t\t\t\t\t\treturn column;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\treturn entry.getValue();\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\treturn column;\r\n\t\t}\r\n\t\telse {\r\n\t\t\treturn alia;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate String[] buildGroupByCols(List<SQLExpr> groupByItems,Map<String, String> aliaColumns) {\r\n\t\tString[] groupByCols = new String[groupByItems.size()];\r\n\t\tfor(int i= 0; i < groupByItems.size(); i++) {\r\n\t\t\tSQLExpr sqlExpr = groupByItems.get(i);\r\n\t\t\tString column = null;\r\n\t\t\tif(sqlExpr instanceof SQLIdentifierExpr )\r\n\t\t\t{\r\n\t\t\t\tcolumn=((SQLIdentifierExpr) sqlExpr).getName();\r\n\t\t\t} else if(sqlExpr instanceof SQLMethodInvokeExpr){\r\n\t\t\t\tcolumn = ((SQLMethodInvokeExpr) sqlExpr).toString();\r\n\t\t\t} else if(sqlExpr instanceof MySqlOrderingExpr){\r\n\t\t\t\t//todo czn\r\n\t\t\t\tSQLExpr expr = ((MySqlOrderingExpr) sqlExpr).getExpr();\r\n\r\n\t\t\t\tif (expr instanceof SQLName)\r\n\t\t\t\t{\r\n\t\t\t\t\tcolumn = StringUtil.removeBackquote(((SQLName) expr).getSimpleName());//不要转大写 2015-2-10 sohudo StringUtil.removeBackquote(expr.getSimpleName().toUpperCase());\r\n\t\t\t\t} else\r\n\t\t\t\t{\r\n\t\t\t\t\tcolumn = StringUtil.removeBackquote(expr.toString());\r\n\t\t\t\t}\r\n\t\t\t} else if(sqlExpr instanceof SQLPropertyExpr){\r\n\t\t\t\t/**\r\n\t\t\t\t * 针对子查询别名，例如select id from (select h.id from hotnews h  union select h.title from hotnews h ) as t1 group by t1.id;\r\n\t\t\t\t */\r\n\t\t\t\tcolumn = sqlExpr.toString();\r\n\t\t\t}\r\n\t\t\tif(column == null){\r\n\t\t\t\tcolumn = sqlExpr.toString();\r\n\t\t\t}\r\n\t\t\tint dotIndex=column.indexOf(\".\") ;\r\n\t\t\tint bracketIndex=column.indexOf(\"(\") ;\r\n\t\t\t//通过判断含有括号来决定是否为函数列\r\n\t\t\tif(dotIndex!=-1&&bracketIndex==-1)\r\n\t\t\t{\r\n\t\t\t\t//此步骤得到的column必须是不带.的，有别名的用别名，无别名的用字段名\r\n\t\t\t\tcolumn=column.substring(dotIndex+1) ;\r\n\t\t\t}\r\n\t\t\tgroupByCols[i] = getAliaColumn(aliaColumns,column);//column;\r\n\t\t}\r\n\t\treturn groupByCols;\r\n\t}\r\n\r\n\tprotected LinkedHashMap<String, Integer> buildOrderByCols(List<SQLSelectOrderByItem> orderByItems,Map<String, String> aliaColumns) {\r\n\t\tLinkedHashMap<String, Integer> map = new LinkedHashMap<String, Integer>();\r\n\t\tfor(int i= 0; i < orderByItems.size(); i++) {\r\n\t\t\tSQLOrderingSpecification type = orderByItems.get(i).getType();\r\n\t\t\t//orderColumn只记录字段名称,因为返回的结果集是不带表名的。\r\n\t\t\tSQLExpr expr =  orderByItems.get(i).getExpr();\r\n\t\t\tString col;\r\n\t\t\tif (expr instanceof SQLName) {\r\n\t\t\t\tcol = ((SQLName)expr).getSimpleName();\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tcol =expr.toString();\r\n\t\t\t}\r\n\t\t\tif(type == null) {\r\n\t\t\t\ttype = SQLOrderingSpecification.ASC;\r\n\t\t\t}\r\n\t\t\tcol=getAliaColumn(aliaColumns,col);//此步骤得到的col必须是不带.的，有别名的用别名，无别名的用字段名\r\n\t\t\tmap.put(col, type == SQLOrderingSpecification.ASC ? OrderCol.COL_ORDER_TYPE_ASC : OrderCol.COL_ORDER_TYPE_DESC);\r\n\t\t}\r\n\t\treturn map;\r\n\t}\r\n\r\n\tprivate boolean isConditionAlwaysTrue(SQLStatement statement) {\r\n\t\tSQLSelectStatement selectStmt = (SQLSelectStatement)statement;\r\n\t\tSQLSelectQuery sqlSelectQuery = selectStmt.getSelect().getQuery();\r\n\t\tif(sqlSelectQuery instanceof MySqlSelectQueryBlock) {\r\n\t\t\tMySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock)selectStmt.getSelect().getQuery();\r\n\t\t\tSQLExpr expr = mysqlSelectQuery.getWhere();\r\n\r\n\t\t\tObject o = WallVisitorUtils.getValue(expr);\r\n\t\t\tif(Boolean.TRUE.equals(o)) {\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t\treturn false;\r\n\t\t} else {//union\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tprotected void setLimitIFChange(SQLStatement stmt, RouteResultset rrs, SchemaConfig schema, SQLBinaryOpExpr one, int firstrownum, int lastrownum)\r\n\t{\r\n\t\trrs.setLimitStart(firstrownum);\r\n\t\trrs.setLimitSize(lastrownum - firstrownum);\r\n\t\tLayerCachePool tableId2DataNodeCache = (LayerCachePool) MycatServer.getInstance().getCacheService().getCachePool(\"TableID2DataNodeCache\");\r\n\t\ttry\r\n\t\t{\r\n\t\t\ttryRoute(schema, rrs, tableId2DataNodeCache);\r\n\t\t} catch (SQLNonTransientException e)\r\n\t\t{\r\n\t\t\tthrow new RuntimeException(e);\r\n\t\t}\r\n\t\tif (isNeedChangeLimit(rrs))\r\n\t\t{\r\n\t\t\tone.setRight(new SQLIntegerExpr(0));\r\n\t\t\tString curentDbType =\"db2\".equalsIgnoreCase(this.getCurentDbType())?\"oracle\":getCurentDbType();\r\n\t\t\tString sql =   SQLUtils.toSQLString(stmt, curentDbType);;\r\n\t\t\trrs.changeNodeSqlAfterAddLimit(schema,getCurentDbType(), sql,0,lastrownum, false);\r\n\t\t\t//设置改写后的sql\r\n\t\t\tgetCtx().setSql(sql);\r\n\t\t}\r\n\t}\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/DruidSelectPostgresqlParser.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport com.alibaba.druid.util.JdbcConstants;\n\npublic class DruidSelectPostgresqlParser extends DruidSelectParser\n{\n\n\n    protected String getCurentDbType()\n    {\n\t\treturn JdbcConstants.POSTGRESQL.name();\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/DruidSelectSqlServerParser.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.SQLLimit;\nimport com.alibaba.druid.sql.ast.SQLOrderBy;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLAggregateExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;\nimport com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectItem;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQuery;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLTableSource;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\nimport com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock;\nimport com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerTop;\nimport com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser;\nimport com.alibaba.druid.util.JdbcConstants;\n\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.route.RouteResultset;\n\npublic class DruidSelectSqlServerParser extends DruidSelectParser {\n\n\tpublic DruidSelectSqlServerParser(){\n\t\tsuper();\n\t\tisNeedParseOrderAgg=true;\n\t}\n\n\t@Override\n\tpublic void statementParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt) {\n\t\tSQLSelectStatement selectStmt = (SQLSelectStatement)stmt;\n\t\tSQLSelectQuery sqlSelectQuery = selectStmt.getSelect().getQuery();\n\t\t//从mysql解析过来\n\t\tif(sqlSelectQuery instanceof MySqlSelectQueryBlock) {\n\t\t\tMySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock)selectStmt.getSelect().getQuery();\n\t\t\tSQLLimit limit = mysqlSelectQuery.getLimit();\n\t\t\tif(limit==null)\n\t\t\t{\n                sqlserverParse(schema, rrs);\n\n\n            }\n\t\t\tif(isNeedParseOrderAgg)\n\t\t\t{\n\t\t\t\tparseOrderAggGroupMysql(schema, stmt,rrs, mysqlSelectQuery);\n\t\t\t\t//更改canRunInReadDB属性\n\t\t\t\tif ((mysqlSelectQuery.isForUpdate() || mysqlSelectQuery.isLockInShareMode()) && rrs.isAutocommit() == false)\n\t\t\t\t{\n\t\t\t\t\trrs.setCanRunInReadDB(false);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\n\t}\n\tprotected String getCurentDbType()\n\t{\n\t\treturn JdbcConstants.SQL_SERVER.name();\n\t}\n    private void sqlserverParse(SchemaConfig schema, RouteResultset rrs)\n    {\n        //使用sqlserver的解析，否则会有部分语法识别错误\n        SQLServerStatementParser oracleParser = new SQLServerStatementParser(getCtx().getSql());\n        SQLSelectStatement oracleStmt = (SQLSelectStatement) oracleParser.parseStatement();\n        SQLSelectQuery oracleSqlSelectQuery = oracleStmt.getSelect().getQuery();\n        if(oracleSqlSelectQuery instanceof SQLServerSelectQueryBlock)\n        {\n            parseSqlServerPageSql(oracleStmt, rrs, (SQLServerSelectQueryBlock) oracleSqlSelectQuery, schema);\n            if(isNeedParseOrderAgg)\n            {\n                parseOrderAggGroupSqlServer(schema, oracleStmt,rrs, (SQLServerSelectQueryBlock) oracleSqlSelectQuery);\n            }\n        }\n\n    }\n\n\n    private void parseOrderAggGroupSqlServer(SchemaConfig schema, SQLStatement stmt, RouteResultset rrs, SQLServerSelectQueryBlock mysqlSelectQuery)\n\t{\n\t\tMap<String, String> aliaColumns = parseAggGroupCommon(schema, stmt,rrs, mysqlSelectQuery);\n\n\t\tSQLSelect oracleSelect = (SQLSelect) mysqlSelectQuery.getParent();\n\t\tif(oracleSelect.getOrderBy() != null) {\n\t\t\tList<SQLSelectOrderByItem> orderByItems = oracleSelect.getOrderBy().getItems();\n\t\t\trrs.setOrderByCols(buildOrderByCols(orderByItems,aliaColumns));\n\t\t}\n\t}\n\n\tprivate void parseSqlServerPageSql(SQLStatement stmt, RouteResultset rrs, SQLServerSelectQueryBlock sqlserverSelectQuery, SchemaConfig schema)\n\t{\n\t\t//第一层子查询\n\t\tSQLExpr where=  sqlserverSelectQuery.getWhere();\n\t\tSQLTableSource from= sqlserverSelectQuery.getFrom();\n        if(sqlserverSelectQuery.getTop()!=null)\n        {\n            SQLServerTop top= sqlserverSelectQuery.getTop() ;\n            SQLExpr sqlExpr=  top.getExpr()  ;\n            if(sqlExpr instanceof SQLIntegerExpr)\n            {\n\n                int    topValue=((SQLIntegerExpr) sqlExpr).getNumber().intValue();\n                rrs.setLimitStart(0);\n                rrs.setLimitSize(topValue);\n            }\n        }\n        else\n\t\tif(where instanceof SQLBinaryOpExpr &&from instanceof SQLSubqueryTableSource)\n\t\t{\n\n\t\t\tSQLBinaryOpExpr one= (SQLBinaryOpExpr) where;\n\t\t\tSQLExpr left=one.getLeft();\n\t\t\tSQLBinaryOperator operator =one.getOperator();\n\t\t\tSQLSelectQuery subSelect = ((SQLSubqueryTableSource) from).getSelect().getQuery();\n\t\t\tSQLOrderBy orderBy=null;\n\t\t\tif (subSelect instanceof SQLServerSelectQueryBlock)\n\t\t\t{\n\t\t\t\tboolean hasRowNumber=false;\n                boolean hasSubTop=false;\n                int subTop=0;\n\t\t\t\tSQLServerSelectQueryBlock subSelectOracle = (SQLServerSelectQueryBlock) subSelect;\n\t\t\t\tList<SQLSelectItem> sqlSelectItems=    subSelectOracle.getSelectList();\n\t\t\t\tfor (SQLSelectItem sqlSelectItem : sqlSelectItems)\n\t\t\t\t{\n\t\t\t\t\tSQLExpr sqlExpr=  sqlSelectItem.getExpr()   ;\n\t\t\t\t\tif(sqlExpr instanceof  SQLAggregateExpr )\n\t\t\t\t\t{\n\t\t\t\t\t\tSQLAggregateExpr agg= (SQLAggregateExpr) sqlExpr;\n\t\t\t\t\t\tif(\"row_number\".equalsIgnoreCase(agg.getMethodName())&&agg.getOver()!=null)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thasRowNumber=true;\n\t\t\t\t\t\t\torderBy= agg.getOver().getOrderBy();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}\n                if(subSelectOracle.getFrom() instanceof SQLSubqueryTableSource)\n                {\n                    SQLSubqueryTableSource subFrom= (SQLSubqueryTableSource) subSelectOracle.getFrom();\n                    if (subFrom.getSelect().getQuery() instanceof SQLServerSelectQueryBlock)\n                    {\n                        SQLServerSelectQueryBlock sqlSelectQuery = (SQLServerSelectQueryBlock) subFrom.getSelect().getQuery();\n                        if(sqlSelectQuery.getTop()!=null)\n                        {\n\n                            SQLExpr sqlExpr=  sqlSelectQuery.getTop().getExpr()  ;\n                            if(sqlExpr instanceof SQLIntegerExpr)\n                            {\n                                hasSubTop=true;\n                                subTop=((SQLIntegerExpr) sqlExpr).getNumber().intValue();\n                                orderBy=  subFrom.getSelect().getOrderBy();\n                            }\n                        }\n\n                    }\n                }\n\n\t\t\t\tif(hasRowNumber)\n\t\t\t\t{\n                     if(hasSubTop&&(operator==SQLBinaryOperator.GreaterThan||operator==SQLBinaryOperator.GreaterThanOrEqual)&& one.getRight() instanceof SQLIntegerExpr)\n                     {\n                         SQLIntegerExpr right = (SQLIntegerExpr) one.getRight();\n                         int firstrownum = right.getNumber().intValue();\n                         if (operator == SQLBinaryOperator.GreaterThanOrEqual&&firstrownum!=0) {\n\t\t\t\t\t\t\t firstrownum = firstrownum - 1;\n\t\t\t\t\t\t }\n                         int lastrownum =subTop;\n                         setLimitIFChange(stmt, rrs, schema, one, firstrownum, lastrownum);\n                         if(orderBy!=null)\n                         {\n\t\t\t\t\t\t\t\tSQLSelect oracleSelect = (SQLSelect) subSelect.getParent();\n\t\t\t\t\t\t\t\toracleSelect.setOrderBy(orderBy);\n                         }\n                         parseOrderAggGroupSqlServer(schema, stmt,rrs, (SQLServerSelectQueryBlock) subSelect);\n                         isNeedParseOrderAgg=false;\n\n                     }\n                       else\n\n\t\t\t\t\tif((operator==SQLBinaryOperator.LessThan||operator==SQLBinaryOperator.LessThanOrEqual) && one.getRight() instanceof SQLIntegerExpr )\n\t\t\t\t\t{\n\t\t\t\t\t\tSQLIntegerExpr right = (SQLIntegerExpr) one.getRight();\n\t\t\t\t\t\tint firstrownum = right.getNumber().intValue();\n\t\t\t\t\t\tif (operator == SQLBinaryOperator.LessThan&&firstrownum!=0) {\n\t\t\t\t\t\t\tfirstrownum = firstrownum - 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (subSelect instanceof SQLServerSelectQueryBlock)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\trrs.setLimitStart(0);\n\t\t\t\t\t\t\trrs.setLimitSize(firstrownum);\n\t\t\t\t\t\t\tsqlserverSelectQuery = (SQLServerSelectQueryBlock) subSelect;    //为了继续解出order by 等\n\t\t\t\t\t\t\tif(orderBy!=null)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSQLSelect oracleSelect = (SQLSelect) subSelect.getParent();\n\t\t\t\t\t\t\t\toracleSelect.setOrderBy(orderBy);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tparseOrderAggGroupSqlServer(schema, stmt,rrs, sqlserverSelectQuery);\n\t\t\t\t\t\t\tisNeedParseOrderAgg=false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\tif(operator==SQLBinaryOperator.BooleanAnd && left instanceof SQLBinaryOpExpr&&one.getRight() instanceof SQLBinaryOpExpr )\n\t\t\t\t\t{\n\t\t\t\t\t\tSQLBinaryOpExpr leftE= (SQLBinaryOpExpr) left;\n\t\t\t\t\t\tSQLBinaryOpExpr rightE= (SQLBinaryOpExpr) one.getRight();\n\t\t\t\t\t\tSQLBinaryOpExpr small=null ;\n\t\t\t\t\t\tSQLBinaryOpExpr larger=null ;\n\t\t\t\t\t\tint firstrownum =0;\n\t\t\t\t\t\tint lastrownum =0;\n\t\t\t\t\t\tif(leftE.getRight() instanceof SQLIntegerExpr&&(leftE.getOperator()==SQLBinaryOperator.GreaterThan||leftE.getOperator()==SQLBinaryOperator.GreaterThanOrEqual))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsmall=leftE;\n\t\t\t\t\t\t\tfirstrownum=((SQLIntegerExpr) leftE.getRight()).getNumber().intValue();\n\t\t\t\t\t\t\tif(leftE.getOperator()==SQLBinaryOperator.GreaterThanOrEqual &&firstrownum!=0) {\n\t\t\t\t\t\t\t\tfirstrownum = firstrownum - 1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else\n\t\t\t\t\t\tif(leftE.getRight() instanceof SQLIntegerExpr&&(leftE.getOperator()==SQLBinaryOperator.LessThan||leftE.getOperator()==SQLBinaryOperator.LessThanOrEqual))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlarger=leftE;\n\t\t\t\t\t\t\tlastrownum=((SQLIntegerExpr) leftE.getRight()).getNumber().intValue();\n\t\t\t\t\t\t\tif(leftE.getOperator()==SQLBinaryOperator.LessThan&&lastrownum!=0) {\n\t\t\t\t\t\t\t\tlastrownum = lastrownum - 1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif(rightE.getRight() instanceof SQLIntegerExpr&&(rightE.getOperator()==SQLBinaryOperator.GreaterThan||rightE.getOperator()==SQLBinaryOperator.GreaterThanOrEqual))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsmall=rightE;\n\t\t\t\t\t\t\tfirstrownum=((SQLIntegerExpr) rightE.getRight()).getNumber().intValue();\n\t\t\t\t\t\t\tif(rightE.getOperator()==SQLBinaryOperator.GreaterThanOrEqual&&firstrownum!=0) {\n\t\t\t\t\t\t\t\tfirstrownum = firstrownum - 1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else\n\t\t\t\t\t\tif(rightE.getRight() instanceof SQLIntegerExpr&&(rightE.getOperator()==SQLBinaryOperator.LessThan||rightE.getOperator()==SQLBinaryOperator.LessThanOrEqual))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlarger=rightE;\n\t\t\t\t\t\t\tlastrownum=((SQLIntegerExpr) rightE.getRight()).getNumber().intValue();\n\t\t\t\t\t\t\tif(rightE.getOperator()==SQLBinaryOperator.LessThan&&lastrownum!=0) {\n\t\t\t\t\t\t\t\tlastrownum = lastrownum - 1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif(small!=null&&larger!=null)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsetLimitIFChange(stmt, rrs, schema, small, firstrownum, lastrownum);\n\t\t\t\t\t\t\tif(orderBy!=null)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSQLSelect oracleSelect = (SQLSelect) subSelect.getParent();\n\t\t\t\t\t\t\t\toracleSelect.setOrderBy(orderBy);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tparseOrderAggGroupSqlServer(schema, stmt,rrs, (SQLServerSelectQueryBlock) subSelect);\n\t\t\t\t\t\t\tisNeedParseOrderAgg=false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\n\t\t\t\t}\n\n\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\n\t\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/DruidUpdateParser.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.expr.*;\nimport com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;\nimport com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;\nimport com.alibaba.druid.stat.TableStat;\nimport com.alibaba.druid.stat.TableStat.Name;\n\nimport io.mycat.MycatServer;\nimport io.mycat.cache.CachePool;\nimport io.mycat.cache.DefaultLayedCachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.util.RouterUtil;\nimport io.mycat.util.StringUtil;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.List;\nimport java.util.Map;\n\npublic class DruidUpdateParser extends DefaultDruidParser {\n    @Override\n    public void statementParse(SchemaConfig schema, RouteResultset rrs, SQLStatement stmt) throws SQLNonTransientException {\n        //这里限制了update分片表的个数只能有一个\n        if (ctx.getTables() != null && getUpdateTableCount() > 1 && !schema.isNoSharding()) {\n            String msg = \"multi table related update not supported,tables:\" + ctx.getTables();\n            LOGGER.warn(msg);\n            throw new SQLNonTransientException(msg);\n        }\n        MySqlUpdateStatement update = (MySqlUpdateStatement) stmt;\n        String tableName = StringUtil.removeBackquote(update.getTableName().getSimpleName().toUpperCase());\n\n        TableConfig tc = schema.getTables().get(tableName);\n\n        if (RouterUtil.isNoSharding(schema, tableName)) {//整个schema都不分库或者该表不拆分\n            RouterUtil.routeForTableMeta(rrs, schema, tableName, rrs.getStatement());\n            rrs.setFinishedRoute(true);\n            return;\n        }\n\n        String partitionColumn = tc.getPartitionColumn();\n        String joinKey = tc.getJoinKey();\n        if (tc.isGlobalTable() || (partitionColumn == null && joinKey == null)) {\n            //修改全局表 update 受影响的行数\n            RouterUtil.routeToMultiNode(false, rrs, tc.getDataNodes(), rrs.getStatement(), tc.isGlobalTable());\n            rrs.setFinishedRoute(true);\n            return;\n        }\n\n\n        confirmShardColumnNotUpdated(update, schema, tableName, partitionColumn, joinKey, rrs);\n\n//\t\tif(ctx.getTablesAndConditions().size() > 0) {\n//\t\t\tMap<String, Set<ColumnRoutePair>> map = ctx.getTablesAndConditions().get(tableName);\n//\t\t\tif(map != null) {\n//\t\t\t\tfor(Map.Entry<String, Set<ColumnRoutePair>> entry : map.entrySet()) {\n//\t\t\t\t\tString column = entry.getKey();\n//\t\t\t\t\tSet<ColumnRoutePair> value = entry.getValue();\n//\t\t\t\t\tif(column.toUpperCase().equals(anObject))\n//\t\t\t\t}\n//\t\t\t}\n//\t\t\t\n//\t\t}\n//\t\tSystem.out.println();\n\n        if (schema.getTables().get(tableName).isGlobalTable() && ctx.getRouteCalculateUnit().getTablesAndConditions().size() > 1) {\n            throw new SQLNonTransientException(\"global table is not supported in multi table related update \" + tableName);\n        }\n\n        //在解析SQL时清空该表的主键缓存\n        TableConfig tableConfig = schema.getTables().get(tableName);\n        if (tableConfig != null && !tableConfig.primaryKeyIsPartionKey()) {\n            String cacheName = schema.getName() +\"_\" + tableName;\n            cacheName = cacheName.toUpperCase();\n            for (CachePool value : MycatServer.getInstance().getCacheService().getAllCachePools().values()) {\n                value.clearCache(cacheName);\n                value.getCacheStatic().reset();\n            }\n        }\n    }\n    \n    /**\n     * 获取更新的表数\n     * @author lian\n     * @date 2016年11月2日\n     * @return\n     */\n    private int getUpdateTableCount(){\n    \tMap<Name, TableStat> tableMap = this.ctx.getVisitor().getTables();\n    \tint updateTableCount = 0;\n    \tfor(Name _name : tableMap.keySet()){\n    \t\t\n    \t\tTableStat ts = tableMap.get(_name);\n    \t\tupdateTableCount += ts.getUpdateCount();\n    \t}\n    \treturn updateTableCount;\n    }\n    \n    /*\n    * 判断字段是否在SQL AST的节点中，比如 col 在 col = 'A' 中，这里要注意，一些子句中可能会在字段前加上表的别名，\n    * 比如 t.col = 'A'，这两种情况， 操作符(=)左边被druid解析器解析成不同的对象SQLIdentifierExpr(无表别名)和\n    * SQLPropertyExpr(有表别名)\n     */\n    private static boolean columnInExpr(SQLExpr sqlExpr, String colName) throws SQLNonTransientException {\n        String column;\n        if (sqlExpr instanceof SQLIdentifierExpr) {\n            column = StringUtil.removeBackquote(((SQLIdentifierExpr) sqlExpr).getName()).toUpperCase();\n        } else if (sqlExpr instanceof SQLPropertyExpr) {\n            column = StringUtil.removeBackquote(((SQLPropertyExpr) sqlExpr).getName()).toUpperCase();\n        } else {\n            throw new SQLNonTransientException(\"Unhandled SQL AST node type encountered: \" + sqlExpr.getClass());\n        }\n\n        return column.equals(colName.toUpperCase());\n    }\n\n    /*\n    * 当前节点是不是一个子查询\n    * IN (select...), ANY, EXISTS, ALL等关键字, IN (1,2,3...) 这种对应的是SQLInListExpr\n     */\n    private static boolean isSubQueryClause(SQLExpr sqlExpr) throws SQLNonTransientException {\n        return (sqlExpr instanceof SQLInSubQueryExpr || sqlExpr instanceof SQLAnyExpr || sqlExpr instanceof SQLAllExpr\n                || sqlExpr instanceof SQLQueryExpr || sqlExpr instanceof SQLExistsExpr);\n    }\n\n    /*\n    * 遍历where子句的AST，寻找是否有与update子句中更新分片字段相同的条件，\n    * o 如果发现有or或者xor，然后分片字段的条件在or或者xor中的，这种情况update也无法执行，比如\n    *   update mytab set ptn_col = val, col1 = val1 where col1 = val11 or ptn_col = val；\n    *   但是下面的这种update是可以执行的\n    *   update mytab set ptn_col = val, col1 = val1 where ptn_col = val and (col1 = val11 or col2 = val2);\n    * o 如果没有发现与update子句中更新分片字段相同的条件，则update也无法执行，比如\n    *   update mytab set ptn_col = val， col1 = val1 where col1 = val11 and col2 = val22;\n    * o 如果条件之间都是and，且有与更新分片字段相同的条件，这种情况是允许执行的。比如\n    *   update mytab set ptn_col = val, col1 = val1 where ptn_col = val and col1 = val11 and col2 = val2;\n    * o 对于一些特殊的运算符，比如between，not，或者子查询，遇到这些子句现在不会去检查分片字段是否在此类子句中，\n    *  即使分片字段在此类子句中，现在也认为对应的update语句无法执行。\n    *\n    * @param whereClauseExpr   where子句的语法树AST\n    * @param column   分片字段的名字\n    * @param value    分片字段要被更新成的值\n    * @hasOR          遍历到whereClauseExpr这个节点的时候，其上层路径中是否有OR/XOR关系运算\n    *\n    * @return         true，表示update不能执行，false表示可以执行\n    */\n    private boolean shardColCanBeUpdated(SQLExpr whereClauseExpr, String column, SQLExpr value, boolean hasOR)\n            throws SQLNonTransientException {\n        boolean canUpdate = false;\n        boolean parentHasOR = false;\n\n        if (whereClauseExpr == null)\n            return false;\n\n        if (whereClauseExpr instanceof SQLBinaryOpExpr) {\n            SQLBinaryOpExpr nodeOpExpr = (SQLBinaryOpExpr) whereClauseExpr;\n            /*\n            * 条件中有or或者xor的，如果分片字段出现在or/xor的一个子句中，则此update\n            * 语句无法执行\n             */\n            if ((nodeOpExpr.getOperator() == SQLBinaryOperator.BooleanOr) ||\n                    (nodeOpExpr.getOperator() == SQLBinaryOperator.BooleanXor)) {\n                parentHasOR = true;\n            }\n            // 发现类似 col = value 的子句\n            if (nodeOpExpr.getOperator() == SQLBinaryOperator.Equality) {\n                boolean foundCol;\n                SQLExpr leftExpr = nodeOpExpr.getLeft();\n                SQLExpr rightExpr = nodeOpExpr.getRight();\n\n                foundCol = columnInExpr(leftExpr, column);\n\n                // 发现col = value子句，col刚好是分片字段，比较value与update要更新的值是否一样，并且是否在or/xor子句中\n                if (foundCol) {\n                    if (rightExpr.getClass() != value.getClass()) {\n                        throw new SQLNonTransientException(\"SQL AST nodes type mismatch!\");\n                    }\n\n                    canUpdate = rightExpr.toString().equals(value.toString()) && (!hasOR) && (!parentHasOR);\n                }\n            } else if (nodeOpExpr.getOperator().isLogical()) {\n                if (nodeOpExpr.getLeft() != null) {\n                    if (nodeOpExpr.getLeft() instanceof SQLBinaryOpExpr) {\n                        canUpdate = shardColCanBeUpdated(nodeOpExpr.getLeft(), column, value, parentHasOR);\n                    }\n                    // else\n                    // 此子语句不是 =,>,<等关系运算符(对应的类是SQLBinaryOpExpr)。比如between X and Y\n                    // 或者 NOT，或者单独的子查询，这些情况，我们不做处理\n                }\n                if ((!canUpdate) && nodeOpExpr.getRight() != null) {\n                    if (nodeOpExpr.getRight() instanceof SQLBinaryOpExpr) {\n                        canUpdate = shardColCanBeUpdated(nodeOpExpr.getRight(), column, value, parentHasOR);\n                    }\n                    // else\n                    // 此子语句不是 =,>,<等关系运算符(对应的类是SQLBinaryOpExpr)。比如between X and Y\n                    // 或者 NOT，或者单独的子查询，这些情况，我们不做处理\n                }\n            } else if (isSubQueryClause(nodeOpExpr)){\n                // 对于子查询的检查有点复杂，这里暂时不支持\n                return false;\n            }\n            // else\n            // 其他类型的子句，忽略, 如果分片字段在这类子句中，此类情况目前不做处理，将返回false\n        }\n        // else\n        //此处说明update的where只有一个条件，并且不是 =,>,<等关系运算符(对应的类是SQLBinaryOpExpr)。比如between X and Y\n        // 或者 NOT，或者单独的子查询，这些情况，我们都不做处理\n\n        return canUpdate;\n    }\n\n    private void confirmShardColumnNotUpdated(SQLUpdateStatement update,SchemaConfig schema,String tableName,String partitionColumn,String joinKey,RouteResultset rrs) throws SQLNonTransientException {\n        List<SQLUpdateSetItem> updateSetItem = update.getItems();\n        if (updateSetItem != null && updateSetItem.size() > 0) {\n            boolean hasParent = (schema.getTables().get(tableName).getParentTC() != null);\n            for (SQLUpdateSetItem item : updateSetItem) {\n                String column = StringUtil.removeBackquote(item.getColumn().toString().toUpperCase());\n                //考虑别名，前面已经限制了update分片表的个数只能有一个，所以这里别名只能是分片表的\n                if (column.contains(StringUtil.TABLE_COLUMN_SEPARATOR)) {\n                    column = column.substring(column.indexOf(\".\") + 1).trim().toUpperCase();\n                }\n                if (partitionColumn != null && partitionColumn.equals(column)) {\n                    boolean canUpdate;\n                    canUpdate = ((update.getWhere() != null) && shardColCanBeUpdated(update.getWhere(),\n                            partitionColumn, item.getValue(), false));\n\n                    if (!canUpdate) {\n                        String msg = \"Sharding column can't be updated \" + tableName + \"->\" + partitionColumn;\n                        LOGGER.warn(msg);\n                        throw new SQLNonTransientException(msg);\n                    }\n                }\n                if (hasParent) {\n                    if (column.equals(joinKey)) {\n                        String msg = \"Parent relevant column can't be updated \" + tableName + \"->\" + joinKey;\n                        LOGGER.warn(msg);\n                        throw new SQLNonTransientException(msg);\n                    }\n                    rrs.setCacheAble(true);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/MysqlMethodInvocationHandler.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport java.sql.SQLNonTransientException;\nimport java.text.ParseException;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.time.DateFormatUtils;\nimport org.apache.commons.lang.time.DateUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLCharExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;\n\nimport io.mycat.route.parser.druid.SqlMethodInvocationHandler;\n\n/**\n * mysql函数调用\n *\n * @author zhuyiqiang\n * @version 2018/9/3\n */\npublic class MysqlMethodInvocationHandler implements SqlMethodInvocationHandler {\n    protected static final Logger LOGGER = LoggerFactory.getLogger(MysqlMethodInvocationHandler.class);\n    private final String[] SUPPORT_PATTERNS = {\n            \"yyyy-MM-dd HH:mm:ss\",\n            \"yyyy-MM-dd\",\n            \"yyyy-MM-dd HH:mm\"\n    };\n\n    @Override\n    public String invoke(SQLMethodInvokeExpr expr) throws SQLNonTransientException {\n        SQLExpr ret = doInvoke(expr);\n        if (ret != null) {\n            return ret.toString();\n        }\n        throw new SQLNonTransientException(\"unsupported mysql function expression: \" + expr.toString());\n    }\n\n    private SQLExpr doInvoke(SQLMethodInvokeExpr expr) throws SQLNonTransientException {\n        String methodName = expr.getMethodName().toUpperCase();\n        switch (methodName) {\n            case \"NOW\":\n            case \"SYSDATE\":\n            case \"CURRENT_TIMESTAMP\":\n                return invokeNow();\n            case \"ADDDATE\":\n            case \"DATE_ADD\":\n                return invokeAddDate(expr, false);\n            case \"SUBDATE\":\n            case \"DATE_SUB\":\n                return invokeAddDate(expr, true);\n        }\n        return null;\n    }\n\n    private SQLExpr invokeNow() {\n        String time = DateFormatUtils.format(new Date(), \"yyyy-MM-dd HH:mm:ss\");\n        return new SQLIdentifierExpr(time);\n    }\n\n    private SQLExpr invokeAddDate(SQLMethodInvokeExpr expr, boolean negative) throws SQLNonTransientException {\n        List<SQLExpr> parameters = expr.getParameters();\n        if (parameters.size() != 2) {\n            throwSyntaxError(expr);\n        }\n        SQLExpr p1 = parameters.get(0);\n        SQLExpr p2 = parameters.get(1);\n        if (p1 instanceof SQLMethodInvokeExpr) {\n            p1 = doInvoke((SQLMethodInvokeExpr) p1);\n        }\n        if (p1 instanceof SQLCharExpr) {\n            String time = ((SQLCharExpr) p1).getText();\n            Integer delta = null;\n            String unit = null;\n            if (p2 instanceof SQLIntegerExpr) {\n                delta = (Integer) ((SQLIntegerExpr) p2).getNumber();\n                unit = \"DAY\";\n\t\t\t} else {\n\t\t\t\tthrowSyntaxError(p2);\n\t\t\t}\n\t\t\t// else if (p2 instanceof MySqlIntervalExpr) {\n//                SQLIntegerExpr value = (SQLIntegerExpr) ((MySqlIntervalExpr) p2).getValue();\n//                delta = (Integer) value.getNumber();\n//                unit = ((MySqlIntervalExpr) p2).getUnit().name();\n//            } \n\n            try {\n                Date date = DateUtils.parseDate(time, SUPPORT_PATTERNS);\n                Date result;\n                delta = negative ? -delta : delta;\n                if (\"MONTH\".equals(unit)) {\n                    result = DateUtils.addMonths(date, delta);\n                } else if (\"DAY\".equals(unit)) {\n                    result = DateUtils.addDays(date, delta);\n                } else if (\"HOUR\".equals(unit)) {\n                    result = DateUtils.addHours(date, delta);\n                } else if (\"MINUTE\".equals(unit)) {\n                    result = DateUtils.addMinutes(date, delta);\n                } else if (\"SECOND\".equals(unit)) {\n                    result = DateUtils.addSeconds(date, delta);\n                } else {\n                    return null;\n                }\n                String ret = DateFormatUtils.format(result, \"yyyy-MM-dd HH:mm:ss\");\n                return new SQLIdentifierExpr(ret);\n            } catch (ParseException e) {\n                LOGGER.error(\"\",e);\n            }\n        }\n        return null;\n    }\n\n    private void throwSyntaxError(SQLExpr expr) throws SQLNonTransientException {\n        String errMsg = \"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\" + expr + \"' at line 1\";\n        throw new SQLNonTransientException(errMsg);\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/OracleMethodInvocationHandler.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;\nimport io.mycat.route.parser.druid.SqlMethodInvocationHandler;\n\nimport java.sql.SQLNonTransientException;\n\n/**\n * oracle函数调用\n *\n * @author zhuyiqiang\n * @version 2018/9/3\n */\npublic class OracleMethodInvocationHandler implements SqlMethodInvocationHandler {\n    @Override\n    public String invoke(SQLMethodInvokeExpr expr) throws SQLNonTransientException {\n        return null;\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/druid/impl/PgsqlMethodInvocationHandler.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;\nimport io.mycat.route.parser.druid.SqlMethodInvocationHandler;\n\nimport java.sql.SQLNonTransientException;\n\n/**\n * PostgreSQL函数调用\n *\n * @author zhuyiqiang\n * @version 2018/9/3\n */\npublic class PgsqlMethodInvocationHandler implements SqlMethodInvocationHandler {\n    @Override\n    public String invoke(SQLMethodInvokeExpr expr) throws SQLNonTransientException {\n        return null;\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/primitive/FunctionParser.java",
    "content": "package io.mycat.route.parser.primitive;\n\nimport io.mycat.route.parser.primitive.Model.Commons;\nimport io.mycat.route.parser.primitive.Model.Field;\nimport io.mycat.route.parser.primitive.Model.Function;\nimport io.mycat.route.parser.primitive.Model.Identifier;\nimport io.mycat.util.StringUtil;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Stack;\n\n/**\n * @author Hash Zhang\n * @version 1.0.0\n * @date 2016/7/26\n */\npublic class FunctionParser {\n    public static Function parseFunction(String function) throws SQLNonTransientException {\n        StringBuilder buffer = new StringBuilder();\n        Stack<Function> functions = new Stack<>();\n\n        int flag = 0;\n        for (int i = 0; i < function.length(); i++) {\n            char current = function.charAt(i);\n            switch (current) {\n                case Commons.LEFT_BRACKET:\n                    if (flag == 0) {\n                        String currentIdentifier = buffer.toString().trim();\n                        buffer = new StringBuilder();\n                        if (!StringUtil.isEmpty(currentIdentifier)) {\n                            Function function1 = new Function(currentIdentifier);\n                            if (!functions.empty() && functions.peek() != null) {\n                                functions.peek().getArguments().add(function1);\n                            }\n                            functions.push(function1);\n                        }\n                        break;\n                    }\n                    buffer.append(current);\n                    break;\n\n                case Commons.ARGUMENT_SEPARATOR:\n                    if (flag == 0 || flag == 3) {\n                        String currentIdentifier = buffer.toString().trim();\n                        buffer = new StringBuilder();\n                        if (!StringUtil.isEmpty(currentIdentifier)) {\n                            if (flag == 3) {\n                                flag = 0;\n                                Identifier identifier = new Identifier(currentIdentifier);\n                                functions.peek().getArguments().add(identifier);\n                            } else {\n                                Field field = new Field(currentIdentifier);\n                                functions.peek().getArguments().add(field);\n                            }\n                        }\n                        break;\n                    }\n                    buffer.append(current);\n                    break;\n                case Commons.RIGHT_BRACKET:\n                    if (flag != 1 && flag != 2) {\n                        String currentIdentifier = buffer.toString().trim();\n                        buffer = new StringBuilder();\n                        if (!StringUtil.isEmpty(currentIdentifier)) {\n                            if (flag == 3) {\n                                flag = 0;\n                                Identifier identifier = new Identifier(currentIdentifier);\n                                functions.peek().getArguments().add(identifier);\n                            } else {\n                                Field field = new Field(currentIdentifier);\n                                functions.peek().getArguments().add(field);\n                            }\n                        }\n                        if (flag == 0) {\n                            if (functions.size() == 1) {\n                                return functions.pop();\n                            } else {\n                                functions.pop();\n                            }\n                        }\n                        break;\n                    }\n                    buffer.append(current);\n                    break;\n                case Commons.QUOTE:\n                    if (flag == 0) {\n                        flag = 1;\n                    } else if (flag == 1) {\n                        flag = 3;\n                    }\n                case Commons.DOUBLE_QUOTE:\n                    if (flag == 0) {\n                        flag = 2;\n                    } else if (flag == 2) {\n                        flag = 3;\n                    }\n                default:\n                    buffer.append(current);\n            }\n        }\n        throw new SQLNonTransientException(\"Function is not in right format!\");\n    }\n\n    public static List<String> getFields(Function function){\n        List<String> fields = new LinkedList<>();\n        for(Identifier identifier : function.getArguments()){\n            if(identifier instanceof Field){\n                fields.add(identifier.getName());\n            } else if (identifier instanceof Function){\n                fields.addAll(getFields((Function) identifier));\n            }\n        }\n        return fields;\n    }\n    public static void main(String[] args) throws SQLNonTransientException {\n        Function function = FunctionParser.parseFunction(\"function1(arg1,a.t,\\\"ast()\\\",function2(c.t,function3(x)))\");\n        System.out.println(getFields(function));\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/primitive/Model/Commons.java",
    "content": "package io.mycat.route.parser.primitive.Model;\n\n/**\n * @author Hash Zhang\n * @version 1.0.0\n * @date 2016/7/26\n */\npublic class Commons {\n    public final static char ARGUMENT_SEPARATOR = ',';\n    public final static char DEPENDENCY_SEPARATOR = '.';\n    public final static char LEFT_BRACKET = '(';\n    public final static char RIGHT_BRACKET = ')';\n    public final static char SLASH = '\\\\';\n    public final static char QUOTE = '\\'';\n    public final static char DOUBLE_QUOTE = '\\\"';\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/primitive/Model/Field.java",
    "content": "package io.mycat.route.parser.primitive.Model;\n\n/**\n * @author Hash Zhang\n * @version 1.0.0\n * @date 2016/7/26\n */\npublic class Field extends Identifier {\n    public Field(String name) {\n        super(name);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/primitive/Model/Function.java",
    "content": "package io.mycat.route.parser.primitive.Model;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * @author Hash Zhang\n * @version 1.0.0\n * @date 2016/7/26\n */\npublic class Function extends Identifier {\n    private final List<Identifier> arguments;\n\n    public Function(String name) {\n        super(name);\n        this.arguments = new LinkedList<>();\n    }\n\n    public List<Identifier> getArguments() {\n        return arguments;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/primitive/Model/Identifier.java",
    "content": "package io.mycat.route.parser.primitive.Model;\n\n/**\n * @author Hash Zhang\n * @version 1.0.0\n * @date 2016/7/26\n */\npublic class Identifier {\n    private final String name;\n\n    public Identifier(String name) {\n        this.name = name;\n    }\n\n    public String getName() {\n        return name;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/util/ArrayUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser.util;\n\n/**\n * @author mycat\n */\npublic class ArrayUtil {\n    public static boolean equals(String str1, String str2) {\n        if (str1 == null) {\n            return str2 == null;\n        }\n        return str1.equals(str2);\n    }\n\n    public static boolean contains(String[] list, String str) {\n        if (list == null) {\n            return false;\n        }\n        for (String string : list) {\n            if (equals(str, string)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/util/CharTypes.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser.util;\n/**\n * @author mycat\n * @author mycat\n */\npublic class CharTypes {\n    private final static boolean[] hexFlags = new boolean[256];\n    static {\n        for (char c = 0; c < hexFlags.length; ++c) {\n            if (c >= 'A' && c <= 'F') {\n                hexFlags[c] = true;\n            } else if (c >= 'a' && c <= 'f') {\n                hexFlags[c] = true;\n            } else if (c >= '0' && c <= '9') {\n                hexFlags[c] = true;\n            }\n        }\n    }\n\n    public static boolean isHex(char c) {\n        return c < 256 && hexFlags[c];\n    }\n\n    public static boolean isDigit(char c) {\n        return c >= '0' && c <= '9';\n    }\n\n    private final static boolean[] identifierFlags = new boolean[256];\n    static {\n        for (char c = 0; c < identifierFlags.length; ++c) {\n            if (c >= 'A' && c <= 'Z') {\n                identifierFlags[c] = true;\n            } else if (c >= 'a' && c <= 'z') {\n                identifierFlags[c] = true;\n            } else if (c >= '0' && c <= '9') {\n                identifierFlags[c] = true;\n            }\n        }\n        //  identifierFlags['`'] = true;\n        identifierFlags['_'] = true;\n        identifierFlags['$'] = true;\n    }\n\n    public static boolean isIdentifierChar(char c) {\n        return c > identifierFlags.length || identifierFlags[c];\n    }\n\n    private final static boolean[] whitespaceFlags = new boolean[256];\n    static {\n        whitespaceFlags[' '] = true;\n        whitespaceFlags['\\n'] = true;\n        whitespaceFlags['\\r'] = true;\n        whitespaceFlags['\\t'] = true;\n        whitespaceFlags['\\f'] = true;\n        whitespaceFlags['\\b'] = true;\n    }\n\n    /**\n     * @return false if {@link MySQLLexer#EOI}\n     */\n    public static boolean isWhitespace(char c) {\n        return c <= whitespaceFlags.length && whitespaceFlags[c];\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/util/PageSQLUtil.java",
    "content": "package io.mycat.route.parser.util;\n\nimport java.util.List;\n\nimport com.alibaba.druid.sql.PagerUtils;\nimport com.alibaba.druid.sql.SQLUtils;\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.SQLOrderBy;\nimport com.alibaba.druid.sql.ast.SQLOver;\nimport com.alibaba.druid.sql.ast.expr.SQLAggregateExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLNumberExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLSelect;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectItem;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQuery;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLTableSource;\nimport com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock;\nimport com.alibaba.druid.sql.dialect.db2.parser.DB2StatementParser;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\nimport com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser;\nimport com.alibaba.druid.sql.dialect.postgresql.ast.stmt.PGSelectQueryBlock;\nimport com.alibaba.druid.sql.dialect.postgresql.parser.PGSQLStatementParser;\nimport com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock;\nimport com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser;\nimport com.alibaba.druid.util.JdbcConstants;\n\n/**\n * Created by magicdoom on 2015/3/15.\n */\npublic class PageSQLUtil\n{\n    public static String convertLimitToNativePageSql(String dbType, String sql, int offset, int count)\n    {\n\t\tif (JdbcConstants.ORACLE.name().equalsIgnoreCase(dbType))\n        {\n            OracleStatementParser oracleParser = new OracleStatementParser(sql);\n            SQLSelectStatement oracleStmt = (SQLSelectStatement) oracleParser.parseStatement();\n            return PagerUtils.limit(oracleStmt.getSelect(), JdbcConstants.ORACLE, offset, count);\n\t\t} else if (JdbcConstants.SQL_SERVER.name().equalsIgnoreCase(dbType))\n        {\n            SQLServerStatementParser oracleParser = new SQLServerStatementParser(sql);\n            SQLSelectStatement sqlserverStmt = (SQLSelectStatement) oracleParser.parseStatement();\n            SQLSelect select = sqlserverStmt.getSelect();\n            SQLOrderBy orderBy=  select.getOrderBy() ;\n            if(orderBy==null)\n            {\n                SQLSelectQuery sqlSelectQuery=      select.getQuery();\n                if(sqlSelectQuery instanceof SQLServerSelectQueryBlock)\n                {\n                    SQLServerSelectQueryBlock sqlServerSelectQueryBlock= (SQLServerSelectQueryBlock) sqlSelectQuery;\n                    SQLTableSource from=       sqlServerSelectQueryBlock.getFrom();\n                    if(\"limit\".equalsIgnoreCase(from.getAlias()))\n                    {\n                        from.setAlias(null);\n                    }\n                }\n                SQLOrderBy newOrderBy=new SQLOrderBy(new SQLIdentifierExpr(\"(select 0)\"));\n                select.setOrderBy(newOrderBy);\n\n            }\n\n            return \tPagerUtils.limit(select, JdbcConstants.SQL_SERVER, offset, count)  ;\n        }\n\t\telse if (JdbcConstants.DB2.name().equalsIgnoreCase(dbType))\n        {\n            DB2StatementParser db2Parser = new DB2StatementParser(sql);\n            SQLSelectStatement db2Stmt = (SQLSelectStatement) db2Parser.parseStatement();\n\n\t\t\treturn limitDB2(db2Stmt.getSelect(), JdbcConstants.DB2.name(), offset, count);\n\t\t} else if (JdbcConstants.POSTGRESQL.name().equalsIgnoreCase(dbType))\n        {\n            PGSQLStatementParser pgParser = new PGSQLStatementParser(sql);\n            SQLSelectStatement pgStmt = (SQLSelectStatement) pgParser.parseStatement();\n            SQLSelect select = pgStmt.getSelect();\n            SQLSelectQuery query= select.getQuery();\n            if(query instanceof PGSelectQueryBlock)\n            {\n                PGSelectQueryBlock pgSelectQueryBlock= (PGSelectQueryBlock) query;\n                pgSelectQueryBlock.setOffset(null);\n                pgSelectQueryBlock.setLimit(null);\n\n            }\n            return PagerUtils.limit(select, JdbcConstants.POSTGRESQL, offset, count);\n\n\t\t} else if (JdbcConstants.MYSQL.name().equalsIgnoreCase(dbType))\n        {\n            MySqlStatementParser pgParser = new MySqlStatementParser(sql);\n            SQLSelectStatement pgStmt = (SQLSelectStatement) pgParser.parseStatement();\n            SQLSelect select = pgStmt.getSelect();\n            SQLSelectQuery query= select.getQuery();\n            if(query instanceof MySqlSelectQueryBlock)\n            {\n                MySqlSelectQueryBlock pgSelectQueryBlock= (MySqlSelectQueryBlock) query;\n                pgSelectQueryBlock.setLimit(null);\n            }\n            return PagerUtils.limit(select, JdbcConstants.MYSQL, offset, count);\n        }\n\n        return sql;\n\n    }\n    private static String limitDB2(SQLSelect select, String dbType, int offset, int count)\n    {\n        SQLSelectQuery query = select.getQuery();\n\n        SQLBinaryOpExpr gt = new SQLBinaryOpExpr(new SQLIdentifierExpr(\"ROWNUM\"), //\n                SQLBinaryOperator.GreaterThan, //\n                new SQLNumberExpr(offset), //\n                JdbcConstants.DB2);\n        SQLBinaryOpExpr lteq = new SQLBinaryOpExpr(new SQLIdentifierExpr(\"ROWNUM\"), //\n                SQLBinaryOperator.LessThanOrEqual, //\n                new SQLNumberExpr(count + offset), //\n                JdbcConstants.DB2);\n        SQLBinaryOpExpr pageCondition = new SQLBinaryOpExpr(gt, SQLBinaryOperator.BooleanAnd, lteq, JdbcConstants.DB2);\n\n        if (query instanceof SQLSelectQueryBlock)\n        {\n            DB2SelectQueryBlock queryBlock = (DB2SelectQueryBlock) query;\n\n            List<SQLSelectItem> selectItemList = queryBlock.getSelectList();\n            for (int i = 0; i < selectItemList.size(); i++)\n            {\n                SQLSelectItem sqlSelectItem = selectItemList.get(i);\n                SQLExpr expr = sqlSelectItem.getExpr();\n                String alias = sqlSelectItem.getAlias();\n                if (expr instanceof SQLAllColumnExpr && alias == null)\n                {\n                    //未加别名会报语法错误\n                    sqlSelectItem.setExpr(new SQLPropertyExpr(new SQLIdentifierExpr(\"XXYY\"), \"*\"));\n                    queryBlock.getFrom().setAlias(\"XXYY\");\n                }\n            }\n\n//      此处生成order by的顺序不对\n//   if (offset <= 0) {\n//                queryBlock.setFirst(new SQLNumberExpr(count));\n//                return SQLUtils.toSQLString(select, dbType);\n//            }\n\n            SQLAggregateExpr aggregateExpr = new SQLAggregateExpr(\"ROW_NUMBER\");\n            SQLOrderBy orderBy = select.getOrderBy();\n            aggregateExpr.setOver(new SQLOver(orderBy));\n            select.setOrderBy(null);\n\n            queryBlock.getSelectList().add(new SQLSelectItem(aggregateExpr, \"ROWNUM\"));\n\n            DB2SelectQueryBlock countQueryBlock = new DB2SelectQueryBlock();\n            countQueryBlock.getSelectList().add(new SQLSelectItem(new SQLAllColumnExpr()));\n\n            countQueryBlock.setFrom(new SQLSubqueryTableSource(select, \"XX\"));\n\n            countQueryBlock.setWhere(pageCondition);\n\n            return SQLUtils.toSQLString(countQueryBlock, dbType);\n        }\n\n        DB2SelectQueryBlock countQueryBlock = new DB2SelectQueryBlock();\n        countQueryBlock.getSelectList().add(new SQLSelectItem(new SQLPropertyExpr(new SQLIdentifierExpr(\"XX\"), \"*\")));\n        SQLAggregateExpr aggregateExpr = new SQLAggregateExpr(\"ROW_NUMBER\");\n        SQLOrderBy orderBy = select.getOrderBy();\n        aggregateExpr.setOver(new SQLOver(orderBy));\n        select.setOrderBy(null);\n        countQueryBlock.getSelectList().add(new SQLSelectItem(aggregateExpr, \"ROWNUM\"));\n\n        countQueryBlock.setFrom(new SQLSubqueryTableSource(select, \"XX\"));\n\n        if (offset <= 0)\n        {\n            return SQLUtils.toSQLString(countQueryBlock, dbType);\n        }\n\n        DB2SelectQueryBlock offsetQueryBlock = new DB2SelectQueryBlock();\n        offsetQueryBlock.getSelectList().add(new SQLSelectItem(new SQLAllColumnExpr()));\n        offsetQueryBlock.setFrom(new SQLSubqueryTableSource(new SQLSelect(countQueryBlock), \"XXX\"));\n        offsetQueryBlock.setWhere(pageCondition);\n\n        return SQLUtils.toSQLString(offsetQueryBlock, dbType);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/util/Pair.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser.util;\n\n/**\n * (created at 2010-7-21)\n * \n * @author mycat\n */\npublic final class Pair<K, V> {\n\n    private final K key;\n    private final V value;\n\n    public Pair(K key, V value) {\n        this.key = key;\n        this.value = value;\n    }\n\n    public K getKey() {\n        return key;\n    }\n\n    public V getValue() {\n        return value;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder sb = new StringBuilder();\n        sb.append(\"(\").append(key).append(\", \").append(value).append(\")\");\n        return sb.toString();\n    }\n\n    private static final int HASH_CONST = 37;\n\n    @Override\n    public int hashCode() {\n        int hash = 17;\n        if (key == null) {\n            hash += HASH_CONST;\n        } else {\n            hash = hash << 5 + hash << 1 + hash + key.hashCode();\n        }\n        if (value == null) {\n            hash += HASH_CONST;\n        } else {\n            hash = hash << 5 + hash << 1 + hash + value.hashCode();\n        }\n        return hash;\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (!(obj instanceof Pair)) {\n            return false;\n        }\n        Pair that = (Pair) obj;\n        return isEquals(this.key, that.key) && isEquals(this.value, that.value);\n    }\n\n    private boolean isEquals(Object o1, Object o2) {\n        if (o1 == o2) {\n            return true;\n        }\n        if (o1 == null) {\n            return o2 == null;\n        }\n        return o1.equals(o2);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/util/PairUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser.util;\n\n/**\n * @author mycat\n */\npublic final class PairUtil {\n    private static final int DEFAULT_INDEX = -1;\n\n    /**\n     * \"2\" -&gt; (0,2)<br/>\n     * \"1:2\" -&gt; (1,2)<br/>\n     * \"1:\" -&gt; (1,0)<br/>\n     * \"-1:\" -&gt; (-1,0)<br/>\n     * \":-1\" -&gt; (0,-1)<br/>\n     * \":\" -&gt; (0,0)<br/>\n     */\n    public static Pair<Integer, Integer> sequenceSlicing(String slice) {\n        int ind = slice.indexOf(':');\n        if (ind < 0) {\n            int i = Integer.parseInt(slice.trim());\n            if (i >= 0) {\n                return new Pair<Integer, Integer>(0, i);\n            } else {\n                return new Pair<Integer, Integer>(i, 0);\n            }\n        }\n        String left = slice.substring(0, ind).trim();\n        String right = slice.substring(1 + ind).trim();\n        int start, end;\n        if (left.length() <= 0) {\n            start = 0;\n        } else {\n            start = Integer.parseInt(left);\n        }\n        if (right.length() <= 0) {\n            end = 0;\n        } else {\n            end = Integer.parseInt(right);\n        }\n        return new Pair<Integer, Integer>(start, end);\n    }\n\n    /**\n     * <pre>\n     * 将名字和索引用进行分割 当src = \"offer_group[4]\", l='[', r=']'时，\n     * 返回的Piar<String,Integer>(\"offer\", 4);\n     * 当src = \"offer_group\", l='[', r=']'时， \n     * 返回Pair<String, Integer>(\"offer\",-1);\n     * </pre>\n     */\n    public static Pair<String, Integer> splitIndex(String src, char l, char r) {\n        if (src == null) {\n            return null;\n        }\n        int length = src.length();\n        if (length == 0) {\n            return new Pair<String, Integer>(\"\", DEFAULT_INDEX);\n        }\n        if (src.charAt(length - 1) != r) {\n            return new Pair<String, Integer>(src, DEFAULT_INDEX);\n        }\n        int offset = src.lastIndexOf(l);\n        if (offset == -1) {\n            return new Pair<String, Integer>(src, DEFAULT_INDEX);\n        }\n        int index = DEFAULT_INDEX;\n        try {\n            index = Integer.parseInt(src.substring(offset + 1, length - 1));\n        } catch (NumberFormatException e) {\n            return new Pair<String, Integer>(src, DEFAULT_INDEX);\n        }\n        return new Pair<String, Integer>(src.substring(0, offset), index);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/util/ParseString.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.parser.util;\n\n/**\n * @author mycat\n */\npublic final class ParseString {\n\n    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];\n\n    public static byte[] hexString2Bytes(char[] hexString, int offset, int length) {\n        if (hexString == null) {\n            return null;\n        }\n        if (length == 0) {\n            return EMPTY_BYTE_ARRAY;\n        }\n        boolean odd = length << 31 == Integer.MIN_VALUE;\n        byte[] bs = new byte[odd ? (length + 1) >> 1 : length >> 1];\n        for (int i = offset, limit = offset + length; i < limit; ++i) {\n            char high, low;\n            if (i == offset && odd) {\n                high = '0';\n                low = hexString[i];\n            } else {\n                high = hexString[i];\n                low = hexString[++i];\n            }\n            int b;\n            switch (high) {\n            case '0':\n                b = 0;\n                break;\n            case '1':\n                b = 0x10;\n                break;\n            case '2':\n                b = 0x20;\n                break;\n            case '3':\n                b = 0x30;\n                break;\n            case '4':\n                b = 0x40;\n                break;\n            case '5':\n                b = 0x50;\n                break;\n            case '6':\n                b = 0x60;\n                break;\n            case '7':\n                b = 0x70;\n                break;\n            case '8':\n                b = 0x80;\n                break;\n            case '9':\n                b = 0x90;\n                break;\n            case 'a':\n            case 'A':\n                b = 0xa0;\n                break;\n            case 'b':\n            case 'B':\n                b = 0xb0;\n                break;\n            case 'c':\n            case 'C':\n                b = 0xc0;\n                break;\n            case 'd':\n            case 'D':\n                b = 0xd0;\n                break;\n            case 'e':\n            case 'E':\n                b = 0xe0;\n                break;\n            case 'f':\n            case 'F':\n                b = 0xf0;\n                break;\n            default:\n                throw new IllegalArgumentException(\"illegal hex-string: \" + new String(hexString, offset, length));\n            }\n            switch (low) {\n            case '0':\n                break;\n            case '1':\n                b += 1;\n                break;\n            case '2':\n                b += 2;\n                break;\n            case '3':\n                b += 3;\n                break;\n            case '4':\n                b += 4;\n                break;\n            case '5':\n                b += 5;\n                break;\n            case '6':\n                b += 6;\n                break;\n            case '7':\n                b += 7;\n                break;\n            case '8':\n                b += 8;\n                break;\n            case '9':\n                b += 9;\n                break;\n            case 'a':\n            case 'A':\n                b += 10;\n                break;\n            case 'b':\n            case 'B':\n                b += 11;\n                break;\n            case 'c':\n            case 'C':\n                b += 12;\n                break;\n            case 'd':\n            case 'D':\n                b += 13;\n                break;\n            case 'e':\n            case 'E':\n                b += 14;\n                break;\n            case 'f':\n            case 'F':\n                b += 15;\n                break;\n            default:\n                throw new IllegalArgumentException(\"illegal hex-string: \" + new String(hexString, offset, length));\n            }\n            bs[(i - offset) >> 1] = (byte) b;\n        }\n        return bs;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/util/ParseUtil.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.route.parser.util;\r\n\r\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;\r\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\r\nimport io.mycat.route.parser.druid.MycatStatementParser;\r\n\r\n/**\r\n * @author mycat\r\n */\r\npublic final class ParseUtil {\r\n\r\n    public static boolean isEOF(char c) {\r\n        return (c == ' ' || c == '\\t' || c == '\\n' || c == '\\r' || c == ';');\r\n    }\r\n\r\n\tpublic static boolean isEOF(String stmt, int offset) {\r\n\t\tfor (; offset < stmt.length(); offset++) {\r\n\t\t\tif (!ParseUtil.isEOF(stmt.charAt(offset))) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn true;\r\n\t}\r\n\r\n    public static String parseString(String stmt) {\r\n    \t int offset = stmt.indexOf('=');\r\n         if (offset != -1 && stmt.length() > ++offset) {\r\n             String txt = stmt.substring(offset).trim();\r\n             return txt;\r\n         }\r\n         return null;\r\n    }\r\n    \r\n    public static long getSQLId(String stmt) {\r\n        int offset = stmt.indexOf('=');\r\n        if (offset != -1 && stmt.length() > ++offset) {\r\n            String id = stmt.substring(offset).trim();\r\n            try {\r\n                return Long.parseLong(id);\r\n            } catch (NumberFormatException e) {\r\n            }\r\n        }\r\n        return 0L;\r\n    }\r\n\r\n    public static String changeInsertAddSlot(String sql,int slotValue)\r\n    {\r\n        SQLStatementParser parser = new MycatStatementParser(sql);\r\n        MySqlInsertStatement insert = (MySqlInsertStatement) parser.parseStatement();\r\n        insert.getColumns().add(new SQLIdentifierExpr(\"_slot\") );\r\n        insert.getValues().getValues().add(new SQLIntegerExpr(slotValue))  ;\r\n        return insert.toString();\r\n    }\r\n    /**\r\n     * <code>'abc'</code>\r\n     * \r\n     * @param offset stmt.charAt(offset) == first <code>'</code>\r\n     */\r\n    private static String parseString(String stmt, int offset) {\r\n        StringBuilder sb = new StringBuilder();\r\n        loop: for (++offset; offset < stmt.length(); ++offset) {\r\n            char c = stmt.charAt(offset);\r\n            if (c == '\\\\') {\r\n                switch (c = stmt.charAt(++offset)) {\r\n                case '0':\r\n                    sb.append('\\0');\r\n                    break;\r\n                case 'b':\r\n                    sb.append('\\b');\r\n                    break;\r\n                case 'n':\r\n                    sb.append('\\n');\r\n                    break;\r\n                case 'r':\r\n                    sb.append('\\r');\r\n                    break;\r\n                case 't':\r\n                    sb.append('\\t');\r\n                    break;\r\n                case 'Z':\r\n                    sb.append((char) 26);\r\n                    break;\r\n                default:\r\n                    sb.append(c);\r\n                }\r\n            } else if (c == '\\'') {\r\n                if (offset + 1 < stmt.length() && stmt.charAt(offset + 1) == '\\'') {\r\n                    ++offset;\r\n                    sb.append('\\'');\r\n                } else {\r\n                    break loop;\r\n                }\r\n            } else {\r\n                sb.append(c);\r\n            }\r\n        }\r\n        return sb.toString();\r\n    }\r\n\r\n    /**\r\n     * <code>\"abc\"</code>\r\n     * \r\n     * @param offset stmt.charAt(offset) == first <code>\"</code>\r\n     */\r\n    private static String parseString2(String stmt, int offset) {\r\n        StringBuilder sb = new StringBuilder();\r\n        loop: for (++offset; offset < stmt.length(); ++offset) {\r\n            char c = stmt.charAt(offset);\r\n            if (c == '\\\\') {\r\n                switch (c = stmt.charAt(++offset)) {\r\n                case '0':\r\n                    sb.append('\\0');\r\n                    break;\r\n                case 'b':\r\n                    sb.append('\\b');\r\n                    break;\r\n                case 'n':\r\n                    sb.append('\\n');\r\n                    break;\r\n                case 'r':\r\n                    sb.append('\\r');\r\n                    break;\r\n                case 't':\r\n                    sb.append('\\t');\r\n                    break;\r\n                case 'Z':\r\n                    sb.append((char) 26);\r\n                    break;\r\n                default:\r\n                    sb.append(c);\r\n                }\r\n            } else if (c == '\"') {\r\n                if (offset + 1 < stmt.length() && stmt.charAt(offset + 1) == '\"') {\r\n                    ++offset;\r\n                    sb.append('\"');\r\n                } else {\r\n                    break loop;\r\n                }\r\n            } else {\r\n                sb.append(c);\r\n            }\r\n        }\r\n        return sb.toString();\r\n    }\r\n\r\n    /**\r\n     * <code>AS `abc`</code>\r\n     * \r\n     * @param offset stmt.charAt(offset) == first <code>`</code>\r\n     */\r\n    private static String parseIdentifierEscape(String stmt, int offset) {\r\n        StringBuilder sb = new StringBuilder();\r\n        loop: for (++offset; offset < stmt.length(); ++offset) {\r\n            char c = stmt.charAt(offset);\r\n            if (c == '`') {\r\n                if (offset + 1 < stmt.length() && stmt.charAt(offset + 1) == '`') {\r\n                    ++offset;\r\n                    sb.append('`');\r\n                } else {\r\n                    break loop;\r\n                }\r\n            } else {\r\n                sb.append(c);\r\n            }\r\n        }\r\n        return sb.toString();\r\n    }\r\n\r\n    /**\r\n     * @param aliasIndex for <code>AS id</code>, index of 'i'\r\n     */\r\n    public static String parseAlias(String stmt, final int aliasIndex) {\r\n        if (aliasIndex < 0 || aliasIndex >= stmt.length()) {\r\n            return null;\r\n        }\r\n        switch (stmt.charAt(aliasIndex)) {\r\n        case '\\'':\r\n            return parseString(stmt, aliasIndex);\r\n        case '\"':\r\n            return parseString2(stmt, aliasIndex);\r\n        case '`':\r\n            return parseIdentifierEscape(stmt, aliasIndex);\r\n        default:\r\n            int offset = aliasIndex;\r\n            for (; offset < stmt.length() && CharTypes.isIdentifierChar(stmt.charAt(offset)); ++offset) {\r\n                ;\r\n            }\r\n            return stmt.substring(aliasIndex, offset);\r\n        }\r\n    }\r\n\r\n    /**\r\n     * 解析注释，返回stmt中注释结尾的index\r\n     * @param stmt\r\n     * @param offset\r\n     * @return\r\n     */\r\n    public static int comment(String stmt, int offset) {\r\n        int len = stmt.length();\r\n        int n = offset;\r\n        switch (stmt.charAt(n)) {\r\n        case '/':\r\n            if (len > ++n && stmt.charAt(n++) == '*' && len > n + 1) {\r\n                for (int i = n; i < len; ++i) {\r\n                    if (stmt.charAt(i) == '*') {\r\n                        int m = i + 1;\r\n                        if (len > m && stmt.charAt(m) == '/') {\r\n                            return m;\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            break;\r\n        case '#':\r\n            for (int i = n + 1; i < len; ++i) {\r\n                if (stmt.charAt(i) == '\\n') {\r\n                    return i;\r\n                }\r\n            }\r\n            break;\r\n        }\r\n        return offset;\r\n    }\r\n\r\n    public static boolean currentCharIsSep(String stmt, int offset) {\r\n        if (stmt.length() > offset) {\r\n            switch (stmt.charAt(offset)) {\r\n            case ' ':\r\n            case '\\t':\r\n            case '\\r':\r\n            case '\\n':\r\n                return true;\r\n            default:\r\n                return false;\r\n            }\r\n        }\r\n        return true;\r\n    }\r\n\r\n    /*****\r\n     * 检查下一个字符是否为分隔符，并把偏移量加1\r\n     */\r\n    public static boolean nextCharIsSep(String stmt, int offset) {\r\n        return currentCharIsSep(stmt, ++offset);\r\n    }\r\n\r\n    /*****\r\n     * 检查下一个字符串是否为期望的字符串，并把偏移量移到从offset开始计算，expectValue之后的位置\r\n     * \r\n     * @param stmt 被解析的sql\r\n     * @param offset 被解析的sql的当前位置\r\n     * @param nextExpectedString 在stmt中准备查找的字符串\r\n     * @param checkSepChar 当找到expectValue值时，是否检查其后面字符为分隔符号\r\n     * @return 如果包含指定的字符串，则移动相应的偏移量，否则返回值=offset\r\n     */\r\n    public static int nextStringIsExpectedWithIgnoreSepChar(String stmt,\r\n                                                            int offset,\r\n                                                            String nextExpectedString,\r\n                                                            boolean checkSepChar) {\r\n        if (nextExpectedString == null || nextExpectedString.length() < 1) {\r\n            return offset;\r\n        }\r\n        int i = offset;\r\n        int index = 0;\r\n        char expectedChar;\r\n        char actualChar;\r\n        boolean isSep;\r\n        for (; i < stmt.length() && index < nextExpectedString.length(); ++i) {\r\n            if (index == 0) {\r\n                isSep = currentCharIsSep(stmt, i);\r\n                if (isSep) {\r\n                    continue;\r\n                }\r\n            }\r\n            actualChar = stmt.charAt(i);\r\n            expectedChar = nextExpectedString.charAt(index++);\r\n            if (actualChar != expectedChar) {\r\n                return offset;\r\n            }\r\n        }\r\n        if (index == nextExpectedString.length()) {\r\n            boolean ok = true;\r\n            if (checkSepChar) {\r\n                ok = nextCharIsSep(stmt, i);\r\n            }\r\n            if (ok) {\r\n                return i;\r\n            }\r\n        }\r\n        return offset;\r\n    }\r\n\r\n    private static final String JSON = \"json\";\r\n    private static final String EQ = \"=\";\r\n\r\n    //private static final String WHERE = \"where\";\r\n    //private static final String SET = \"set\";\r\n\r\n    /**********\r\n     * 检查下一个字符串是否json= *\r\n     * \r\n     * @param stmt 被解析的sql\r\n     * @param offset 被解析的sql的当前位置\r\n     * @return 如果包含指定的字符串，则移动相应的偏移量，否则返回值=offset\r\n     */\r\n    public static int nextStringIsJsonEq(String stmt, int offset) {\r\n        int i = offset;\r\n\r\n        // / drds 之后的符号\r\n        if (!currentCharIsSep(stmt, ++i)) {\r\n            return offset;\r\n        }\r\n\r\n        // json 串\r\n        int k = nextStringIsExpectedWithIgnoreSepChar(stmt, i, JSON, false);\r\n        if (k <= i) {\r\n            return offset;\r\n        }\r\n        i = k;\r\n\r\n        // 等于符号\r\n        k = nextStringIsExpectedWithIgnoreSepChar(stmt, i, EQ, false);\r\n        if (k <= i) {\r\n            return offset;\r\n        }\r\n        return i;\r\n    }\r\n\r\n    public static int move(String stmt, int offset, int length) {\r\n        int i = offset;\r\n        for (; i < stmt.length(); ++i) {\r\n            switch (stmt.charAt(i)) {\r\n            case ' ':\r\n            case '\\t':\r\n            case '\\r':\r\n            case '\\n':\r\n                continue;\r\n            case '/':\r\n            case '#':\r\n                i = comment(stmt, i);\r\n                continue;\r\n            default:\r\n                return i + length;\r\n            }\r\n        }\r\n        return i;\r\n    }\r\n\r\n    public static boolean compare(String s, int offset, char[] keyword) {\r\n        if (s.length() >= offset + keyword.length) {\r\n            for (int i = 0; i < keyword.length; ++i, ++offset) {\r\n                if (Character.toUpperCase(s.charAt(offset)) != keyword[i]) {\r\n                    return false;\r\n                }\r\n            }\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n\tpublic static boolean isSpace(char space) {\r\n\t\treturn space == ' ' || space == '\\r' || space == '\\n' || space == '\\t';\r\n\t}\r\n\r\n\tpublic static int findNextBreak(String sql) {\r\n\t\treturn findNextBreak(sql, false);\r\n\t}\r\n\r\n\t/**\r\n\t * @param sql\r\n\t * @param inProcedure\r\n\t * @return\r\n\t */\r\n\tprivate static int findNextBreak(String sql, boolean inProcedure) {\r\n\t\t// is the char in apostrophe\r\n\t\tboolean inApostrophe = false;\r\n\t\t// is the char after procedure begin\r\n\t\tboolean procedureBegin = false;\r\n\t\tString bodyLeft = null;\r\n\t\tfor (int i = 0; i < sql.length(); i++) {\r\n\t\t\tchar c = sql.charAt(i);\r\n\t\t\tswitch (c) {\r\n\t\t\tcase '\\\\':\r\n\t\t\t\ti++;\r\n\t\t\t\tbreak;\r\n\t\t\tcase '\\'':\r\n\t\t\tcase '\\\"':\r\n\t\t\t\tif (!inApostrophe) {\r\n\t\t\t\t\tinApostrophe = true;\r\n\t\t\t\t\tbodyLeft = String.valueOf(c);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tString rightTest = String.valueOf(c);\r\n\t\t\t\t\tif (rightTest.equals(bodyLeft)) {\r\n\t\t\t\t\t\tinApostrophe = false;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tcase ';':\r\n\t\t\t\tif (!inApostrophe && !procedureBegin) {\r\n\t\t\t\t\treturn i;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'p':\r\n\t\t\tcase 'P':\r\n\t\t\t\tif (!inApostrophe) {\r\n\t\t\t\t\ti = getProcedureEndPos(sql, i);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'B':\r\n\t\t\t\tif (inProcedure && !inApostrophe && isBegin(sql, i)) {\r\n\t\t\t\t\tprocedureBegin = true;\r\n\t\t\t\t\ti = i + 4;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'E':\r\n\t\t\t\tif (!inApostrophe && procedureBegin && isEnd(sql, i)) {\r\n\t\t\t\t\tprocedureBegin = false;\r\n\t\t\t\t\ti = i + 2;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn sql.length() - 1;\r\n\t}\r\n\r\n\tprivate static boolean isBegin(String stmt, int offset) {\r\n\t\tif (offset <= 0 || !ParseUtil.isSpace(stmt.charAt(offset - 1)) || stmt.length() <= offset + \"EGIN \".length()) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tchar c1 = stmt.charAt(++offset);\r\n\t\tchar c2 = stmt.charAt(++offset);\r\n\t\tchar c3 = stmt.charAt(++offset);\r\n\t\tchar c4 = stmt.charAt(++offset);\r\n\t\tchar c5 = stmt.charAt(++offset);\r\n\t\treturn c1 == 'E' && c2 == 'G' && c3 == 'I' && c4 == 'N' && ParseUtil.isSpace(c5);\r\n\t}\r\n\r\n\tprivate static boolean isEnd(String stmt, int offset) {\r\n\t\tif (offset <= 0 || !ParseUtil.isSpace(stmt.charAt(offset - 1)) || stmt.length() <= offset + \"ND\".length()) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tchar c1 = stmt.charAt(++offset);\r\n\t\tchar c2 = stmt.charAt(++offset);\r\n\t\tif (c1 == 'N' && c2 == 'D') {\r\n\t\t\toffset++;\r\n\t\t\tfor (; offset < stmt.length(); offset++) {\r\n\t\t\t\tchar tmp = stmt.charAt(offset);\r\n\t\t\t\tif (ParseUtil.isSpace(tmp)) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\treturn tmp == ';';\r\n\t\t\t}\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\tprivate static int getProcedureEndPos(String stmt, int offset) {\r\n\t\tif (stmt.length() > offset + \"ROCEDURE \".length()) {\r\n\t\t\tchar c1 = stmt.charAt(++offset);\r\n\t\t\tchar c2 = stmt.charAt(++offset);\r\n\t\t\tchar c3 = stmt.charAt(++offset);\r\n\t\t\tchar c4 = stmt.charAt(++offset);\r\n\t\t\tchar c5 = stmt.charAt(++offset);\r\n\t\t\tchar c6 = stmt.charAt(++offset);\r\n\t\t\tchar c7 = stmt.charAt(++offset);\r\n\t\t\tchar c8 = stmt.charAt(++offset);\r\n\t\t\tif ((c1 == 'r' || c1 == 'R') && (c2 == 'o' || c2 == 'O') && (c3 == 'c' || c3 == 'C')\r\n\t\t\t\t\t&& (c4 == 'e' || c4 == 'E') && (c5 == 'd' || c5 == 'D') && (c6 == 'u' || c6 == 'U')\r\n\t\t\t\t\t&& (c7 == 'r' || c7 == 'R') && (c8 == 'e' || c8 == 'E')) {\r\n\t\t\t\tString testSql = stmt.substring(++offset).toUpperCase();\r\n\t\t\t\t// offset + length -1 for checking ';' outside\r\n\t\t\t\treturn offset - 1 + findNextBreak(testSql, true);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn offset;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/util/SQLParserUtils.java",
    "content": "package io.mycat.route.parser.util;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.HashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.Scanner;\r\nimport java.util.Stack;\r\nimport java.util.regex.Matcher;\r\nimport java.util.regex.Pattern;\r\n\r\nimport org.junit.Assert;\r\n\r\npublic class SQLParserUtils {\r\n\t\r\n\tclass State{\r\n\t\tInteger state = -1;\r\n\t}\r\n\r\n\tState state = new State();\t\t\t//当前处于哪个部分 0: select 1: from 2:where \r\n\r\n    Stack<State> stateStack = new Stack<State>();\r\n\r\n\tMap<String,String> tables = new HashMap<String,String>();\r\n\tboolean tableFlag = false;\t\t//在From部分出现关键字from join 逗号后的是表名\r\n    \r\n\tpublic Map<String,String> parse(String sql){\r\n\t\ttables.clear();\r\n\t\tstateStack.clear();\r\n\t\tstate = null;\r\n\t\t\r\n\t    tableFlag = false;\r\n\t\t\r\n\t\tboolean sFlag = false;\t\t\t//单引号 \r\n\t\tboolean dFlag = false;\t\t\t//双引号计数器\r\n\t\tScanner reader=new Scanner(sql);\r\n\t\treader.useDelimiter(\" \");\r\n\t\tString value;\r\n\t\twhile(reader.hasNext()){\r\n\t\t\tvalue = reader.next().toLowerCase();\r\n\t\t\t//前面已经出现单引号，在再次出现单引号之前不做任何处理\r\n\t\t\tif (sFlag){\r\n\t\t\t\tif (value.endsWith(\"'\")&& getCount(value,\"'\")==1){\r\n\t\t\t\t\tsFlag = false;\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}else if (value.indexOf(\"'\")!=-1){\r\n\t\t\t\t\tvalue = value.substring(value.indexOf(\"'\")+1);\r\n\t\t\t\t\tsFlag = false;\r\n\t\t\t\t}else {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t//前面已经出现双引号，在再次出现双引号之前不做任何处理\r\n\t\t\tif (dFlag){\r\n\t\t\t\tif (value.endsWith(\"\\\"\")&& getCount(value,\"\\\"\")==1){\r\n\t\t\t\t\tdFlag = false;\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}else if (value.indexOf(\"\\\"\")!=-1){\r\n\t\t\t\t\tvalue = value.substring(value.indexOf(\"\\\"\")+1);\r\n\t\t\t\t\tdFlag = false;\r\n\t\t\t\t}else {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t//单引号在select，where部分不做处理\r\n\t\t\tif (state != null && state.state !=1 && getCount(value,\"'\")%2==1){\r\n\t\t\t\tsFlag = true;\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif (state != null && state.state !=1 && getCount(value,\"\\\"\")%2==1){\r\n\t\t\t\tdFlag = true;\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\t//SELECT关键字\r\n\t\t\tif (value.equals(\"select\") || value.equals(\"(select\")){\r\n\t\t\t\t//if (state != null) \r\n\t\t\t\tstate = new State();\r\n\t\t\t\tstate.state = 0;\r\n\t\t\t\tstateStack.push(state);\t//入栈\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\t\r\n\t\t\t//FROM关键字\r\n\t\t\tif (value.equals(\"from\") || value.equals(\"into\")|| value.equals(\"join\")){\r\n\t\t\t\tstate.state = 1;\r\n\t\t\t\ttableFlag = true;\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\t//From部分出现逗号后面是表名\r\n\t\t\tif (state.state == 1 && value.equals(\",\")){\r\n\t\t\t\ttableFlag = true;\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif (state.state == 1 && tableFlag == true){\r\n\t\t\t\tgetTableName(value);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tif (state.state == 1 && tableFlag == false){\r\n\t\t\t\tif (!value.startsWith(\"),\") &&(value.equals(\",\")|| value.endsWith(\",\"))){\r\n\t\t\t\t\ttableFlag = true;\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}else if (!value.startsWith(\"),\") && value.indexOf(\",\")!=-1){\r\n\t\t\t\t\tgetTableName(value);\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\t\t\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\t//WHERE关键字\r\n\t\t\tif (value.equals(\"where\")){\r\n\t\t\t\tstate.state = 2;\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif (value.endsWith(\"(select\")){\r\n\r\n\t\t\t\tstateStack.push(state);\r\n\t\t\t\tstate = new State();\r\n\t\t\t\tstate.state = 0;\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif ( value.equals(\")\")|| value.startsWith(\"),\")){\r\n\t\t\t\tstateStack.pop();\r\n\t\t\t\tstate = stateStack.peek();\r\n\t\t\t\ttableFlag = value.endsWith(\",\")?true:false;\r\n\t\t\t\tif (state.state ==1){\r\n\t\t\t\t\tgetTableName(value);\r\n\t\t\t\t}\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\r\n\t\t}\r\n\t\t\t\r\n\t\treturn tables;\r\n\t}\r\n\t\r\n\tprivate void getTableName(String str){\r\n\t\tString[] t = str.split(\",\");\r\n\t\tfor (int i=tableFlag?0:1; i<t.length;i++){\r\n\t\t\tif (t[i].endsWith(\")\")){\r\n\t\t\t\ttables.put(t[i].substring(0,t[i].length()-1), \"\");\r\n\t\t\t\tstateStack.pop();\r\n\t\t\t\tstate = stateStack.peek();\r\n\t\t\t\tif (state.state != 1){\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}else if (t[i].equals(\"(select\")){\r\n\t\t\t\t\r\n\t\t\t\tstate = new State();\r\n\t\t\t\tstate.state = 0;\r\n\t\t\t\tstateStack.push(state);\r\n\t\t\t\tbreak;\r\n\t\t\t}else{\r\n\t\t\t\tif (t[i].trim().length()>0 && !t[i].trim().equals(\"(\")) {\r\n\t\t\t\t\ttables.put(t[i], \"\");\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (!str.endsWith(\",\")) {\r\n\t\t\ttableFlag = false;\r\n\t\t}\r\n\t}\r\n\t\r\n\t\r\n\t\r\n\tpublic static int getCount(String str,String match){\r\n\t       int count = 0;\r\n\t        int index = 0;\r\n\t        while((index=str.indexOf(match,index))!=-1){\r\n\t            index = index+match.length();\r\n\t            count++;\r\n\t        }\r\n\t        return count;\r\n\t}\r\n\t\r\n\t\r\n\tprivate boolean test(String sql,String[] tables){\r\n\t\t\r\n\r\n\t\tMap<String,String> result = parse(sql);\r\n\t\tif (result.size() != tables.length) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tfor (String tmp : tables){\r\n\t\t\tif (result.get(tmp.toLowerCase())==null) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn true;\r\n\t\t\t\r\n\t}\r\n\tprivate static final String sql1 = \"select t3.*,ztd3.TypeDetailName as UseStateName\\n\" +\r\n            \"from\\n\" +\r\n            \"( \\n\" +\r\n            \" select t4.*,ztd4.TypeDetailName as AssistantUnitName\\n\" +\r\n            \" from\\n\" +\r\n            \" (\\n\" +\r\n            \"  select t2.*,ztd2.TypeDetailName as UnitName \\n\" +\r\n            \"  from\\n\" +\r\n            \"  (\\n\" +\r\n            \"   select t1.*,ztd1.TypeDetailName as MaterielAttributeName \\n\" +\r\n            \"   from \\n\" +\r\n            \"   (\\n\" +\r\n            \"    select m.*,r.RoutingName,u.username,mc.MoldClassName\\n\" +\r\n            \"    from dbo.D_Materiel as m\\n\" +\r\n            \"    left join dbo.D_Routing as r\\n\" +\r\n            \"    on m.RoutingID=r.RoutingID\\n\" +\r\n            \"    left join dbo.D_MoldClass as mc\\n\" +\r\n            \"    on m.MoldClassID=mc.MoldClassID\\n\" +\r\n            \"    left join dbo.D_User as u\\n\" +\r\n            \"    on u.UserId=m.AddUserID\\n\" +\r\n            \"   )as t1\\n\" +\r\n            \"   left join dbo.D_Type_Detail as ztd1 \\n\" +\r\n            \"   on t1.MaterielAttributeID=ztd1.TypeDetailID\\n\" +\r\n            \"  )as t2\\n\" +\r\n            \"  left join dbo.D_Type_Detail as ztd2 \\n\" +\r\n            \"  on t2.UnitID=ztd2.TypeDetailID\\n\" +\r\n            \" ) as t4\\n\" +\r\n            \" left join dbo.D_Type_Detail as ztd4 \\n\" +\r\n            \" on t4.AssistantUnitID=ztd4.TypeDetailID\\n\" +\r\n            \")as t3\\n\" +\r\n            \"left join dbo.D_Type_Detail as ztd3 \\n\" +\r\n            \"on t3.UseState=ztd3.TypeDetailID\";\r\n\tpublic static void main(String[] args) {\r\n\t\tSQLParserUtils parser = new SQLParserUtils();\r\n\t\t//parser.parse(\"select 'select * from C , D',' select * from E' from B\");\r\n\t\t//if (true) return;\r\n\t\tList<String[]> list = new ArrayList<String[]>();\r\n\t\tlist.add(new String[]{\"select * from B\",\"B\"});\r\n\t\tlist.add(new String[]{\"select * from B,C\",\"B,C\"});\r\n\t\tlist.add(new String[]{\"select * from B ,C\",\"B,C\"});\r\n\t\tlist.add(new String[]{\"select * from B , C\",\"B,C\"});\r\n\t\tlist.add(new String[]{\"select * from B a\",\"B\"});\r\n\t\tlist.add(new String[]{\"select * from B a,C,D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select * from B a,C e ,D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select * from B a,C e ,D f\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select * from B,(select * from C),D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select * from B, (select * from C),D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select * from B, ( select * from C),D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select * from B, ( select * from C),D,E\",\"B,C,D,E\"});\r\n\t\tlist.add(new String[]{\"select * from B,(select * from C ),D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select * from B,(select * from C ) ,D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select * from B,(select * from C), D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select * from B,(select * from C ) , D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select * from B,(select * from C ) , (select * from D )\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select * from B,(select * from C ) , (select * from D ),E\",\"B,C,D,E\"});\r\n\t\tlist.add(new String[]{\"select * from B,(select C.ID , D.ID from C ) , (select * from D ),E\",\"B,C,D,E\"});\r\n\r\n\t\tlist.add(new String[]{\"select (select C.ID,D.ID from C ) from B, D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select (select C.ID,D.ID from C ) , E from B, D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select (select C.ID,D.ID from C ), E from B, D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select (select C.ID,D.ID from C ),E from B, D\",\"B,C,D\"});\r\n\t\tlist.add(new String[]{\"select a from t1 union select b from t2\",\"t1,t2\"});\r\n\t\t\r\n\r\n\t\tlist.add(new String[]{\"select * from B where C =1\",\"B\"});\r\n\t\tlist.add(new String[]{\"select * from B where C = (select 1 from D)\",\"B,D\"});\r\n\t\tlist.add(new String[]{\"select * from B where C = (select 1 from D) AND E = (select 2 from F)\",\"B,D,F\"});\r\n\t\t\r\n\t\t\r\n\t\tlist.add(new String[]{\"select * from B INNER JOIN C ON C.ID = D.ID\",\"B,C\"});\r\n\t\tlist.add(new String[]{\"select * from B INNER JOIN C ON C.ID = D.ID INNER JOIN E ON 1=1\",\"B,C,E\"});\r\n\t\tlist.add(new String[]{\"select * from B INNER JOIN C ON C.ID = (select G,H FROM I) INNER JOIN E ON 1=1\",\"B,C,E,I\"});\r\n\t\tlist.add(new String[]{\"select * from B INNER JOIN C ON C.ID = (select G,H FROM I ) INNER JOIN E ON 1=1\",\"B,C,E,I\"});\r\n\t\tlist.add(new String[]{\"select * from B INNER JOIN C ON C.ID = ( select G,H FROM I ) INNER JOIN E ON 1=1\",\"B,C,E,I\"});\r\n\t\t\r\n\t\t\r\n\r\n\t\tlist.add(new String[]{\"select 'select * from C' from B\",\"B\"});\r\n\t\tlist.add(new String[]{\"select 'select * from C,D' from B\",\"B\"});\r\n\t\tlist.add(new String[]{\"select 'select * from C , D',E from B\",\"B\"});\r\n\t\tlist.add(new String[]{\"select 'select * from C , D',' select * from E' from B\",\"B\"});\r\n\t\tlist.add(new String[]{\"select 'select * from C , D','F',' select * from E' from B\",\"B\"});\r\n\t\tlist.add(new String[]{\"select 'select * from C , D',' F',' select * from E' from B\",\"B\"});\r\n\t\tlist.add(new String[]{\"select 'select * from C , D',' F ',' select * from E' from B\",\"B\"});\r\n\t\t\r\n\r\n\t\tlist.add(new String[]{\"select * from 'B'\",\"'B'\"});\r\n\t\tlist.add(new String[]{\"select * from 'B','C'\",\"'B','C'\"});\r\n\t\t\r\n\t\tlist.add(new String[]{sql1,\"dbo.D_Materiel,dbo.D_Routing,dbo.D_MoldClass,dbo.D_Type_Detail,dbo.D_User\"});\r\n\t\t//String sql  = \"select ' form \\\"' * from \\\"B\\\",C where a='c'\";\r\n\t\t//String sql  = \"select ' form \\\"' * from \\\"B\\\",C\";\r\n\t\tfor (String[] tmp :list){\r\n\t\t\t\r\n\t\t\tAssert.assertTrue(tmp[0],parser.test(tmp[0],tmp[1].split(\",\")));\r\n\t\t\t{\r\n\t\t\t\tSystem.out.println(tmp[0]+\"--->\"+tmp[1]);\r\n\t\t\t\tMap<String,String> tables = parser.parse(tmp[0]);\r\n\t\t\t\tSystem.out.print(\"表名：\");\r\n\t\t\t\tfor (String key :tables.keySet()) {\r\n\t\t\t\t\tSystem.out.println(key);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\t// TODO Auto-generated method stub\r\n\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/route/parser/util/WildcardUtil.java",
    "content": "package io.mycat.route.parser.util;\n\npublic class WildcardUtil {\n  \n  public static String wildcard(String name) {\n    if (name.startsWith(\"`\")) {\n      name = name.replaceAll(\"`\", \"\");\n    } else if (name.startsWith(\"\\\"\")) {\n      name = name.replaceAll(\"\\\"\", \"\");\n    } else if (name.startsWith(\"'\")) {\n      name = name.replaceAll(\"'\", \"\");\n    }\n    return name;\n  }\n\n  public static void wildcards(String[] names) {\n    for (int i = 0; i < names.length; i++) {\n      names[i] = wildcard(names[i]);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/BatchInsertSequence.java",
    "content": "package io.mycat.route.sequence;\n\nimport io.mycat.route.sequence.handler.*;\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\n\nimport io.mycat.MycatServer;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.catlets.Catlet;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.sqlengine.EngineCtx;\nimport io.mycat.util.StringUtil;\n\n/**\n * 执行批量插入sequence Id\n * @author 兵临城下\n * @date 2015/03/20\n */\npublic class BatchInsertSequence implements Catlet {\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(BatchInsertSequence.class);\n\t\n\tprivate RouteResultset rrs;//路由结果集\n\tprivate String executeSql;//接收执行处理任务的sql\n\tprivate SequenceHandler sequenceHandler;//sequence处理对象\n\t\n\t//重新路由使用\n\tprivate SystemConfig sysConfig;\n\tprivate SchemaConfig schema;\n\tprivate int sqltype; \n\tprivate String charset; \n\tprivate ServerConnection sc;\n\tprivate LayerCachePool cachePool;\n\n\t@Override\n\tpublic void processSQL(String sql, EngineCtx ctx) {\n\t\tthrow new UnsupportedOperationException(\"batch insert is not supported because the mycat 1.6 does not support multi statements.\");\n//\t\ttry {\n//\n//\t\t\tgetRoute(executeSql);\n//\t\t\tRouteResultsetNode[] nodes = rrs.getNodes();\n//\t\t\tif (nodes == null || nodes.length == 0 || nodes[0].getName() == null\n//\t\t\t\t\t|| nodes[0].getName().equals(\"\")) {\n//\t\t\t\tctx.getSession().getSource().writeErrMessage(ErrorCode.ER_NO_DB_ERROR,\n//\t\t\t\t\t\t\"No dataNode found ,please check tables defined in schema:\"\n//\t\t\t\t\t\t\t\t+ ctx.getSession().getSource().getSchema());\n//\t\t\t\treturn;\n//\t\t\t}\n//\n//\t\t\tsc.getSession2().execute(rrs, sqltype);//将路由好的数据执行入库\n//\n//\t\t} catch (Exception e) {\n//\t\t\tLOGGER.error(\"BatchInsertSequence.processSQL(String sql, EngineCtx ctx)\",e);\n//\t\t}\n\t}\n\n\t@Override\n\tpublic void route(SystemConfig sysConfig, SchemaConfig schema, int sqlType,\n\t\t\tString realSQL, String charset, ServerConnection sc,\n\t\t\tLayerCachePool cachePool) {\n\t\tint rs = ServerParse.parse(realSQL);\n\t\tthis.sqltype = rs & 0xff;\n\t\tthis.sysConfig=sysConfig; \n\t\tthis.schema=schema;\n\t\tthis.charset=charset; \n\t\tthis.sc=sc;\t\n\t\tthis.cachePool=cachePool;\t\n\t\t\n\t\ttry {\n\t\t\tMySqlStatementParser parser = new MySqlStatementParser(realSQL);\t \n\t\t\tSQLStatement statement = parser.parseStatement();\n\t\t\tMySqlInsertStatement insert = (MySqlInsertStatement)statement;\n\t\t\tif(insert.getValuesList()!=null){\n\t\t\t\tString tableName = StringUtil.getTableName(realSQL).toUpperCase();\n\t\t\t\tTableConfig tableConfig = schema.getTables().get(tableName);\n\t\t\t\tString primaryKey = tableConfig.getPrimaryKey();//获得表的主键字段\n\t\t\t\t\n\t\t\t\tSQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr();\n\t\t\t\tsqlIdentifierExpr.setName(primaryKey);\n\t\t\t\tinsert.getColumns().add(sqlIdentifierExpr);\n\t\t\t\t\n\t\t\t\tif(sequenceHandler == null){\n\t\t\t\t\tint seqHandlerType = MycatServer.getInstance().getConfig().getSystem().getSequenceHandlerType();\n\t\t\t\t\tswitch(seqHandlerType){\n\t\t\t\t\t\tcase SystemConfig.SEQUENCEHANDLER_MYSQLDB:\n\t\t\t\t\t\t\tsequenceHandler = IncrSequenceMySQLHandler.getInstance();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase SystemConfig.SEQUENCEHANDLER_LOCALFILE:\n\t\t\t\t\t\t\tsequenceHandler = IncrSequencePropHandler.getInstance();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase SystemConfig.SEQUENCEHANDLER_LOCAL_TIME:\n\t\t\t\t\t\t\tsequenceHandler = IncrSequenceTimeHandler.getInstance();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase SystemConfig.SEQUENCEHANDLER_ZK_DISTRIBUTED:\n\t\t\t\t\t\t\tsequenceHandler = DistributedSequenceHandler.getInstance(MycatServer.getInstance().getConfig().getSystem());\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase SystemConfig.SEQUENCEHANDLER_ZK_GLOBAL_INCREMENT:\n\t\t\t\t\t\t\tsequenceHandler = IncrSequenceZKHandler.getInstance();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tthrow new java.lang.IllegalArgumentException(\"Invalid sequnce handler type \"+seqHandlerType);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tfor(ValuesClause vc : insert.getValuesList()){\n\t\t\t\t\tSQLIntegerExpr sqlIntegerExpr = new SQLIntegerExpr();\n\t\t\t\t\tlong value = sequenceHandler.nextId(tableName.toUpperCase());\n\t\t\t\t\tsqlIntegerExpr.setNumber(value);//插入生成的sequence值\n\t\t\t\t\tvc.addValue(sqlIntegerExpr);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tString insertSql = insert.toString();\n\t\t\t\tthis.executeSql = insertSql;\n\t\t\t}\n\t\t\t\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(\"BatchInsertSequence.route(......)\",e);\n\t\t}\n\t}\n\t\n\t/**\n\t * 根据sql获得路由执行结果\n\t * @param sql\n\t */\n\tprivate void getRoute(String sql){\n\t\ttry {\n\t\t\trrs =RouteStrategyFactory.getRouteStrategy().route(sysConfig, schema, sqltype,sql,charset, sc, cachePool);\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(\"BatchInsertSequence.getRoute(String sql)\",e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/handler/DistributedSequenceHandler.java",
    "content": "package io.mycat.route.sequence.handler;\n\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.util.PropertiesUtil;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.CuratorFrameworkFactory;\nimport org.apache.curator.framework.recipes.leader.CancelLeadershipException;\nimport org.apache.curator.framework.recipes.leader.LeaderSelector;\nimport org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;\nimport org.apache.curator.framework.state.ConnectionState;\nimport org.apache.curator.retry.ExponentialBackoffRetry;\nimport org.apache.curator.utils.CloseableUtils;\nimport org.apache.zookeeper.CreateMode;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 基于ZK与本地配置的分布式ID生成器(可以通过ZK获取集群（机房）唯一InstanceID，也可以通过配置文件配置InstanceID)\n * ID结构：long 64位，ID最大可占63位\n * |current time millis(微秒时间戳38位,可以使用17年)|clusterId（机房或者ZKid，通过配置文件配置5位）|instanceId（实例ID，可以通过ZK或者配置文件获取，5位）|threadId（线程ID，9位）|increment(自增,6位)\n * 一共63位，可以承受单机房单机器单线程1000*(2^6)=640000的并发。\n * 无悲观锁，无强竞争，吞吐量更高\n * <p/>\n * 配置文件：sequence_distributed_conf.properties\n * 只要配置里面：INSTANCEID=ZK就是从ZK上获取InstanceID\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 00:08:03 2016/5/3\n */\npublic class DistributedSequenceHandler extends LeaderSelectorListenerAdapter implements Closeable, SequenceHandler {\n    protected static final Logger LOGGER = LoggerFactory.getLogger(DistributedSequenceHandler.class);\n    private static final String SEQUENCE_DB_PROPS = \"sequence_distributed_conf.properties\";\n    private static DistributedSequenceHandler instance;\n\n    private final long timestampBits = 38L;\n    private final long clusterIdBits = 5L;\n    private final long instanceIdBits = 5L;\n    private final long threadIdBits = 9L;\n    private final long incrementBits = 6L;\n\n    private final long timestampMask = (1L << timestampBits) - 1L;\n\n    private final long incrementShift = 0L;\n    private final long threadIdShift = incrementShift + incrementBits;\n    private final long instanceIdShift = threadIdShift + threadIdBits;\n    private final long clusterIdShift = instanceIdShift + instanceIdBits;\n    private final long timestampShift = clusterIdShift + clusterIdBits;\n\n    private final long maxIncrement = 1L << incrementBits;\n    private final long maxThreadId = 1L << threadIdBits;\n    private final long maxinstanceId = 1L << instanceIdBits;\n    private final long maxclusterId = 1L << instanceIdBits;\n\n    private volatile long instanceId;\n    private long clusterId;\n\n    private ThreadLocal<Long> threadInc = new ThreadLocal<>();\n    private ThreadLocal<Long> threadLastTime = new ThreadLocal<>();\n    private ThreadLocal<Long> threadID = new ThreadLocal<>();\n    private long nextID = 0L;\n\n    private final static String PATH = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_BASE.getKey()\n            + io.mycat.config.loader.zkprocess.comm.ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTERID)\n            + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE.getKey();\n    // private final static String PATH = \"/mycat/sequence\";\n    private final static String INSTANCE_PATH = ZookeeperPath.ZK_SEPARATOR.getKey()\n            + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_INSTANCE.getKey();\n    private final static String LEADER_PATH = ZookeeperPath.ZK_SEPARATOR.getKey()\n            + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_LEADER.getKey();\n    private SystemConfig mycatConfig;\n    private String ID;\n\n    private int mark[];\n    private volatile boolean isLeader = false;\n    private volatile String slavePath;\n    // 配置是否载入好\n    private volatile boolean ready = false;\n\n    private CuratorFramework client;\n\n    private LeaderSelector leaderSelector;\n\n    private final ScheduledExecutorService timerExecutor = Executors.newSingleThreadScheduledExecutor();\n    private ScheduledExecutorService leaderExecutor;\n    private final long SELF_CHECK_PERIOD = 10L;\n\n    public static DistributedSequenceHandler getInstance(SystemConfig systemConfig) {\n        if (instance == null) {\n            instance = new DistributedSequenceHandler(systemConfig);\n        }\n        return instance;\n    }\n\n    public long getClusterId() {\n        return clusterId;\n    }\n\n    public void setClusterId(long clusterId) {\n        this.clusterId = clusterId;\n    }\n\n    public LeaderSelector getLeaderSelector() {\n        return leaderSelector;\n    }\n\n    public long getInstanceId() {\n        return instanceId;\n    }\n\n    public void setInstanceId(long instanceId) {\n        this.instanceId = instanceId;\n    }\n\n    public CuratorFramework getClient() {\n        return client;\n    }\n\n    public void setClient(CuratorFramework client) {\n        this.client = client;\n    }\n\n    public DistributedSequenceHandler(SystemConfig mycatConfig) {\n        this.mycatConfig = mycatConfig;\n        ID = mycatConfig.getBindIp() + mycatConfig.getServerPort();\n    }\n\n    public void load() {\n        // load sequnce properties\n        Properties props = PropertiesUtil.loadProps(SEQUENCE_DB_PROPS);\n        if (\"ZK\".equals(props.getProperty(\"INSTANCEID\"))) {\n            initializeZK(ZkConfig.getInstance().getZkURL());\n        } else {\n            this.instanceId = Long.parseLong(props.getProperty(\"INSTANCEID\"));\n            this.ready = true;\n        }\n        this.clusterId = Long.valueOf(props.getProperty(\"CLUSTERID\"));\n\n    }\n\n    public void initializeZK(String zkAddress) {\n        this.client = CuratorFrameworkFactory.newClient(zkAddress, new ExponentialBackoffRetry(1000, 3));\n        this.client.start();\n        try {\n            if (client.checkExists().forPath(PATH.concat(INSTANCE_PATH)) == null) {\n                client.create().creatingParentContainersIfNeeded().forPath(PATH.concat(INSTANCE_PATH));\n            }\n        } catch (Exception e) {\n            // do nothing\n        }\n        this.leaderSelector = new LeaderSelector(client, PATH.concat(LEADER_PATH), this);\n        this.leaderSelector.autoRequeue();\n        this.leaderSelector.start();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    while (leaderSelector.getLeader() == null) {\n                        Thread.currentThread().yield();\n                    }\n                    if (!leaderSelector.hasLeadership()) {\n                        isLeader = false;\n                        if (slavePath != null && client.checkExists().forPath(slavePath) != null) {\n                            return;\n                        }\n                        slavePath = client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)\n                                .forPath(PATH.concat(\"/instance/node\"), \"ready\".getBytes());\n                        while (\"ready\".equals(new String(client.getData().forPath(slavePath)))) {\n                            Thread.currentThread().yield();\n                        }\n                        instanceId = Long.parseLong(new String(client.getData().forPath(slavePath)));\n                        ready = true;\n                    }\n                } catch (Exception e) {\n                    LOGGER.warn(\"Caught exception while handling zk!\", e);\n                }\n            }\n        };\n        timerExecutor.scheduleAtFixedRate(runnable, 1L, 10L, TimeUnit.SECONDS);\n    }\n\n    @Override\n    public long nextId(String prefixName) {\n        // System.out.println(instanceId);\n        while (!ready) {\n            try {\n                Thread.sleep(50);\n            } catch (InterruptedException e) {\n                LOGGER.warn(\"Unexpected thread interruption!\");\n                Thread.currentThread().interrupt();\n            }\n        }\n        long time = System.currentTimeMillis();\n        if (threadLastTime.get() == null) {\n            threadLastTime.set(time);\n        }\n        if (threadInc.get() == null) {\n            threadInc.set(0L);\n        }\n        if (threadID.get() == null) {\n            threadID.set(getNextThreadID());\n        }\n        long a = threadInc.get();\n        if ((a + 1L) >= maxIncrement) {\n            if (threadLastTime.get() == time) {\n                time = blockUntilNextMillis(time);\n            }\n            threadInc.set(0L);\n        } else {\n            threadInc.set(a + 1L);\n        }\n        threadLastTime.set(time);\n        return ((time & timestampMask) << timestampShift) | (((threadID.get() % maxThreadId) << threadIdShift))\n                | (instanceId << instanceIdShift) | (clusterId << clusterIdShift) | a;\n    }\n\n    private synchronized Long getNextThreadID() {\n        long i = nextID;\n        nextID++;\n        return i;\n    }\n\n    private long blockUntilNextMillis(long time) {\n        while (System.currentTimeMillis() == time) {\n        }\n        return System.currentTimeMillis();\n    }\n\n    @Override\n    public void stateChanged(CuratorFramework client, ConnectionState newState) {\n        if (newState == ConnectionState.SUSPENDED || newState == ConnectionState.LOST) {\n            this.isLeader = false;\n            leaderExecutor.shutdownNow();\n            throw new CancelLeadershipException();\n        }\n    }\n\n    @Override\n    public void takeLeadership(final CuratorFramework curatorFramework) {\n        this.isLeader = true;\n        this.instanceId = 1;\n        this.ready = true;\n        this.mark = new int[(int) maxinstanceId];\n        List<String> children = null;\n        try {\n            if (this.slavePath != null) {\n                client.delete().forPath(slavePath);\n            }\n            if (client.checkExists().forPath(PATH.concat(INSTANCE_PATH)) != null) {\n                children = client.getChildren().forPath(PATH.concat(INSTANCE_PATH));\n            }\n            if (children != null) {\n                for (String child : children) {\n                    String data = new String(\n                            client.getData().forPath(PATH.concat(INSTANCE_PATH.concat(\"/\").concat(child))));\n                    if (!\"ready\".equals(data)) {\n                        mark[Integer.parseInt(data)] = 1;\n                    }\n                }\n            }\n        } catch (Exception e) {\n            LOGGER.warn(\"Caught exception while handling zk!\", e);\n        }\n\n        leaderExecutor = Executors.newSingleThreadScheduledExecutor();\n        leaderExecutor.scheduleAtFixedRate(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    while (!client.isStarted()) {\n                        Thread.currentThread().yield();\n                    }\n                    List<String> children = client.getChildren().forPath(PATH.concat(INSTANCE_PATH));\n                    int mark2[] = new int[(int) maxinstanceId];\n                    for (String child : children) {\n                        String data = new String(client.getData().forPath(PATH.concat(\"/instance/\" + child)));\n                        if (\"ready\".equals(data)) {\n                            int i = nextFree();\n                            client.setData().forPath(PATH.concat(INSTANCE_PATH.concat(\"/\").concat(child)),\n                                    (\"\" + i).getBytes());\n                            mark2[i] = 1;\n                        } else {\n                            mark2[Integer.parseInt(data)] = 1;\n                        }\n                    }\n                    mark = mark2;\n                } catch (Exception e) {\n                    LOGGER.warn(\"Caught exception while handling zk!\", e);\n                }\n            }\n        }, 0L, 3L, TimeUnit.SECONDS);\n        while (true) {\n            Thread.currentThread().yield();\n        }\n    }\n\n    private int nextFree() {\n        for (int i = 0; i < mark.length; i++) {\n            if (i == 1) {\n                continue;\n            }\n            if (mark[i] != 1) {\n                mark[i] = 1;\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    @Override\n    public void close() throws IOException {\n        CloseableUtils.closeQuietly(this.leaderSelector);\n        CloseableUtils.closeQuietly(this.client);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/handler/HttpIncrSequenceHandler.java",
    "content": "package io.mycat.route.sequence.handler;\n\nimport io.mycat.route.util.PropertiesUtil;\nimport okhttp3.*;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOException;\nimport java.util.Properties;\n\npublic class HttpIncrSequenceHandler implements SequenceHandler {\n    protected static final Logger LOGGER = LoggerFactory\n            .getLogger(HttpIncrSequenceHandler.class);\n    private final String url;\n    private long count = 0;\n    private long limit = -1;\n\n    public HttpIncrSequenceHandler() {\n        Properties props = PropertiesUtil.loadProps(\"sequence_http_conf.properties\");\n        this.url = (String) props.get(\"url\");\n    }\n\n    @Override\n    public synchronized long nextId(String prefixName) {\n        if (count > limit) {\n            try {\n                OkHttpClient client = new OkHttpClient.Builder()\n                        .build();\n                RequestBody body = new FormBody.Builder()\n                        .add(\"prefixName\", prefixName)\n                        .add(\"count\",Long.toString(count))\n                        .add(\"limit\", Long.toString(limit))\n                        .build();\n                Request request = new Request.Builder()\n                        .url(url)\n                        .post(body)\n                        .build();\n                final Call call = client.newCall(request);\n                Response response = call.execute();\n                ResponseBody body1 = response.body();\n                String s = new String(body1.bytes());\n                String[] split = s.split(\",\");\n                this.count = Long.parseLong(split[0]);\n                this.limit = Long.parseLong(split[1]);\n            } catch (Throwable e) {\n                LOGGER.error(\"\", e);\n                throw new RuntimeException(e);\n            }\n        }\n        return count++;\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/handler/IncrSequenceBDBHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.sequence.handler;\n\nimport java.util.Map;\n\n/**\n * BDB 数据库实现递增序列号\n * \n * @author <a href=\"http://www.micmiu.com\">Michael</a>\n * @time Create on 2013-12-29 下午11:05:44\n * @version 1.0\n */\npublic class IncrSequenceBDBHandler extends IncrSequenceHandler {\n\n\tprivate static class IncrSequenceBDBHandlerHolder {\n\t\tprivate static final IncrSequenceBDBHandler instance = new IncrSequenceBDBHandler();\n\t}\n\n\tpublic static IncrSequenceBDBHandler getInstance() {\n\t\treturn IncrSequenceBDBHandlerHolder.instance;\n\t}\n\n\tprivate IncrSequenceBDBHandler() {\n\t}\n\n\t@Override\n\tpublic Map<String, String> getParaValMap(String prefixName) {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Boolean fetchNextPeriod(String prefixName) {\n\t\t\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Boolean updateCURIDVal(String prefixName, Long val) {\n\t\t\n\t\treturn null;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/handler/IncrSequenceHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.sequence.handler;\n\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 递增序列号处理器\n * \n * @author <a href=\"http://www.micmiu.com\">Michael</a>\n * @time Create on 2013-12-29 下午10:42:39\n * @version 1.0\n */\npublic abstract class IncrSequenceHandler implements SequenceHandler {\n\n\tpublic static final Logger logger = LoggerFactory\n\t\t\t.getLogger(IncrSequenceHandler.class);\n\n\tpublic static final String FILE_NAME = \"sequence_conf.properties\";\n\n\tpublic static final String KEY_HIS_NAME = \".HISIDS\";// 1-10000,50001-60000\n\tpublic static final String KEY_MIN_NAME = \".MINID\";// 1\n\tpublic static final String KEY_MAX_NAME = \".MAXID\";// 10000\n\tpublic static final String KEY_CUR_NAME = \".CURID\";// 888\n\n\tpublic abstract Map<String, String> getParaValMap(String prefixName);\n\n\tpublic abstract Boolean updateCURIDVal(String prefixName, Long val);\n\n\tpublic abstract Boolean fetchNextPeriod(String prefixName);\n\n\t@Override\n\tpublic long nextId(String prefixName) {\n\t\tMap<String, String> paraMap = this.getParaValMap(prefixName);\n\t\tif (null == paraMap) {\n\t\t\tthrow new RuntimeException(\"fetch Param Values error.\");\n\t\t}\n\t\tLong nextId = Long.parseLong(paraMap.get(prefixName + KEY_CUR_NAME)) + 1;\n\t\tLong maxId = Long.parseLong(paraMap.get(prefixName + KEY_MAX_NAME));\n\t\tif (nextId > maxId) {\n\t\t\tfetchNextPeriod(prefixName);\n\t\t\treturn nextId(prefixName);\n\t\t}\n\t\tupdateCURIDVal(prefixName, nextId);\n\t\treturn nextId.longValue();\n\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/handler/IncrSequenceMySQLHandler.java",
    "content": "package io.mycat.route.sequence.handler;\n\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.util.ConfigException;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.route.util.PropertiesUtil;\nimport io.mycat.server.parser.ServerParse;\n\npublic class IncrSequenceMySQLHandler implements SequenceHandler {\n\n\tprotected static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(IncrSequenceMySQLHandler.class);\n\n\tprivate static final String SEQUENCE_DB_PROPS = \"sequence_db_conf.properties\";\n\tprotected static final String errSeqResult = \"-999999,null\";\n\tprotected static Map<String, String> latestErrors = new ConcurrentHashMap<String, String>();\n\tprivate final FetchMySQLSequnceHandler mysqlSeqFetcher = new FetchMySQLSequnceHandler();\n\n\tprivate static class IncrSequenceMySQLHandlerHolder {\n\t\tprivate static final IncrSequenceMySQLHandler instance = new IncrSequenceMySQLHandler();\n\t}\n\n\tpublic static IncrSequenceMySQLHandler getInstance() {\n\t\treturn IncrSequenceMySQLHandlerHolder.instance;\n\t}\n\n\tprivate IncrSequenceMySQLHandler() {\n\n\t\tload();\n\t}\n\t//加载配置文件\n\tpublic void load() {\n\t\t// load sequnce properties\n\t\tProperties props = PropertiesUtil.loadProps(SEQUENCE_DB_PROPS);\n\t\tremoveDesertedSequenceVals(props);\n\t\tputNewSequenceVals(props);\n\t}\n\n\t//移除旧的配置\n\tprivate void removeDesertedSequenceVals(Properties props) {\n\t\tIterator<Map.Entry<String, SequenceVal>> i = seqValueMap.entrySet()\n\t\t\t\t.iterator();\n\t\twhile (i.hasNext()) {\n\t\t\tMap.Entry<String, SequenceVal> entry = i.next();\n\t\t\tif (!props.containsKey(entry.getKey())) {\n\t\t\t\ti.remove();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void putNewSequenceVals(Properties props) {\n\t\tfor (Map.Entry<Object, Object> entry : props.entrySet()) {\n\t\t\tString seqName = (String) entry.getKey();\n\t\t\tString dataNode = (String) entry.getValue();\n\t\t\tif (!seqValueMap.containsKey(seqName)) {\n\t\t\t\tseqValueMap.put(seqName, new SequenceVal(seqName, dataNode));\n\t\t\t} else {\n\t\t\t\tseqValueMap.get(seqName).dataNode = dataNode;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * save sequnce -> curval\n\t */\n\tprivate ConcurrentHashMap<String, SequenceVal> seqValueMap = new ConcurrentHashMap<String, SequenceVal>();\n\n\t@Override\n\tpublic long nextId(String seqName) {\n\t\tSequenceVal seqVal = seqValueMap.get(seqName);\n\t\tif (seqVal == null) {\n\t\t\tthrow new ConfigException(\"can't find definition for sequence :\"\n\t\t\t\t\t+ seqName);\n\t\t}\n\t\tif (!seqVal.isSuccessFetched()) {\n\t\t\t//从数据库获取\n\t\t\treturn getSeqValueFromDB(seqVal);\n\t\t} else {\n\t\t\t//已经设置 获取下一个有效id\n\t\t\treturn getNextValidSeqVal(seqVal);\n\t\t}\n\n\t}\n\t//获取有效的sequence\n\tprivate Long getNextValidSeqVal(SequenceVal seqVal) {\n\t\tLong nexVal = seqVal.nextValue();\n\t\t//当前id有效返回 无效则从数据库获取\n\t\tif (seqVal.isNexValValid(nexVal)) {\n\t\t\treturn nexVal;\n\t\t} else {\n//\t\t\tseqVal.fetching.compareAndSet(true, false);\n\t\t\treturn getSeqValueFromDB(seqVal);\n\t\t}\n\t}\n\n\t//在这边用锁。\n\t/*\n\t * 1.尝试获取fetching锁， \n\t *    成功： \n\t *       再次判断下一个值是否有效，有效直接返回\n\t *       否则 调用waitFinish请求后端的请求\n\t *     失败：\n\t *        进行等待，等待一定次数 尝试从数据库获取\n\t *             别的后端请求已经成功获取，调用getNextValidSeqVal获取下一个可用值\n\t *  \n\t * \n\t * */\n\tprivate long getSeqValueFromDB(SequenceVal seqVal) {\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"get next segement of sequence from db for sequnce:\"\n\t\t\t\t\t+ seqVal.seqName + \" curVal \" + seqVal.curVal);\n\t\t}\n\t\t//设置正在获取\n\t\tboolean isLock = seqVal.fetching.compareAndSet(false, true);\n\t\tif(isLock) {\n\t\t\t//判断当前的是否有效。\n\t\t\tif(seqVal.successFetched == true) {\n\t\t\t\tLong nexVal = seqVal.nextValue();\n\t\t\t\tif (seqVal.isNexValValid(nexVal)) {\n\t\t\t\t\tseqVal.fetching.compareAndSet(true, false);\n\t\t\t\t\treturn nexVal;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\t\t\t\n\t\t\t//发起请求sql 等待到返回  或者进行\n\t\t\tLong[] values = seqVal.fetchSequenceFromDB( mysqlSeqFetcher, 1, true); //只有一个线程可以进 并且有重试机制。\n\t\t\tif (values == null) {\n\t\t\t\tthrow new RuntimeException(\"can't fetch sequnce in db,sequnce :\"\n\t\t\t\t\t\t+ seqVal.seqName + \" detail:\"\n\t\t\t\t\t\t+ mysqlSeqFetcher.getLastestError(seqVal.seqName));\n\t\t\t} else {\n\t\t\t\t\tseqVal.setCurValue(values[0]); \n\t\t\t\t\tseqVal.maxSegValue = values[1];\n\t\t\t\t\tseqVal.successFetched = true; //设置successFetched\n\t\t\t\t\treturn values[0];\n\t\t\t\n\t\t\t}\n\t\t} else {\n\t\t\tlong count = 0 ;\n\t\t\t//正在获取 ，或者还未返回\n\t\t\twhile(seqVal.fetching.get() || seqVal.successFetched == false){\n\t\t\t\ttry {\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tThread.sleep(10);\n\t\t\t\t\tif(++count > 10000L) {\n\t\t\t\t\t\treturn this.getSeqValueFromDB(seqVal);\n\t\t\t\t\t}\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\treturn this.getSeqValueFromDB(seqVal);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this.getNextValidSeqVal(seqVal);\n\t\t}\t\n\t}\n\n}\n\nclass FetchMySQLSequnceHandler implements ResponseHandler {\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(FetchMySQLSequnceHandler.class);\n\n\tpublic void execute(SequenceVal seqVal) {\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\tPhysicalDBNode mysqlDN = conf.getDataNodes().get(seqVal.dataNode);\n\t\ttry {\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(\"execute in datanode \" + seqVal.dataNode\n\t\t\t\t\t\t+ \" for fetch sequnce sql \" + seqVal.sql);\n\t\t\t}\n\t\t\t// 修正获取seq的逻辑，在读写分离的情况下只能走写节点。修改Select模式为Update模式。\n\t\t\tmysqlDN.getConnection(mysqlDN.getDatabase(), true,\n\t\t\t\t\tnew RouteResultsetNode(seqVal.dataNode, ServerParse.UPDATE,\n\t\t\t\t\t\t\tseqVal.sql), this, seqVal);\n\t\t} catch (Exception e) {\n\t\t\tseqVal.dbfinished = true;\n\n\t\t\tLOGGER.warn(\"get connection err \" + e);\n\t\t}\n\n\t}\n\n\tpublic String getLastestError(String seqName) {\n\t\treturn IncrSequenceMySQLHandler.latestErrors.get(seqName);\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(BackendConnection conn) {\n\n\t\tconn.setResponseHandler(this);\n\t\ttry {\n\t\t\t//发起sql请求\n\t\t\tSequenceVal sequenceVal = ((SequenceVal) conn.getAttachment()) ;\n\t\t\tconn.query(sequenceVal.sql);\n\t\t} catch (Exception e) {\n\t\t\texecuteException(conn, e);\n\t\t}\n\t}\n\t//连接错误\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\t\t((SequenceVal) conn.getAttachment()).dbfinished = true;\n\t\tLOGGER.warn(\"connectionError \" + e);\n\n\t}\n\t//返回错误结果处理\n\t@Override\n\tpublic void errorResponse(byte[] data, BackendConnection conn) {\n\t\tSequenceVal seqVal = ((SequenceVal) conn.getAttachment());\n//\t\tseqVal.dbfinished = true;\n\t\tseqVal.setDbfinished();\n\t\tErrorPacket err = new ErrorPacket();\n\t\terr.read(data);\n\t\tString errMsg = new String(err.message);\n\t\tLOGGER.warn(\"errorResponse \" + err.errno + \" \" + errMsg);\n\t\tIncrSequenceMySQLHandler.latestErrors.put(seqVal.seqName, errMsg);\n\t\tconn.release();\n\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] ok, BackendConnection conn) {\n\t\tboolean executeResponse = conn.syncAndExcute();\n\t\tif (executeResponse) {\n\t\t\t((SequenceVal) conn.getAttachment()).setDbfinished();\n//\t\t\t((SequenceVal) conn.getAttachment()).dbfinished = true;\n\t\t\tconn.release();\n\t\t}\n\n\t}\n\t//获取一行的数据 \n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t\tRowDataPacket rowDataPkg = new RowDataPacket(1);\n\t\trowDataPkg.read(row);\n\t\tbyte[] columnData = rowDataPkg.fieldValues.get(0);\n\t\tString columnVal = new String(columnData);\n\t\tSequenceVal seqVal = (SequenceVal) conn.getAttachment();\n\t\tif (IncrSequenceMySQLHandler.errSeqResult.equals(columnVal)) {\n\t\t\tseqVal.dbretVal = IncrSequenceMySQLHandler.errSeqResult;\n\t\t\tLOGGER.warn(\" sequnce sql returned err value ,sequence:\"\n\t\t\t\t\t+ seqVal.seqName + \" \" + columnVal + \" sql:\" + seqVal.sql);\n\t\t}else {\n\t\t\tseqVal.dbretVal = columnVal;\n\t\t}\n\t}\n\t//结果集合接受完毕\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t\tSequenceVal sequenceVal = ((SequenceVal) conn.getAttachment());\n\t\tconn.release();\n//\t\tsequenceVal.dbfinished = true;\n\t\tsequenceVal.setDbfinished();\n\t}\n\t//错误返回异常处理\n\tprivate void executeException(BackendConnection c, Throwable e) {\n\t\tSequenceVal seqVal = ((SequenceVal) c.getAttachment());\n//\t\tseqVal.dbfinished = true;\n\t\tseqVal.setDbfinished();\n\t\tString errMgs=e.toString();\n\t\tLOGGER.error(\"{}\",e);\n\t\tIncrSequenceMySQLHandler.latestErrors.put(seqVal.seqName, errMgs);\n\t\tc.close(\"exception:\" +errMgs);\n\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\n\t}\n\t//连接关闭异常处理\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\t\t//((SequenceVal) conn.getAttachment()).dbfinished = true;\n\t\t((SequenceVal) conn.getAttachment()).setDbfinished();\n\t\tLOGGER.warn(\"connection closed \" + conn + \" reason:\" + reason);\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\n\t}\n\n}\n\n"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/handler/IncrSequencePropHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.sequence.handler;\n\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.OutputStream;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\n\n/**\n * 本地prop文件实现递增序列号\n * \n * @author <a href=\"http://www.micmiu.com\">Michael</a>\n * @time Create on 2013-12-29 下午11:00:05\n * @version 1.0\n */\npublic class IncrSequencePropHandler extends IncrSequenceHandler {\n\tprivate String filePath;\n\n\tprivate static class IncrSequencePropHandlerHolder {\n\t\tprivate static final IncrSequencePropHandler instance = new IncrSequencePropHandler();\n\t}\n\n\tpublic static IncrSequencePropHandler getInstance() {\n\t\treturn IncrSequencePropHandlerHolder.instance;\n\t}\n\n\tprivate IncrSequencePropHandler() {\n\t\tfilePath = Thread.currentThread().getContextClassLoader()\n\t\t\t\t.getResource(\"\").getPath().replaceAll(\"%20\", \" \")\n\t\t\t\t+ FILE_NAME;\n\t\t// filePath = SystemConfig.getHomePath() + \"/conf/seq_gloal.properties\";\n\t}\n\n\t@Override\n\tpublic Map<String, String> getParaValMap(String prefixName) {\n\n\t\tMap<String, String> valMap = new HashMap<String, String>();\n\t\tProperties prop = new Properties();\n\t\ttry {\n\t\t\tprop.load(new FileInputStream(filePath));\n\t\t\tvalMap.put(prefixName + KEY_HIS_NAME,\n\t\t\t\t\tprop.getProperty(prefixName + KEY_HIS_NAME));\n\t\t\tvalMap.put(prefixName + KEY_MIN_NAME,\n\t\t\t\t\tprop.getProperty(prefixName + KEY_MIN_NAME));\n\t\t\tvalMap.put(prefixName + KEY_MAX_NAME,\n\t\t\t\t\tprop.getProperty(prefixName + KEY_MAX_NAME));\n\t\t\tvalMap.put(prefixName + KEY_CUR_NAME,\n\t\t\t\t\tprop.getProperty(prefixName + KEY_CUR_NAME));\n\t\t} catch (Exception e) {\n\t\t\tlogger.error(e.getMessage());\n\t\t\treturn null;\n\t\t}\n\n\t\treturn valMap;\n\t}\n\n\t@Override\n\tpublic Boolean fetchNextPeriod(String prefixName) {\n\t\tProperties props = new Properties();\n\t\ttry {\n\t\t\tprops.load(new FileInputStream(filePath));\n\t\t\tString minStr = props.getProperty(prefixName + KEY_MIN_NAME);\n\t\t\tString maxStr = props.getProperty(prefixName + KEY_MAX_NAME);\n\t\t\tString hisIDS = props.getProperty(prefixName + KEY_HIS_NAME);\n\t\t\tprops.setProperty(prefixName + KEY_HIS_NAME,\n\t\t\t\t\t\"\".equals(hisIDS) ? minStr + \"-\" + maxStr : \",\" + minStr\n\t\t\t\t\t\t\t+ \"-\" + maxStr);\n\t\t\tlong minId = Long.parseLong(minStr);\n\t\t\tlong maxId = Long.parseLong(maxStr);\n\t\t\tprops.setProperty(prefixName + KEY_MIN_NAME, (maxId + 1) + \"\");\n\t\t\tprops.setProperty(prefixName + KEY_MAX_NAME,\n\t\t\t\t\t(maxId - minId + maxId + 1) + \"\");\n\t\t\tprops.setProperty(prefixName + KEY_CUR_NAME, maxStr);\n\t\t\tOutputStream fos = new FileOutputStream(filePath);\n\t\t\tprops.store(fos, \"\");\n\t\t} catch (Exception e) {\n\t\t\tlogger.error(e.getLocalizedMessage());\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic synchronized Boolean  updateCURIDVal(String prefixName, Long val) {\n\t\tProperties props = new Properties();\n\t\ttry {\n\t\t\tprops.load(new FileInputStream(filePath));\n\t\t\tprops.setProperty(prefixName + KEY_CUR_NAME, val.longValue() + \"\");\n\t\t\tOutputStream fos = new FileOutputStream(filePath);\n\t\t\tprops.store(fos, \"\");\n\t\t} catch (Exception e) {\n\t\t\tlogger.error(e.getLocalizedMessage());\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/handler/IncrSequenceTimeHandler.java",
    "content": "package io.mycat.route.sequence.handler;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Properties;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\npublic class IncrSequenceTimeHandler implements SequenceHandler {\n    protected static final Logger LOGGER = LoggerFactory.getLogger(IncrSequenceTimeHandler.class);\n\n\tprivate static final String SEQUENCE_DB_PROPS = \"sequence_time_conf.properties\";\n\tprivate static final IncrSequenceTimeHandler instance = new IncrSequenceTimeHandler();\n\tprivate static IdWorker workey = new IdWorker(1,1);\n\n\n\tpublic static IncrSequenceTimeHandler getInstance() {\n\t\treturn IncrSequenceTimeHandler.instance;\n\t}\n\n\tpublic IncrSequenceTimeHandler() {\n\t\tload();\n\t}\n\n\n\tpublic void load(){\n\t\t// load sequnce properties\n\t\tProperties props = loadProps(SEQUENCE_DB_PROPS);\n\n\t\tlong workid = Long.parseLong(props.getProperty(\"WORKID\"));\n\t\tlong dataCenterId = Long.parseLong(props.getProperty(\"DATAACENTERID\"));\n\n\t\tworkey = new IdWorker(workid,dataCenterId);\n\t}\n\tprivate Properties loadProps(String propsFile){\n\t\tProperties props = new Properties();\n\t\tInputStream inp = Thread.currentThread().getContextClassLoader().getResourceAsStream(propsFile);\n\n\t\tif (inp == null) {\n\t\t\tthrow new java.lang.RuntimeException(\"time sequnce properties not found \" + propsFile);\n\t\t}\n\t\ttry {\n\t\t\tprops.load(inp);\n\t\t} catch (IOException e) {\n\t\t\tthrow new java.lang.RuntimeException(e);\n\t\t}\n\t\treturn props;\n\t}\n\t@Override\n\tpublic long nextId(String prefixName) {\n\t\treturn workey.nextId();\n\t}\n\n\n\t/**\n\t* 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加))\n\t* @author sw\n\t*/\n\tstatic class IdWorker {\n\t\tprivate final static long twepoch = 1288834974657L;\n\t\t// 机器标识位数\n\t\tprivate final static long workerIdBits = 5L;\n\t\t// 数据中心标识位数\n\t\tprivate final static long datacenterIdBits = 5L;\n\t\t// 机器ID最大值 31\n\t\tprivate final static long maxWorkerId = -1L ^ (-1L << workerIdBits);\n\t\t// 数据中心ID最大值 31\n\t\tprivate final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);\n\t\t// 毫秒内自增位\n\t\tprivate final static long sequenceBits = 12L;\n\t\t// 机器ID偏左移12位\n\t\tprivate final static long workerIdShift = sequenceBits;\n\t\tprivate final static long datacenterIdShift = sequenceBits + workerIdBits;\n\t\t// 时间毫秒左移22位\n\t\tprivate final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;\n\n\t\tprivate final static long sequenceMask = -1L ^ (-1L << sequenceBits);\n\n\t\tprivate static long lastTimestamp = -1L;\n\n\t\tprivate long sequence = 0L;\n\t\tprivate final long workerId;\n\t\tprivate final long datacenterId;\n\n\t\tpublic IdWorker(long workerId, long datacenterId) {\n\t\t\tif (workerId > maxWorkerId || workerId < 0) {\n\t\t\t\tthrow new IllegalArgumentException(\"worker Id can't be greater than %d or less than 0\");\n\t\t\t}\n\t\t\tif (datacenterId > maxDatacenterId || datacenterId < 0) {\n\t\t\t\tthrow new IllegalArgumentException(\"datacenter Id can't be greater than %d or less than 0\");\n\t\t\t}\n\t\t\tthis.workerId = workerId;\n\t\t\tthis.datacenterId = datacenterId;\n\t\t}\n\n\t\tpublic synchronized long nextId() {\n\t\t\tlong timestamp = timeGen();\n\t\t\tif (timestamp < lastTimestamp) {\n\t\t\ttry {\n\t\t\t\tthrow new Exception(\"Clock moved backwards.  Refusing to generate id for \"+ (lastTimestamp - timestamp) + \" milliseconds\");\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(\"error\",e);\n\t\t\t}\n\t\t\t}\n\n\t\t\tif (lastTimestamp == timestamp) {\n\t\t\t\t// 当前毫秒内，则+1\n\t\t\t\tsequence = (sequence + 1) & sequenceMask;\n\t\t\t\tif (sequence == 0) {\n\t\t\t\t\t// 当前毫秒内计数满了，则等待下一秒\n\t\t\t\t\ttimestamp = tilNextMillis(lastTimestamp);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsequence = timestamp & 1;\n\t\t\t}\n\t\t\tlastTimestamp = timestamp;\n\t\t\t// ID偏移组合生成最终的ID，并返回ID\n\t\t\tlong nextId = ((timestamp - twepoch) << timestampLeftShift)\n\t\t\t\t\t| (datacenterId << datacenterIdShift)\n\t\t\t\t\t| (workerId << workerIdShift) | sequence;\n\n\t\t\treturn nextId;\n\t\t}\n\n\t\tprivate long tilNextMillis(final long lastTimestamp) {\n\t\t\tlong timestamp = this.timeGen();\n\t\t\twhile (timestamp <= lastTimestamp) {\n\t\t\t\ttimestamp = this.timeGen();\n\t\t\t}\n\t\t\treturn timestamp;\n\t\t}\n\n\t\tprivate long timeGen() {\n\t\t\treturn System.currentTimeMillis();\n\t\t}\n\n\n\n\t}\n\n\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/handler/IncrSequenceZKHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.sequence.handler;\n\n\nimport io.mycat.config.loader.console.ZookeeperPath;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.route.util.PropertiesUtil;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.CuratorFrameworkFactory;\nimport org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;\nimport org.apache.curator.retry.ExponentialBackoffRetry;\nimport org.apache.curator.utils.ZKPaths;\nimport org.apache.zookeeper.CreateMode;\nimport org.apache.zookeeper.data.Stat;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * zookeeper 实现递增序列号\n * 配置文件：sequence_conf.properties\n * 只要配置好ZK地址和表名的如下属性\n * TABLE.MINID 某线程当前区间内最小值\n * TABLE.MAXID 某线程当前区间内最大值\n * TABLE.CURID 某线程当前区间内当前值\n * 文件配置的MAXID以及MINID决定每次取得区间，这个对于每个线程或者进程都有效\n * 文件中的这三个属性配置只对第一个进程的第一个线程有效，其他线程和进程会动态读取ZK\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 23:35 2016/5/6\n */\npublic class IncrSequenceZKHandler extends IncrSequenceHandler {\n    protected static final Logger LOGGER = LoggerFactory.getLogger(IncrSequenceHandler.class);\n    private final static String PATH = ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_BASE.getKey()\n            + ZookeeperPath.ZK_SEPARATOR.getKey()\n            + io.mycat.config.loader.zkprocess.comm.ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTERID)\n            + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE.getKey()\n            + ZookeeperPath.ZK_SEPARATOR.getKey() + ZookeeperPath.FLOW_ZK_PATH_SEQUENCE_INCREMENT_SEQ.getKey();\n    private final static String LOCK = \"/lock\";\n    private final static String SEQ = \"/seq\";\n    private final static IncrSequenceZKHandler instance = new IncrSequenceZKHandler();\n\n    public static IncrSequenceZKHandler getInstance() {\n        return instance;\n    }\n\n    private ThreadLocal<Map<String, Map<String, String>>> tableParaValMapThreadLocal = new ThreadLocal<>();\n\n    private CuratorFramework client;\n    private ThreadLocal<InterProcessSemaphoreMutex> interProcessSemaphoreMutexThreadLocal = new ThreadLocal<>();\n    private Properties props;\n\n    public void load() {\n        props = PropertiesUtil.loadProps(FILE_NAME);\n        String zkAddress = ZkConfig.getInstance().getZkURL();\n        try {\n            initializeZK(props, zkAddress);\n        } catch (Exception e) {\n            LOGGER.error(\"Error caught while initializing ZK:\" + e.getCause());\n        }\n    }\n\n    public void threadLocalLoad() throws Exception {\n        Enumeration<?> enu = props.propertyNames();\n        while (enu.hasMoreElements()) {\n            String key = (String) enu.nextElement();\n            if (key.endsWith(KEY_MIN_NAME)) {\n                handle(key);\n            }\n        }\n    }\n\n    public void initializeZK(Properties props, String zkAddress) throws Exception {\n        this.client = CuratorFrameworkFactory.newClient(zkAddress, new ExponentialBackoffRetry(1000, 3));\n        this.client.start();\n        this.props = props;\n        Enumeration<?> enu = props.propertyNames();\n        while (enu.hasMoreElements()) {\n            String key = (String) enu.nextElement();\n            if (key.endsWith(KEY_MIN_NAME)) {\n                handle(key);\n            }\n        }\n    }\n\n    private void handle(String key) throws Exception {\n        String table = key.substring(0, key.indexOf(KEY_MIN_NAME));\n        InterProcessSemaphoreMutex interProcessSemaphoreMutex = interProcessSemaphoreMutexThreadLocal.get();\n        if (interProcessSemaphoreMutex == null) {\n            interProcessSemaphoreMutex = new InterProcessSemaphoreMutex(client, PATH + \"/\" + table + SEQ + LOCK);\n            interProcessSemaphoreMutexThreadLocal.set(interProcessSemaphoreMutex);\n        }\n        Map<String, Map<String, String>> tableParaValMap = tableParaValMapThreadLocal.get();\n        if (tableParaValMap == null) {\n            tableParaValMap = new HashMap<>();\n            tableParaValMapThreadLocal.set(tableParaValMap);\n        }\n        Map<String, String> paraValMap = tableParaValMap.get(table);\n        if (paraValMap == null) {\n            paraValMap = new ConcurrentHashMap<>();\n            tableParaValMap.put(table, paraValMap);\n\n            String seqPath = PATH + ZookeeperPath.ZK_SEPARATOR.getKey() + table + SEQ;\n\n            Stat stat = this.client.checkExists().forPath(seqPath);\n\n            if (stat == null || (stat.getDataLength() == 0)) {\n                paraValMap.put(table + KEY_MIN_NAME, props.getProperty(key));\n                paraValMap.put(table + KEY_MAX_NAME, props.getProperty(table + KEY_MAX_NAME));\n                paraValMap.put(table + KEY_CUR_NAME, props.getProperty(table + KEY_CUR_NAME));\n                try {\n                    String val = props.getProperty(table + KEY_MIN_NAME);\n                    client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT)\n                            .forPath(PATH + \"/\" + table + SEQ, val.getBytes());\n                } catch (Exception e) {\n                    LOGGER.debug(\"Node exists! Maybe other instance is initializing!\");\n                }\n            }\n            fetchNextPeriod(table);\n        }\n    }\n\n    @Override\n    public Map<String, String> getParaValMap(String prefixName) {\n        Map<String, Map<String, String>> tableParaValMap = tableParaValMapThreadLocal.get();\n        if (tableParaValMap == null) {\n            try {\n                threadLocalLoad();\n            } catch (Exception e) {\n                LOGGER.error(\"Error caught while loding configuration within current thread:\" + e.getCause());\n            }\n            tableParaValMap = tableParaValMapThreadLocal.get();\n        }\n        Map<String, String> paraValMap = tableParaValMap.get(prefixName);\n        return paraValMap;\n    }\n\n    @Override\n    public Boolean fetchNextPeriod(String prefixName) {\n        InterProcessSemaphoreMutex interProcessSemaphoreMutex = interProcessSemaphoreMutexThreadLocal.get();\n        try {\n            if (interProcessSemaphoreMutex == null) {\n                throw new IllegalStateException(\"IncrSequenceZKHandler should be loaded first!\");\n            }\n            interProcessSemaphoreMutex.acquire();\n            Map<String, Map<String, String>> tableParaValMap = tableParaValMapThreadLocal.get();\n            if (tableParaValMap == null) {\n                throw new IllegalStateException(\"IncrSequenceZKHandler should be loaded first!\");\n            }\n            Map<String, String> paraValMap = tableParaValMap.get(prefixName);\n            if (paraValMap == null) {\n                throw new IllegalStateException(\"IncrSequenceZKHandler should be loaded first!\");\n            }\n            if (paraValMap.get(prefixName + KEY_MAX_NAME) == null) {\n                paraValMap.put(prefixName + KEY_MAX_NAME, props.getProperty(prefixName + KEY_MAX_NAME));\n            }\n            if (paraValMap.get(prefixName + KEY_MIN_NAME) == null) {\n                paraValMap.put(prefixName + KEY_MIN_NAME, props.getProperty(prefixName + KEY_MIN_NAME));\n            }\n            if (paraValMap.get(prefixName + KEY_CUR_NAME) == null) {\n                paraValMap.put(prefixName + KEY_CUR_NAME, props.getProperty(prefixName + KEY_CUR_NAME));\n            }\n            long period = Long.parseLong(paraValMap.get(prefixName + KEY_MAX_NAME))\n                    - Long.parseLong(paraValMap.get(prefixName + KEY_MIN_NAME));\n            long now = Long.parseLong(new String(client.getData().forPath(PATH + \"/\" + prefixName + SEQ)));\n            client.setData().forPath(PATH + \"/\" + prefixName + SEQ, ((now + period + 1) + \"\").getBytes());\n\n            paraValMap.put(prefixName + KEY_MAX_NAME, (now + period + 1) + \"\");\n            paraValMap.put(prefixName + KEY_MIN_NAME, (now + 1) + \"\");\n            paraValMap.put(prefixName + KEY_CUR_NAME, (now) + \"\");\n\n        } catch (Exception e) {\n            LOGGER.error(\"Error caught while updating period from ZK:\" + e.getCause());\n        } finally {\n            try {\n                interProcessSemaphoreMutex.release();\n            } catch (Exception e) {\n                LOGGER.error(\"Error caught while realeasing distributed lock\" + e.getCause());\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public Boolean updateCURIDVal(String prefixName, Long val) {\n        Map<String, Map<String, String>> tableParaValMap = tableParaValMapThreadLocal.get();\n        if (tableParaValMap == null) {\n            throw new IllegalStateException(\"IncrSequenceZKHandler should be loaded first!\");\n        }\n        Map<String, String> paraValMap = tableParaValMap.get(prefixName);\n        if (paraValMap == null) {\n            throw new IllegalStateException(\"IncrSequenceZKHandler should be loaded first!\");\n        }\n        paraValMap.put(prefixName + KEY_CUR_NAME, val + \"\");\n        return true;\n    }\n\n    public static void main(String[] args) throws UnsupportedEncodingException {\n        IncrSequenceZKHandler incrSequenceZKHandler = new IncrSequenceZKHandler();\n        incrSequenceZKHandler.load();\n        System.out.println(incrSequenceZKHandler.nextId(\"TRAVELRECORD\"));\n        System.out.println(incrSequenceZKHandler.nextId(\"TRAVELRECORD\"));\n        System.out.println(incrSequenceZKHandler.nextId(\"TRAVELRECORD\"));\n        System.out.println(incrSequenceZKHandler.nextId(\"TRAVELRECORD\"));\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/handler/SequenceHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.sequence.handler;\n\n/**\n * \n * @author <a href=\"http://www.micmiu.com\">Michael</a>\n * @time Create on 2013-12-20 下午3:35:53\n * @version 1.0\n */\npublic interface SequenceHandler {\n\n\tpublic long nextId(String prefixName);\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/handler/SequenceVal.java",
    "content": "package io.mycat.route.sequence.handler;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.jdbc.JDBCDatasource;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.DataHostConfig;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicLong;\n\nclass SequenceVal {\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(SequenceVal.class);\n\n\tprivate static final String errSeqResult = \"-999999,null\";\n\tpublic AtomicLong curVal = new AtomicLong(0); //当前可用的id\n\tpublic volatile long maxSegValue; //最大的可用id\n\n\tpublic volatile String dbretVal = null; //数据库的返回结果\n\tpublic volatile boolean dbfinished ;//请求是否成功返回\n\tpublic AtomicBoolean fetching = new AtomicBoolean(false); //是否正在获取id序列当中。\n\tpublic volatile boolean successFetched; //是否已经成功的获取并且设置值了。\n\tpublic volatile String dataNode;//请求的分片节点\n\tpublic final String seqName;//那个表\n\tpublic final String sql;//请求分片的sql SELECT mycat_seq_nextval('tableName');\n\t//初始化\n\tpublic void reset(){\n\n\t\tdbretVal = null;\n\t\tdbfinished = false;\n\t\tsuccessFetched = false; //添加代码\n\n\t}\n\t//设置成功返回\n\tpublic void setDbfinished() {\n\t\tdbfinished = true;\n\t}\n\t//\n\tpublic SequenceVal(String seqName, String dataNode) {\n\t\tthis.seqName = seqName;\n\t\tthis.dataNode = dataNode;\n\t\tsql = \"SELECT mycat_seq_nextval('\" + seqName + \"')\";\n\t}\n\t//nexVal是否可以用的判断\n\tpublic boolean isNexValValid(Long nexVal) {\n\t\tif (nexVal < this.maxSegValue) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t//设置当前的可用值。\n\tpublic void setCurValue(long newValue) {\n\t\tcurVal.set(newValue);\n\t}\n\t// 重试次数 保证最大可能性的获取到。\n\t/*\n\t *1.后端获取序列\n\t *  如果返回的结果为超时，异常，尝试数据库重试获取\n\t *     返回的为0，0   尝试数据库重试获取\n\t *     结果还未返回，调用函数继续等待\n\t *  成功返回则正常返回\n\t * */\n\n\tpublic Long[] fetchSequenceFromDB(FetchMySQLSequnceHandler mysqlSeqFetcher, int retryCount, boolean canSendFetch) {\n\t\tDataHostConfig dataHostConfig = getDBHostConfig();\n\n\t\tif (dataHostConfig.isJDBCDriver()) {\n\t\t\treturn getSequenceValueByJDBC(retryCount);\n\t\t}\n\n\t\tif (dataHostConfig.isNativeDriver()) {\n\t\t\treturn getSequenceByNaitve(mysqlSeqFetcher, retryCount, canSendFetch);\n\t\t}\n\n\t\tthrow new RuntimeException(\"fetching sequence can not support the db driver:\" + dataHostConfig.getDbDriver());\n\t}\n\n\tprivate Long[] getSequenceByNaitve(FetchMySQLSequnceHandler mysqlSeqFetcher, int retryCount, boolean canSendFetch) {\n\t\tfinal int systemRetryCount = MycatServer.getInstance().getConfig().getSystem().getSequnceMySqlRetryCount();\n\t\t//进入waitFinish小于4次，或者可以后端获取数据\n\t\tif(retryCount <= systemRetryCount && canSendFetch) {\n\n\t\t\tthis.reset();\n\t\t\tmysqlSeqFetcher.execute(this);\n\t\t} else if(retryCount > systemRetryCount){\n\t\t\tfetching.compareAndSet(true, false); //\n\t\t\treturn null;\n\t\t}\n\t\tlong start = System.currentTimeMillis();\n\t\t//直接等待\n\t\tlong mysqlWaitTime = MycatServer.getInstance().getConfig().getSystem().getSequnceMySqlWaitTime();\n\n\t\tlong end = start + mysqlWaitTime;\n\t\twhile (System.currentTimeMillis() < end) {\n\t\t\tif(dbfinished){\n\t\t\t\tif (dbretVal == IncrSequenceMySQLHandler.errSeqResult) {\n\t\t\t\t\tfetching.compareAndSet(true, false); //修改\n\t\t\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\t\t\"sequnce not found in db table \");\n\t\t\t\t}\n\t\t\t\t//进行处理 还有可能是链接错误等。\n\t\t\t\tif(dbretVal == null ){\n\t\t\t\t\tLOGGER.warn(\"can't fetch sequnce in db,sequnce :\"\n\t\t\t\t\t\t\t+ seqName + \" detail:\"\n\t\t\t\t\t\t\t+ mysqlSeqFetcher.getLastestError(seqName) + \"\\n\"\n\t\t\t\t\t\t\t\t\t+ \", and retry \" + (retryCount) +\" time\");\n\t\t\t\t\t//数据库之类的连接错误，休息一下在重试。\n\t\t\t\t\tsleep(10);\n\t\t\t\t\treturn getSequenceByNaitve(mysqlSeqFetcher, ++retryCount , true);\n\t\t\t\t}\n\t\t\t\tString[] items = dbretVal.split(\",\");\n\n\t\t\t\tLong curVal = Long.parseLong(items[0]);\n\t\t\t\tint span = Integer.parseInt(items[1]);\n\t\t\t\t//处理返回0，0\n\t\t\t\tif(0 == curVal) {\n\t\t\t\t\tLOGGER.warn(\"can't fetch sequnce in db,sequnce :\"\n\t\t\t\t\t\t\t+ seqName + \" detail:\"\n\t\t\t\t\t\t\t+  \" fetch return 0,0 , and retry \" + (retryCount) +\" time\");\n\t\t\t\t\t//数据库之类的连接错误，休息一下在重试。\n\t\t\t\t\tsleep(100);\n\t\t\t\t\treturn getSequenceByNaitve(mysqlSeqFetcher, ++retryCount, true);\n\t\t\t\t}\n\t\t\t\tfetching.compareAndSet(true, false); //修改s\n\t\t\t\treturn new Long[] { curVal, curVal + span };\n\t\t\t} else{\n\t\t\t\tsleep(10);\n\t\t\t}\n\t\t}\n\t\t//等待超时 重试\n\t\tLOGGER.warn(\"wait sequnce in db sequnce  :\"\n\t\t\t\t+ seqName + \" detail:\"\n\t\t\t\t+ \" wait timeout \" + \" retry time:\" + retryCount);\n\t\treturn getSequenceByNaitve(mysqlSeqFetcher, ++retryCount, false);\n\t}\n\n\n\tprivate Long[] getSequenceValueByJDBC(int retryCount) {\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\tPhysicalDBNode mysqlDN = conf.getDataNodes().get(this.dataNode);\n\t\tfinal int systemRetryCount = MycatServer.getInstance().getConfig().getSystem().getSequnceMySqlRetryCount();\n\t\tlong mysqlWaitTime = MycatServer.getInstance().getConfig().getSystem().getSequnceMySqlWaitTime();\n\n\t\tif (retryCount > systemRetryCount){\n\t\t\tthis.fetching.compareAndSet(true, false);\n\t\t\treturn null;\n\t\t}\n\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"execute in datanode \" + this.dataNode\n\t\t\t\t\t+ \" for fetch sequence sql \" + this.sql + \" with retry count:\" + retryCount);\n\t\t}\n\n\t\tlong start = System.currentTimeMillis();\n\t\t//直接等待\n\t\tlong end = start + mysqlWaitTime;\n\t\twhile (System.currentTimeMillis() < end) {\n\t\t\tPhysicalDatasource physicalDatasource = mysqlDN.getDbPool().getSource();\n\t\t\tif(physicalDatasource instanceof JDBCDatasource) {\n\t\t\t\tJDBCDatasource jdbcDatasource = (JDBCDatasource) physicalDatasource;\n\n\t\t\t\tConnection con = null;\n\t\t\t\tPreparedStatement pstmt = null;\n\t\t\t\tResultSet rs = null;\n\t\t\t\ttry {\n\t\t\t\t\tcon = jdbcDatasource.getDruidConnection();\n\t\t\t\t\tString useDB = \"use `\" + mysqlDN.getDatabase() + \"`;\";\n\t\t\t\t\tpstmt = con.prepareStatement(useDB);\n\t\t\t\t\tpstmt.execute();\n\t\t\t\t\tpstmt = con.prepareStatement(this.sql);\n\t\t\t\t\trs = pstmt.executeQuery();\n\t\t\t\t\tString returnedValue = \"\";\n\t\t\t\t\tif (rs.next())\n\t\t\t\t\t\treturnedValue = rs.getString(1);\n\n\t\t\t\t\tif (StringUtils.isEmpty(returnedValue) || errSeqResult.equals(returnedValue)) {\n\t\t\t\t\t\tLOGGER.warn(\"can't fetch sequnce in db,sequnce :\"\n\t\t\t\t\t\t\t\t+ this.seqName + \" detail:\"\n\t\t\t\t\t\t\t\t+  \" fetch return -999999,null , and retry \" + (retryCount) +\" time\");\n\t\t\t\t\t\t//数据库之类的连接错误，休息一下在重试。\n\t\t\t\t\t\tsleep(100);\n\t\t\t\t\t\treturn getSequenceValueByJDBC(++retryCount);\n\t\t\t\t\t}\n\t\t\t\t\tLong curVal = Long.parseLong(returnedValue.split(\",\")[0]);\n\t\t\t\t\tLong increment = Long.parseLong(returnedValue.split(\",\")[1]);\n\n\t\t\t\t\tif (curVal == 0) {\n\t\t\t\t\t\tLOGGER.warn(\"can't fetch sequnce in db,sequnce :\"\n\t\t\t\t\t\t\t\t+ this.seqName + \" detail:\"\n\t\t\t\t\t\t\t\t+  \" fetch return 0,0 , and retry \" + (retryCount) +\" time\");\n\t\t\t\t\t\t//数据库之类的连接错误，休息一下在重试。\n\t\t\t\t\t\tsleep(100);\n\t\t\t\t\t\treturn getSequenceValueByJDBC(++retryCount);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.fetching.compareAndSet(true, false);\n\t\t\t\t\t\treturn new Long[]{curVal, curVal + increment};\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLOGGER.warn(\"get sequence value err \", e);\n\t\t\t\t} finally {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcon.close();\n\t\t\t\t\t\tpstmt.close();\n\t\t\t\t\t\trs.close();\n\t\t\t\t\t} catch (SQLException e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tLOGGER.error(\"get sequence:\" + this.seqName + \" value failure, please check the sequence config\");\n\t\tthis.fetching.compareAndSet(true, false);\n\t\treturn null;\n\t}\n\n\tprivate DataHostConfig getDBHostConfig() {\n\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\tPhysicalDBNode mysqlDN = conf.getDataNodes().get(this.dataNode);\n\t\tif (mysqlDN == null) {\n\t\t\tthrow new RuntimeException(\"can not find the data node with name:\" + this.dataNode);\n\t\t}\n\t\tPhysicalDatasource physicalDatasource = mysqlDN.getDbPool().getSource();\n\t\treturn physicalDatasource.getHostConfig();\n\t}\n\n\tpublic void sleep(long time) {\n\t\ttry {\n\t\t\tThread.sleep(time);\n\t\t} catch (InterruptedException e) {\n\t\t\tIncrSequenceMySQLHandler.LOGGER\n\t\t\t\t\t.warn(\"wait db fetch sequnce err \" + e);\n\t\t}\n\t}\n\t//是否成功返回。\n\tpublic boolean isSuccessFetched() {\n\t\treturn successFetched;\n\t}\n\t//下一个可用的id\n\tpublic long nextValue() {\n\t\tif (successFetched == false) {\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\"sequnce fetched failed  from db \");\n\t\t}\n\t\treturn curVal.incrementAndGet();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/handler/SnowflakeIdSequenceHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.sequence.handler;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 本地默认获取的全局ID（用于单机或者测试） <br>\n * java for base on https://github.com/twitter/snowflake\n * \n * @author <a href=\"http://www.micmiu.com\">Michael</a>\n * @time Create on 2013-12-22 下午4:52:25\n * @version 1.0\n */\npublic class SnowflakeIdSequenceHandler implements SequenceHandler {\n\n\tprivate final static Logger logger = LoggerFactory\n\t\t\t.getLogger(SequenceHandler.class);\n\n\tprivate final long workerId;\n\tprivate final long datacenterId;\n\tprivate static final long twepoch = 1355285532520L;\n\n\tprivate static final long workerIdBits = 5L;\n\tprivate static final long datacenterIdBits = 5L;\n\tprivate static final long maxWorkerId = -1L ^ -1L << workerIdBits;\n\tprivate static final long maxDatacenterId = -1L ^ -1L << datacenterIdBits;\n\tprivate static final long sequenceBits = 12L;\n\tprivate static final long workerIdShift = sequenceBits;\n\tprivate static final long datacenterIdShift = sequenceBits + workerIdBits;\n\n\tprivate static final long timestampLeftShift = sequenceBits\n\t\t\t+ workerIdBits;\n\tprivate static final long sequenceMask = -1L ^ -1L << sequenceBits;\n\n\tprivate long sequence = 0L;\n\tprivate long lastTimestamp = -1L;\n\n\tpublic SnowflakeIdSequenceHandler(long workerId, long datacenterId) {\n\t\tsuper();\n\t\tSystem.out.println(\"maxWorkerId = \" + maxWorkerId);\n\t\tSystem.out.println(\"maxDatacenterId = \" + maxDatacenterId);\n\t\tif (workerId > this.maxWorkerId || workerId < 0) {\n\t\t\tthrow new IllegalArgumentException(String.format(\n\t\t\t\t\t\"worker Id can't be greater than %d or less than 0\",\n\t\t\t\t\tthis.maxWorkerId));\n\t\t}\n\n\t\tthis.workerId = workerId;\n\t\tif (datacenterId > maxDatacenterId || datacenterId < 0) {\n\t\t\tthrow new IllegalArgumentException(String.format(\n\t\t\t\t\t\"datacenter Id can't be greater than %d or less than 0\",\n\t\t\t\t\tmaxDatacenterId));\n\n\t\t}\n\t\tthis.datacenterId = datacenterId;\n\t\tlogger.info(String\n\t\t\t\t.format(\"worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d\",\n\t\t\t\t\t\ttimestampLeftShift, datacenterIdBits, workerIdBits,\n\t\t\t\t\t\tsequenceBits, workerId));\n\n\t}\n\n\tpublic SnowflakeIdSequenceHandler(long workerId) {\n\t\tthis(workerId, 13);\n\t}\n\n\t// 默认\n\tpublic SnowflakeIdSequenceHandler() {\n\t\tthis(23, 13);\n\t}\n\n\t@Override\n\tpublic synchronized long nextId(String prefixName) {\n\t\tlong timestamp = this.timeGen();\n\t\tif (timestamp < this.lastTimestamp) {\n\t\t\tlogger.error(\n\t\t\t\t\t\"clock is moving backwards.  Rejecting requests until %d.\",\n\t\t\t\t\tlastTimestamp);\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\"Clock moved backwards.  Refusing to generate id for %d milliseconds\",\n\t\t\t\t\t\t\t(this.lastTimestamp - timestamp)));\n\t\t}\n\t\tif (this.lastTimestamp == timestamp) {\n\t\t\tthis.sequence = this.sequence + 1 & this.sequenceMask;\n\t\t\tif (this.sequence == 0) {\n\t\t\t\ttimestamp = this.tilNextMillis(this.lastTimestamp);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.sequence = 0;\n\t\t}\n\n\t\tthis.lastTimestamp = timestamp;\n\t\treturn timestamp - this.twepoch << this.timestampLeftShift\n\t\t\t\t| this.datacenterId << this.datacenterIdShift\n\t\t\t\t| this.workerId << this.workerIdShift | this.sequence;\n\t}\n\n\tprivate synchronized long tilNextMillis(long lastTimestamp) {\n\t\tlong timestamp = this.timeGen();\n\t\twhile (timestamp <= lastTimestamp) {\n\t\t\ttimestamp = this.timeGen();\n\t\t}\n\t\treturn timestamp;\n\t}\n\n\tprivate long timeGen() {\n\t\treturn System.currentTimeMillis();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSnowflakeIdSequenceHandler gen = new SnowflakeIdSequenceHandler(16);\n\t\tSystem.out.println(\"nextId = \" + gen.nextId(null));\n\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/sequence/handler/ThirftClientSequenceHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.sequence.handler;\n\n\n/**\n * 通过Thirft客户端获取集群中心分配的全局ID\n * \n * @author <a href=\"http://www.micmiu.com\">Michael</a>\n * @time Create on 2013-12-25 上午12:15:48\n * @version 1.0\n */\npublic class ThirftClientSequenceHandler implements SequenceHandler {\n\n\t@Override\n\tpublic long nextId(String prefixName) {\n\t\t\n\t\treturn 0;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/util/CacheUtil.java",
    "content": "package io.mycat.route.util;\n\n/**\n * Created by 862911 on 2016/5/4.\n */\npublic class CacheUtil {\n        private static final int RUNS = 10;\n        private static final int DIMENSION_1 = 1024 * 1024;\n        private static final int DIMENSION_2 = 6;\n\n        private static long[][] longs;\n\n        public static void main(String[] args) throws Exception {\n            Thread.sleep(10000);\n            longs = new long[DIMENSION_1][];\n            for (int i = 0; i < DIMENSION_1; i++) {\n                longs[i] = new long[DIMENSION_2];\n                for (int j = 0; j < DIMENSION_2; j++) {\n                    longs[i][j] = 0L;\n                }\n            }\n            System.out.println(\"starting....\");\n\n            long sum = 0L;\n            for (int r = 0; r < RUNS; r++) {\n\n                final long start = System.nanoTime();\n\n//                slow\n            for (int j = 0; j < DIMENSION_2; j++) {\n                for (int i = 0; i < DIMENSION_1; i++) {\n                    sum += longs[i][j];\n                }\n            }\n\n                //fast\n//                for (int i = 0; i < DIMENSION_1; i++) {\n//                    for (int j = 0; j < DIMENSION_2; j++) {\n//                        sum += longs[i][j];\n//                    }\n//                }\n\n                System.out.println((System.nanoTime() - start));\n            }\n\n        }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/util/PartitionUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.util;\n\nimport io.mycat.util.StringUtil;\n\n/**\n * 数据分区工具\n * \n * @author mycat\n */\npublic final class PartitionUtil {\n\n    // 分区长度:数据段分布定义，其中取模的数一定要是2^n， 因为这里使用x % 2^n == x & (2^n - 1)等式，来优化性能。\n    private static final int PARTITION_LENGTH = 1024;\n\n    // %转换为&操作的换算数值\n    private static final long AND_VALUE = PARTITION_LENGTH - 1;\n\n    // 分区线段\n    private final int[] segment = new int[PARTITION_LENGTH];\n\n    /**\n     * <pre>\n     * @param count 表示定义的分区数\n     * @param length 表示对应每个分区的取值长度\n     * 注意：其中count,length两个数组的长度必须是一致的。\n     * 约束：1024 = sum((count[i]*length[i])). count和length两个向量的点积恒等于1024\n     * </pre>\n     */\n    public PartitionUtil(int[] count, int[] length) {\n        if (count == null || length == null || (count.length != length.length)) {\n            throw new RuntimeException(\"error,check your scope & scopeLength definition.\");\n        }\n        int segmentLength = 0;\n        for (int i = 0; i < count.length; i++) {\n            segmentLength += count[i];\n        }\n        int[] ai = new int[segmentLength + 1];\n\n        int index = 0;\n        for (int i = 0; i < count.length; i++) {\n            for (int j = 0; j < count[i]; j++) {\n                ai[++index] = ai[index - 1] + length[i];\n            }\n        }\n        if (ai[ai.length - 1] != PARTITION_LENGTH) {\n            throw new RuntimeException(\"error,check your partitionScope definition.\");\n        }\n\n        // 数据映射操作\n        for (int i = 1; i < ai.length; i++) {\n            for (int j = ai[i - 1]; j < ai[i]; j++) {\n                segment[j] = (i - 1);\n            }\n        }\n    }\n\n    public int partition(long hash) {\n        return segment[(int) (hash & AND_VALUE)];\n    }\n\n    public int partition(String key, int start, int end) {\n        return partition(StringUtil.hash(key, start, end));\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/route/util/PropertiesUtil.java",
    "content": "package io.mycat.route.util;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Properties;\n\n/**\n * Property文件加载器\n *\n * @author Hash Zhang\n * @time 00:08:03 2016/5/3\n * @version 1.0\n */\npublic class PropertiesUtil {\n    public static Properties loadProps(String propsFile){\n        Properties props = new Properties();\n        InputStream inp = Thread.currentThread().getContextClassLoader().getResourceAsStream(propsFile);\n\n        if (inp == null) {\n            throw new java.lang.RuntimeException(\"time sequnce properties not found \" + propsFile);\n        }\n        try {\n            props.load(inp);\n        } catch (IOException e) {\n            throw new java.lang.RuntimeException(e);\n        }\n        return props;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/route/util/RouterUtil.java",
    "content": "package io.mycat.route.util;\r\n\r\nimport java.sql.SQLNonTransientException;\r\nimport java.sql.SQLSyntaxErrorException;\r\nimport java.util.ArrayList;\r\nimport java.util.Arrays;\r\nimport java.util.Collection;\r\nimport java.util.Collections;\r\nimport java.util.HashMap;\r\nimport java.util.HashSet;\r\nimport java.util.Iterator;\r\nimport java.util.LinkedHashSet;\r\nimport java.util.LinkedList;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.Set;\r\nimport java.util.concurrent.Callable;\r\n\r\nimport org.apache.commons.lang.StringUtils;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport com.alibaba.druid.sql.ast.SQLExpr;\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\nimport com.alibaba.druid.sql.ast.expr.SQLCharExpr;\r\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\r\nimport com.alibaba.druid.sql.ast.statement.SQLCharacterDataType;\r\nimport com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;\r\nimport com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;\r\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;\r\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\r\nimport com.alibaba.druid.wall.spi.WallVisitorUtils;\r\nimport com.google.common.base.Strings;\r\nimport com.google.common.collect.Maps;\r\nimport com.google.common.util.concurrent.FutureCallback;\r\nimport com.google.common.util.concurrent.Futures;\r\nimport com.google.common.util.concurrent.ListenableFuture;\r\n\r\nimport io.mycat.MycatServer;\r\nimport io.mycat.backend.datasource.PhysicalDBNode;\r\nimport io.mycat.backend.datasource.PhysicalDBPool;\r\nimport io.mycat.backend.datasource.PhysicalDatasource;\r\nimport io.mycat.backend.mysql.nio.handler.FetchStoreNodeOfChildTableHandler;\r\nimport io.mycat.backend.mysql.nio.handler.JDBCFetchStoreNodeOfChildTableHandler;\r\nimport io.mycat.cache.LayerCachePool;\r\nimport io.mycat.config.ErrorCode;\r\nimport io.mycat.config.MycatConfig;\r\nimport io.mycat.config.model.SchemaConfig;\r\nimport io.mycat.config.model.TableConfig;\r\nimport io.mycat.config.model.rule.RuleConfig;\r\nimport io.mycat.route.RouteResultset;\r\nimport io.mycat.route.RouteResultsetNode;\r\nimport io.mycat.route.SessionSQLPair;\r\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\r\nimport io.mycat.route.function.SlotFunction;\r\nimport io.mycat.route.parser.druid.DruidShardingParseInfo;\r\nimport io.mycat.route.parser.druid.RouteCalculateUnit;\r\nimport io.mycat.server.ServerConnection;\r\nimport io.mycat.server.parser.ServerParse;\r\nimport io.mycat.sqlengine.mpp.ColumnRoutePair;\r\nimport io.mycat.sqlengine.mpp.LoadData;\r\nimport io.mycat.util.StringUtil;\r\n\r\n/**\r\n * 从ServerRouterUtil中抽取的一些公用方法，路由解析工具类\r\n * @author wang.dw\r\n *\r\n */\r\npublic class RouterUtil {\r\n\r\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(RouterUtil.class);\r\n\r\n\t/**\r\n\t * 移除执行语句中的数据库名\r\n\t *\r\n\t * @param stmt\t\t 执行语句\r\n\t * @param schema  \t数据库名\r\n\t * @return \t\t\t执行语句\r\n\t * @author mycat\r\n\t *\r\n\t * @modification 修正移除schema的方法\r\n\t * @date 2016/12/29\r\n\t * @modifiedBy Hash Zhang\r\n\t *\r\n\t */\r\n\tpublic static String removeSchema(String stmt, String schema) {\r\n\t\tfinal String upStmt = stmt.toUpperCase();\r\n\t\tfinal String upSchema = schema.toUpperCase() + \".\";\r\n\t\tfinal String upSchema2 = new StringBuilder(\"`\").append(schema.toUpperCase()).append(\"`.\").toString();\r\n\t\tint strtPos = 0;\r\n\t\tint indx = 0;\r\n\r\n\t\tint indx1 = upStmt.indexOf(upSchema, strtPos);\r\n\t\tint indx2 = upStmt.indexOf(upSchema2, strtPos);\r\n\t\tboolean flag = indx1 < indx2 ? indx1 == -1 : indx2 != -1;\r\n\t\tindx = !flag ? indx1 > 0 ? indx1 : indx2 : indx2 > 0 ? indx2 : indx1;\r\n\t\tif (indx < 0) {\r\n\t\t\treturn stmt;\r\n\t\t}\r\n\r\n\t\tint firstE = upStmt.indexOf(\"'\");\r\n\t\tint endE = upStmt.lastIndexOf(\"'\");\r\n\r\n\t\tStringBuilder sb = new StringBuilder();\r\n\t\twhile (indx > 0) {\r\n\t\t\tsb.append(stmt.substring(strtPos, indx));\r\n\r\n\t\t\tif (flag) {\r\n\t\t\t\tstrtPos = indx + upSchema2.length();\r\n\t\t\t} else {\r\n\t\t\t\tstrtPos = indx + upSchema.length();\r\n\t\t\t}\r\n\t\t\tif (indx > firstE && indx < endE && countChar(stmt, indx) % 2 == 1) {\r\n\t\t\t\tsb.append(stmt.substring(indx, indx + schema.length() + 1));\r\n\t\t\t}\r\n\t\t\tindx1 = upStmt.indexOf(upSchema, strtPos);\r\n\t\t\tindx2 = upStmt.indexOf(upSchema2, strtPos);\r\n\t\t\tflag = indx1 < indx2 ? indx1 == -1 : indx2 != -1;\r\n\t\t\tindx = !flag ? indx1 > 0 ? indx1 : indx2 : indx2 > 0 ? indx2 : indx1;\r\n\t\t}\r\n\t\tsb.append(stmt.substring(strtPos));\r\n\t\treturn sb.toString();\r\n\t}\r\n\r\n\tprivate static int countChar(String sql,int end)\r\n\t{\r\n\t\tint count=0;\r\n\t\tboolean skipChar = false;\r\n\t\tfor (int i = 0; i < end; i++) {\r\n\t\t\tif(sql.charAt(i)=='\\'' && !skipChar) {\r\n\t\t\t\tcount++;\r\n\t\t\t\tskipChar = false;\r\n\t\t\t}else if( sql.charAt(i)=='\\\\'){\r\n\t\t\t\tskipChar = true;\r\n\t\t\t}else{\r\n\t\t\t\tskipChar = false;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn count;\r\n\t}\r\n\r\n\t/**\r\n\t * 获取第一个节点作为路由\r\n\t *\r\n\t * @param rrs\t\t          数据路由集合\r\n\t * @param dataNode  \t数据库所在节点\r\n\t * @param stmt   \t\t执行语句\r\n\t * @return \t\t\t\t数据路由集合\r\n\t *\r\n\t * @author mycat\r\n\t */\r\n\tpublic static RouteResultset routeToSingleNode(RouteResultset rrs,\r\n\t\t\tString dataNode, String stmt) {\r\n\t\tif (dataNode == null) {\r\n\t\t\treturn rrs;\r\n\t\t}\r\n\t\tRouteResultsetNode[] nodes = new RouteResultsetNode[1];\r\n\t\tnodes[0] = new RouteResultsetNode(dataNode, rrs.getSqlType(), stmt);//rrs.getStatement()\r\n\t\tnodes[0].setSource(rrs);\r\n\t\trrs.setNodes(nodes);\r\n\t\trrs.setFinishedRoute(true);\r\n\t\tif(rrs.getDataNodeSlotMap().containsKey(dataNode)){\r\n\t\t\tnodes[0].setSlot(rrs.getDataNodeSlotMap().get(dataNode));\r\n\t\t}\r\n\t\tif (rrs.getCanRunInReadDB() != null) {\r\n\t\t\tnodes[0].setCanRunInReadDB(rrs.getCanRunInReadDB());\r\n\t\t}\r\n\t\tif(rrs.getRunOnSlave() != null){\r\n\t\t\tnodes[0].setRunOnSlave(rrs.getRunOnSlave());\r\n\t\t}\r\n\r\n\t\treturn rrs;\r\n\t}\r\n\r\n\r\n\r\n\t/**\r\n\t * 修复DDL路由\r\n\t *\r\n\t * @return RouteResultset\r\n\t * @author aStoneGod\r\n\t */\r\n\tpublic static RouteResultset routeToDDLNode(RouteResultset rrs, int sqlType, String stmt,SchemaConfig schema) throws SQLSyntaxErrorException {\r\n\t\tstmt = getFixedSql(stmt);\r\n\t\tString tablename = \"\";\r\n\t\t//去除sql前面的注册,如/* ApplicationName=DBeaver 6.0.1 - Main */，这个注册会导致create table出错\r\n\t\tstmt = stmt.replaceFirst(\"\\\\/\\\\*.*\\\\*\\\\/\\\\s*\", \"\");\r\n\t\tfinal String upStmt = stmt.toUpperCase();\r\n\t\tString rrsStmt = new String(upStmt);\r\n\t\tif(upStmt.startsWith(\"CREATE\")){\r\n\t\t\tif (upStmt.contains(\"CREATE INDEX \") || upStmt.contains(\"CREATE UNIQUE INDEX \")){\r\n\t\t\t\ttablename = RouterUtil.getTableName(stmt, RouterUtil.getCreateIndexPos(upStmt, 0));\r\n\t\t\t\t/**\r\n\t\t\t\t * Date：2017年11月2日\r\n\t\t\t\t * @author SvenAugustus\r\n\t\t\t\t修复oracle 语法不支持 drop index i_t_f on t_test，只有drop index i_t_f;\r\n\t\t\t\t */\r\n\t\t\t\tif(!\"oracle\".equalsIgnoreCase(schema.getDefaultDataNodeDbType())){\r\n\t\t\t\t\tint onInd = upStmt.indexOf(\"ON\", 0);\r\n\t\t\t\t\trrsStmt= upStmt.substring(0, onInd);\r\n\t\t\t\t}\r\n\t\t\t}else {\r\n\t\t\t\ttablename = RouterUtil.getTableName(stmt, RouterUtil.getCreateTablePos(upStmt, 0));\r\n\t\t\t}\r\n\t\t}else if(upStmt.startsWith(\"DROP\")){\r\n\t\t\tif (upStmt.contains(\"DROP INDEX \")){\r\n\t\t\t\ttablename = RouterUtil.getTableName(stmt, RouterUtil.getDropIndexPos(upStmt, 0));\r\n\t\t\t}else {\r\n\t\t\t\ttablename = RouterUtil.getTableName(stmt, RouterUtil.getDropTablePos(upStmt, 0));\r\n\t\t\t}\r\n\t\t}else if(upStmt.startsWith(\"ALTER\")){\r\n\t\t\ttablename = RouterUtil.getTableName(stmt, RouterUtil.getAlterTablePos(upStmt, 0));\r\n\t\t}else if (upStmt.startsWith(\"TRUNCATE\")){\r\n\t\t\ttablename = RouterUtil.getTableName(stmt, RouterUtil.getTruncateTablePos(upStmt, 0));\r\n\t\t}\r\n\t\ttablename = tablename.toUpperCase();\r\n\r\n\t\tif (schema.getTables().containsKey(tablename)){\r\n\t\t\tif(ServerParse.DDL==sqlType){\r\n\t\t\t\tList<String> dataNodes = new ArrayList<>();\r\n\t\t\t\tMap<String, TableConfig> tables = schema.getTables();\r\n\t\t\t\tTableConfig tc=tables.get(tablename);\r\n\t\t\t\tif (tables != null && (tc  != null)) {\r\n\t\t\t\t\tdataNodes = tc.getDataNodes();\r\n\t\t\t\t}\r\n\t\t\t\tboolean isSlotFunction= tc.getRule() != null && tc.getRule().getRuleAlgorithm() instanceof SlotFunction;\r\n\t\t\t\tIterator<String> iterator1 = dataNodes.iterator();\r\n\t\t\t\tint nodeSize = dataNodes.size();\r\n\t\t\t\tRouteResultsetNode[] nodes = new RouteResultsetNode[nodeSize];\r\n\t\t\t\tif(isSlotFunction){\r\n\t\t\t\t\tstmt=changeCreateTable(schema,tablename,stmt);\r\n\t\t\t\t}\r\n\t\t\t\tfor(int i=0;i<nodeSize;i++){\r\n\t\t\t\t\tString name = iterator1.next();\r\n\t\t\t\t\tnodes[i] = new RouteResultsetNode(name, sqlType, stmt);\r\n\t\t\t\t\tnodes[i].setSource(rrs);\r\n\t\t\t\t\tif(rrs.getDataNodeSlotMap().containsKey(name)){\r\n\t\t\t\t\t\tnodes[i].setSlot(rrs.getDataNodeSlotMap().get(name));\r\n\t\t\t\t\t}  else if(isSlotFunction){\r\n\t\t\t\t\t\tnodes[i].setSlot(-1);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tnodes[i].setStatement(rrsStmt);\r\n\t\t\t\t}\r\n\t\t\t\trrs.setNodes(nodes);\r\n\t\t\t}\r\n\t\t\trrs.setStatement(rrsStmt);\r\n\t\t\treturn rrs;\r\n\t\t}else if(schema.getDataNode()!=null){\t\t//默认节点ddl\r\n\t\t\tRouteResultsetNode[] nodes = new RouteResultsetNode[1];\r\n\t\t\tnodes[0] = new RouteResultsetNode(schema.getDataNode(), sqlType, stmt);\r\n\t\t\tnodes[0].setSource(rrs);\r\n\t\t\trrs.setNodes(nodes);\r\n\t\t\treturn rrs;\r\n\t\t}\r\n\t\t//both tablename and defaultnode null\r\n\t\tLOGGER.error(\"table not in schema----\"+tablename);\r\n\t\tthrow new SQLSyntaxErrorException(\"op table not in schema----\"+tablename);\r\n\t}\r\n\r\n\tprivate  static String changeCreateTable(SchemaConfig schema,String tableName,String sql) {\r\n\t\tif (schema.getTables().containsKey(tableName)) {\r\n\t\t\tMySqlStatementParser parser = new MySqlStatementParser(sql);\r\n\t\t\tSQLStatement insertStatement = parser.parseStatement();\r\n\t\t\tif (insertStatement instanceof MySqlCreateTableStatement) {\r\n\t\t\t\tTableConfig tableConfig = schema.getTables().get(tableName);\r\n\t\t\t\tAbstractPartitionAlgorithm algorithm = tableConfig.getRule().getRuleAlgorithm();\r\n\t\t\t\tif (algorithm instanceof SlotFunction) {\r\n\t\t\t\t\tSQLColumnDefinition column = new SQLColumnDefinition();\r\n\t\t\t\t\tcolumn.setDataType(new SQLCharacterDataType(\"int\"));\r\n\t\t\t\t\tcolumn.setName(new SQLIdentifierExpr(\"_slot\"));\r\n\t\t\t\t\tcolumn.setComment(new SQLCharExpr(\"自动迁移算法slot,禁止修改\"));\r\n\t\t\t\t\t((SQLCreateTableStatement) insertStatement).getTableElementList().add(column);\r\n\t\t\t\t\treturn insertStatement.toString();\r\n\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t}\r\n\t\treturn sql;\r\n\t}\r\n\r\n\t/**\r\n\t * 处理SQL\r\n\t *\r\n\t * @param stmt   执行语句\r\n\t * @return \t\t 处理后SQL\r\n\t * @author AStoneGod\r\n\t */\r\n\tpublic static String getFixedSql(String stmt){\r\n\t\tstmt = stmt.replaceAll(\"\\r\\n\", \" \"); //对于\\r\\n的字符 用 空格处理 rainbow\r\n\t\treturn stmt = stmt.trim(); //.toUpperCase();\r\n\t}\r\n\r\n\t/**\r\n\t * 获取table名字\r\n\t *\r\n\t * @param stmt  \t执行语句\r\n\t * @param repPos\t开始位置和位数\r\n\t * @return 表名\r\n\t * @author AStoneGod\r\n\t */\r\n\tpublic static String getTableName(String stmt, int[] repPos) {\r\n\t\tint startPos = repPos[0];\r\n\t\tint secInd = stmt.indexOf(' ', startPos + 1);\r\n\t\tif (secInd < 0) {\r\n\t\t\tsecInd = stmt.length();\r\n\t\t}\r\n\t\tint thiInd = stmt.indexOf('(',secInd+1);\r\n\t\tif (thiInd < 0) {\r\n\t\t\tthiInd = stmt.length();\r\n\t\t}\r\n\t\trepPos[1] = secInd;\r\n\t\tString tableName = \"\";\r\n\t\tif (stmt.toUpperCase().startsWith(\"DESC\")||stmt.toUpperCase().startsWith(\"DESCRIBE\")){\r\n\t\t\ttableName = stmt.substring(startPos, thiInd).trim();\r\n\t\t}else {\r\n\t\t\ttableName = stmt.substring(secInd, thiInd).trim();\r\n\t\t}\r\n\r\n\t\t//ALTER TABLE\r\n\t\tif (tableName.contains(\" \")){\r\n\t\t\ttableName = tableName.substring(0,tableName.indexOf(\" \"));\r\n\t\t}\r\n\t\tint ind2 = tableName.indexOf('.');\r\n\t\tif (ind2 > 0) {\r\n\t\t\ttableName = tableName.substring(ind2 + 1);\r\n\t\t}\r\n\t\treturn tableName;\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * 获取show语句table名字\r\n\t *\r\n\t * @param stmt\t        执行语句\r\n\t * @param repPos   开始位置和位数\r\n\t * @return 表名\r\n\t * @author AStoneGod\r\n\t */\r\n\tpublic static String getShowTableName(SchemaConfig schema, String stmt, int[] repPos) {\r\n\t\tint startPos = repPos[0];\r\n        startPos = getStartPos(stmt, startPos);\r\n\t\tint secInd = stmt.indexOf(' ', startPos + 1);\r\n\t\tif (secInd < 0) {\r\n\t\t\tsecInd = stmt.length();\r\n\t\t}\r\n\r\n\t\trepPos[1] = secInd;\r\n\t\tString tableName = stmt.substring(startPos, secInd).trim();\r\n        if (!schema.isCheckSQLSchema()) {\r\n            return tableName;\r\n        }\r\n\r\n        int ind2 = tableName.indexOf('.');\r\n\t\tif (ind2 > 0) {\r\n\t\t\ttableName = tableName.substring(ind2 + 1);\r\n\t\t}\r\n\t\treturn tableName;\r\n\t}\r\n\r\n\t/**\r\n\t * 获取语句中前关键字位置和占位个数表名位置\r\n\t *\r\n\t * @param upStmt     执行语句\r\n\t * @param start      开始位置\r\n\t * @return int[]\t  关键字位置和占位个数\r\n\t *\r\n\t * @author mycat\r\n\t *\r\n\t * @modification 修改支持语句中包含“IF NOT EXISTS”的情况\r\n\t * @date 2016/12/8\r\n\t * @modifiedBy Hash Zhang\r\n\t */\r\n\tpublic static int[] getCreateTablePos(String upStmt, int start) {\r\n\t\tString token1 = \"CREATE \";\r\n\t\tString token2 = \" TABLE \";\r\n\t\tString token3 = \" EXISTS \";\r\n\t\tint createInd = upStmt.indexOf(token1, start);\r\n\t\tint tabInd1 = upStmt.indexOf(token2, start);\r\n\t\tint tabInd2 = upStmt.indexOf(token3, tabInd1);\r\n\t\t// 既包含CREATE又包含TABLE，且CREATE关键字在TABLE关键字之前\r\n\t\tif (createInd >= 0 && tabInd2 > 0 && tabInd2 > createInd) {\r\n\t\t\treturn new int[] { tabInd2, token3.length() };\r\n\t\t} else if(createInd >= 0 && tabInd1 > 0 && tabInd1 > createInd) {\r\n\t\t\treturn new int[] { tabInd1, token2.length() };\r\n\t\t} else {\r\n\t\t\treturn new int[] { -1, token2.length() };// 不满足条件时，只关注第一个返回值为-1，第二个任意\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 获取语句中前关键字位置和占位个数表名位置\r\n\t *\r\n\t * @param upStmt\r\n\t *            执行语句\r\n\t * @param start\r\n\t *            开始位置\r\n\t * @return int[]关键字位置和占位个数\r\n\t * @author aStoneGod\r\n\t */\r\n\tpublic static int[] getCreateIndexPos(String upStmt, int start) {\r\n\t\tString token1 = \"CREATE \";\r\n\t\tString token2 = \" INDEX \";\r\n\t\tString token3 = \" ON \";\r\n\t\tint createInd = upStmt.indexOf(token1, start);\r\n\t\tint idxInd = upStmt.indexOf(token2, start);\r\n\t\tint onInd = upStmt.indexOf(token3, start);\r\n\t\t// 既包含CREATE又包含INDEX，且CREATE关键字在INDEX关键字之前, 且包含ON...\r\n\t\tif (createInd >= 0 && idxInd > 0 && idxInd > createInd && onInd > 0 && onInd > idxInd) {\r\n\t\t\treturn new int[] {onInd , token3.length() };\r\n\t\t} else {\r\n\t\t\treturn new int[] { -1, token2.length() };// 不满足条件时，只关注第一个返回值为-1，第二个任意\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 获取ALTER语句中前关键字位置和占位个数表名位置\r\n\t *\r\n\t * @param upStmt   执行语句\r\n\t * @param start    开始位置\r\n\t * @return int[]   关键字位置和占位个数\r\n\t * @author aStoneGod\r\n\t */\r\n\tpublic static int[] getAlterTablePos(String upStmt, int start) {\r\n\t\tString token1 = \"ALTER \";\r\n\t\tString token2 = \" TABLE \";\r\n\t\tint createInd = upStmt.indexOf(token1, start);\r\n\t\tint tabInd = upStmt.indexOf(token2, start);\r\n\t\t// 既包含CREATE又包含TABLE，且CREATE关键字在TABLE关键字之前\r\n\t\tif (createInd >= 0 && tabInd > 0 && tabInd > createInd) {\r\n\t\t\treturn new int[] { tabInd, token2.length() };\r\n\t\t} else {\r\n\t\t\treturn new int[] { -1, token2.length() };// 不满足条件时，只关注第一个返回值为-1，第二个任意\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 获取DROP语句中前关键字位置和占位个数表名位置\r\n\t *\r\n\t * @param upStmt \t执行语句\r\n\t * @param start  \t开始位置\r\n\t * @return int[]\t关键字位置和占位个数\r\n\t * @author aStoneGod\r\n\t */\r\n\tpublic static int[] getDropTablePos(String upStmt, int start) {\r\n\t\t//增加 if exists判断\r\n\t\tif(upStmt.contains(\"EXISTS\")){\r\n\t\t\tString token1 = \"IF \";\r\n\t\t\tString token2 = \" EXISTS \";\r\n\t\t\tint ifInd = upStmt.indexOf(token1, start);\r\n\t\t\tint tabInd = upStmt.indexOf(token2, start);\r\n\t\t\tif (ifInd >= 0 && tabInd > 0 && tabInd > ifInd) {\r\n\t\t\t\treturn new int[] { tabInd, token2.length() };\r\n\t\t\t} else {\r\n\t\t\t\treturn new int[] { -1, token2.length() };// 不满足条件时，只关注第一个返回值为-1，第二个任意\r\n\t\t\t}\r\n\t\t}else {\r\n\t\t\tString token1 = \"DROP \";\r\n\t\t\tString token2 = \" TABLE \";\r\n\t\t\tint createInd = upStmt.indexOf(token1, start);\r\n\t\t\tint tabInd = upStmt.indexOf(token2, start);\r\n\r\n\t\t\tif (createInd >= 0 && tabInd > 0 && tabInd > createInd) {\r\n\t\t\t\treturn new int[] { tabInd, token2.length() };\r\n\t\t\t} else {\r\n\t\t\t\treturn new int[] { -1, token2.length() };// 不满足条件时，只关注第一个返回值为-1，第二个任意\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * 获取DROP语句中前关键字位置和占位个数表名位置\r\n\t *\r\n\t * @param upStmt\r\n\t *            执行语句\r\n\t * @param start\r\n\t *            开始位置\r\n\t * @return int[]关键字位置和占位个数\r\n\t * @author aStoneGod\r\n\t */\r\n\r\n\tpublic static int[] getDropIndexPos(String upStmt, int start) {\r\n\t\tString token1 = \"DROP \";\r\n\t\tString token2 = \" INDEX \";\r\n\t\tString token3 = \" ON \";\r\n\t\tint createInd = upStmt.indexOf(token1, start);\r\n\t\tint idxInd = upStmt.indexOf(token2, start);\r\n\t\tint onInd = upStmt.indexOf(token3, start);\r\n\t\t// 既包含CREATE又包含INDEX，且CREATE关键字在INDEX关键字之前, 且包含ON...\r\n\t\tif (createInd >= 0 && idxInd > 0 && idxInd > createInd && onInd > 0 && onInd > idxInd) {\r\n\t\t\treturn new int[] {onInd , token3.length() };\r\n\t\t} else {\r\n\t\t\treturn new int[] { -1, token2.length() };// 不满足条件时，只关注第一个返回值为-1，第二个任意\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 获取TRUNCATE语句中前关键字位置和占位个数表名位置\r\n\t *\r\n\t * @param upStmt    执行语句\r\n\t * @param start     开始位置\r\n\t * @return int[]\t关键字位置和占位个数\r\n\t * @author aStoneGod\r\n\t */\r\n\tpublic static int[] getTruncateTablePos(String upStmt, int start) {\r\n\t\tString token1 = \"TRUNCATE \";\r\n\t\tString token2 = \" TABLE \";\r\n\t\tint createInd = upStmt.indexOf(token1, start);\r\n\t\tint tabInd = upStmt.indexOf(token2, start);\r\n\t\t// 既包含CREATE又包含TABLE，且CREATE关键字在TABLE关键字之前\r\n\t\tif (createInd >= 0 && tabInd > 0 && tabInd > createInd) {\r\n\t\t\treturn new int[] { tabInd, token2.length() };\r\n\t\t} else {\r\n\t\t\treturn new int[] { -1, token2.length() };// 不满足条件时，只关注第一个返回值为-1，第二个任意\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 获取语句中前关键字位置和占位个数表名位置\r\n\t *\r\n\t * @param upStmt   执行语句\r\n\t * @param start    开始位置\r\n\t * @return int[]   关键字位置和占位个数\r\n\t * @author mycat\r\n\t */\r\n\tpublic static int[] getSpecPos(String upStmt, int start) {\r\n\t\tString token1 = \" FROM \";\r\n\t\tString token2 = \" IN \";\r\n\t\tint tabInd1 = upStmt.indexOf(token1, start);\r\n\t\tint tabInd2 = upStmt.indexOf(token2, start);\r\n\t\tif (tabInd1 > 0) {\r\n\t\t\tif (tabInd2 < 0) {\r\n\t\t\t\treturn new int[] { tabInd1, token1.length() };\r\n\t\t\t}\r\n\t\t\treturn (tabInd1 < tabInd2) ? new int[] { tabInd1, token1.length() }\r\n\t\t\t\t\t: new int[] { tabInd2, token2.length() };\r\n\t\t} else {\r\n\t\t\treturn new int[] { tabInd2, token2.length() };\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 获取开始位置后的 LIKE、WHERE 位置 如果不含 LIKE、WHERE 则返回执行语句的长度\r\n\t *\r\n\t * @param upStmt   执行sql\r\n\t * @param start    开始位置\r\n\t * @return int\r\n\t * @author mycat\r\n\t */\r\n\tpublic static int getSpecEndPos(String upStmt, int start) {\r\n\t\tint tabInd = upStmt.toUpperCase().indexOf(\" LIKE \", start);\r\n\t\tif (tabInd < 0) {\r\n\t\t\ttabInd = upStmt.toUpperCase().indexOf(\" WHERE \", start);\r\n\t\t}\r\n\t\tif (tabInd < 0) {\r\n\t\t\treturn upStmt.length();\r\n\t\t}\r\n\t\treturn tabInd;\r\n\t}\r\n\r\n\tpublic static boolean processWithMycatSeq(SchemaConfig schema, int sqlType,\r\n\t\t\tString origSQL, ServerConnection sc) {\r\n\t\t// check if origSQL is with global sequence\r\n\t\t// @micmiu it is just a simple judgement\r\n\t\t//对应本地文件配置方式：insert into table1(id,name) values(next value for MYCATSEQ_GLOBAL,‘test’);\r\n\t\t// edit by dingw,增加mycatseq_ 兼容，因为ServerConnection的373行，进行路由计算时，将原始语句全部转换为小写\r\n\t\tif (origSQL.indexOf(\" MYCATSEQ_\") != -1 || origSQL.indexOf(\"mycatseq_\") != -1) {\r\n\t\t\tprocessSQL(sc,schema,origSQL,sqlType);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\tpublic static void processSQL(ServerConnection sc,SchemaConfig schema,String sql,int sqlType){\r\n//\t\tint sequenceHandlerType = MycatServer.getInstance().getConfig().getSystem().getSequenceHandlerType();\r\n\t\tfinal SessionSQLPair sessionSQLPair = new SessionSQLPair(sc.getSession2(), schema, sql, sqlType);\r\n//      modify by yanjunli  序列获取修改为多线程方式。使用分段锁方式,一个序列一把锁。  begin\r\n//\t\tMycatServer.getInstance().getSequnceProcessor().addNewSql(sessionSQLPair);\r\n\t\tMycatServer.getInstance().getSequenceExecutor().execute(new Runnable() {\r\n\t\t\t@Override\r\n\t\t\tpublic void run() {\r\n\t\t\t\tMycatServer.getInstance().getSequnceProcessor().executeSeq(sessionSQLPair);\r\n\t\t\t}\r\n\t\t});\r\n//      modify   序列获取修改为多线程方式。使用分段锁方式,一个序列一把锁。  end\r\n//\t\t}\r\n\t}\r\n\r\n\tpublic static boolean processInsert(SchemaConfig schema, int sqlType,\r\n\t\t\tString origSQL, ServerConnection sc) throws SQLNonTransientException {\r\n\t\tString tableName = StringUtil.getTableName(origSQL).toUpperCase();\r\n\t\tTableConfig tableConfig = schema.getTables().get(tableName);\r\n\t\tboolean processedInsert=false;\r\n\t\t//判断是有自增字段\r\n\t\tif (null != tableConfig && tableConfig.isAutoIncrement()) {\r\n\t\t\tString primaryKey = tableConfig.getPrimaryKey();\r\n\t\t\tprocessedInsert=processInsert(sc,schema,sqlType,origSQL,tableName,primaryKey);\r\n\t\t}\r\n\t\treturn processedInsert;\r\n\t}\r\n\t/*\r\n\t *  找到返回主键的的位置\r\n\t *  找不到返回 -1\r\n\t * */\r\n\tprivate static int isPKInFields(String origSQL,String primaryKey,int firstLeftBracketIndex,int firstRightBracketIndex){\r\n\r\n\t\tif (primaryKey == null) {\r\n\t\t\tthrow new RuntimeException(\"please make sure the primaryKey's config is not null in schemal.xml\");\r\n\t\t}\r\n\r\n\t\tboolean isPrimaryKeyInFields = false;\r\n\t\tint  pkStart = 0;\r\n\t\tString upperSQL = origSQL.substring(firstLeftBracketIndex, firstRightBracketIndex + 1).toUpperCase();\r\n\t\tfor (int pkOffset = 0, primaryKeyLength = primaryKey.length();;) {\r\n\t\t\tpkStart = upperSQL.indexOf(primaryKey, pkOffset);\r\n\t\t\tif (pkStart >= 0 && pkStart < firstRightBracketIndex) {\r\n\t\t\t\tchar pkSide = upperSQL.charAt(pkStart - 1);\r\n\t\t\t\tif (pkSide <= ' ' || pkSide == '`' || pkSide == ',' || pkSide == '(') {\r\n\t\t\t\t\tpkSide = upperSQL.charAt(pkStart + primaryKey.length());\r\n\t\t\t\t\tisPrimaryKeyInFields = pkSide <= ' ' || pkSide == '`' || pkSide == ',' || pkSide == ')';\r\n\t\t\t\t}\r\n\t\t\t\tif (isPrimaryKeyInFields) {\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tpkOffset = pkStart + primaryKeyLength;\r\n\t\t\t} else {\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (isPrimaryKeyInFields) {\r\n\t\t\treturn firstLeftBracketIndex + pkStart;\r\n\t\t} else {\r\n\t\t\treturn  -1;\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tpublic static boolean processInsert(ServerConnection sc,SchemaConfig schema,\r\n\t\t\tint sqlType,String origSQL,String tableName,String primaryKey) throws SQLNonTransientException {\r\n\r\n\t\tint firstLeftBracketIndex = origSQL.indexOf(\"(\");\r\n\t\tint firstRightBracketIndex = origSQL.indexOf(\")\");\r\n\t\tString upperSql = origSQL.toUpperCase();\r\n\t\tint valuesIndex = upperSql.indexOf(\"VALUES\");\r\n\t\tint selectIndex = upperSql.indexOf(\"SELECT\");\r\n\t\tint fromIndex = upperSql.indexOf(\"FROM\");\r\n\t\t//屏蔽insert into table1 select * from table2语句\r\n\t\tif(firstLeftBracketIndex < 0) {\r\n\t\t\tString msg = \"invalid sql:\" + origSQL;\r\n\t\t\tLOGGER.warn(msg);\r\n\t\t\tthrow new SQLNonTransientException(msg);\r\n\t\t}\r\n\t\t//屏蔽批量插入\r\n\t\tif(selectIndex > 0 &&fromIndex>0&&selectIndex>firstRightBracketIndex&&valuesIndex<0) {\r\n\t\t\tString msg = \"multi insert not provided\" ;\r\n\t\t\tLOGGER.warn(msg);\r\n\t\t\tthrow new SQLNonTransientException(msg);\r\n\t\t}\r\n\t\t//插入语句必须提供列结构，因为MyCat默认对于表结构无感知\r\n\t\tif(valuesIndex + \"VALUES\".length() <= firstLeftBracketIndex) {\r\n\t\t\tthrow new SQLSyntaxErrorException(\"insert must provide ColumnList\");\r\n\t\t}\r\n\t\tObject[] vauleArrayAndSuffixStr = parseSqlValueArrayAndSuffixStr(origSQL , valuesIndex);\r\n\t\tList<List<String>> vauleArray = (List<List<String>>) vauleArrayAndSuffixStr[0];\r\n\t\tString suffixStr = null;\r\n\t\tif (vauleArrayAndSuffixStr.length > 1) {\r\n\t\t\tsuffixStr = (String) vauleArrayAndSuffixStr[1];\r\n\t\t}\r\n\t\t//两种情况处理 1 有主键的 id ,但是值为null 进行改下\r\n\t\t//            2 没有主键的 需要插入 进行改写\r\n\r\n\t\t//如果主键不在插入语句的fields中，则需要进一步处理\r\n\t\tboolean processedInsert= false;\r\n\t\tint pkStart = isPKInFields(origSQL,primaryKey,firstLeftBracketIndex,firstRightBracketIndex);\r\n\r\n\r\n\t\tif(pkStart == -1){\r\n\t\t\tprocessedInsert = true;\r\n\t\t\thandleBatchInsert(sc, schema, sqlType,origSQL, valuesIndex, tableName, primaryKey, vauleArray, suffixStr);\r\n\t\t} else {\r\n\t\t\t//判断 主键id的值是否为null\r\n\t\t\tif(pkStart != -1) {\r\n\t\t\t\tString subPrefix = origSQL.substring(0, pkStart);\r\n\t\t\t\tchar c;\r\n\t\t\t\tint pkIndex = 0;\r\n\t\t\t\tfor(int index = 0, len = subPrefix.length(); index < len; index++) {\r\n\t\t\t\t\tc = subPrefix.charAt(index);\r\n\t\t\t\t\tif(c == ',') {\r\n\t\t\t\t\t\tpkIndex ++;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tprocessedInsert  = handleBatchInsertWithPK(sc, schema, sqlType,origSQL, valuesIndex, tableName, primaryKey, vauleArray, suffixStr, pkIndex);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn processedInsert;\r\n\t}\r\n\r\n\tprivate static boolean handleBatchInsertWithPK(ServerConnection sc, SchemaConfig schema, int sqlType,\r\n\t\t\tString origSQL, int valuesIndex, String tableName, String primaryKey, List<List<String>> vauleList,\r\n\t\t\tString suffixStr, int pkIndex) {\r\n\t\tboolean processedInsert = false;\r\n//\t  \tfinal String pk = \"\\\\(\"+primaryKey+\",\";\r\n\t\tfinal String mycatSeqPrefix = \"next value for MYCATSEQ_\"+tableName.toUpperCase() ;\r\n\r\n\t\t/*\"VALUES\".length() ==6 */\r\n\t\tString prefix = origSQL.substring(0, valuesIndex + 6);\r\n//\r\n\r\n\t\tStringBuilder sb = new StringBuilder(\"\");\r\n\t\tfor(List<String> list : vauleList) {\r\n\t\t\tsb.append(\"(\");\r\n\t\t\tString pkValue = list.get(pkIndex).trim().toLowerCase();\r\n\t\t\t//null值替换为 next value for MYCATSEQ_tableName\r\n\t\t\tif(\"null\".equals(pkValue.trim())) {\r\n\t\t\t\tlist.set(pkIndex, mycatSeqPrefix);\r\n\t\t\t\tprocessedInsert = true;\r\n\t\t\t}\r\n\t\t\tfor(String val : list) {\r\n\t\t\t\tsb.append(val).append(\",\");\r\n\t\t\t}\r\n\t\t\tsb.setCharAt(sb.length() - 1, ')');\r\n\t\t\tsb.append(\",\");\r\n\t\t}\r\n\t\tsb.setCharAt(sb.length() - 1, ' ');\r\n\t\tif (suffixStr != null) {\r\n\t\t\tsb.append(suffixStr);\r\n\t\t}\r\n\t\tif(processedInsert) {\r\n\t\t\tprocessSQL(sc, schema,prefix+sb.toString(), sqlType);\r\n\t\t}\r\n\t\treturn processedInsert;\r\n\t}\r\n\r\n\tpublic static List<String> handleBatchInsert(String origSQL, int valuesIndex) {\r\n\t\tList<String> handledSQLs = new LinkedList<>();\r\n\t\tString prefix = origSQL.substring(0, valuesIndex + \"VALUES\".length());\r\n\t\tString values = origSQL.substring(valuesIndex + \"VALUES\".length());\r\n\t\tint flag = 0;\r\n\t\tStringBuilder currentValue = new StringBuilder();\r\n\t\tcurrentValue.append(prefix);\r\n\t\tfor (int i = 0; i < values.length(); i++) {\r\n\t\t\tchar j = values.charAt(i);\r\n\t\t\tif (j == '(' && flag == 0) {\r\n\t\t\t\tflag = 1;\r\n\t\t\t\tcurrentValue.append(j);\r\n\t\t\t} else if (j == '\\\"' && flag == 1) {\r\n\t\t\t\tflag = 2;\r\n\t\t\t\tcurrentValue.append(j);\r\n\t\t\t} else if (j == '\\'' && flag == 1) {\r\n\t\t\t\tflag = 3;\r\n\t\t\t\tcurrentValue.append(j);\r\n\t\t\t} else if (j == '\\\\' && flag == 2) {\r\n\t\t\t\tflag = 4;\r\n\t\t\t\tcurrentValue.append(j);\r\n\t\t\t} else if (j == '\\\\' && flag == 3) {\r\n\t\t\t\tflag = 5;\r\n\t\t\t\tcurrentValue.append(j);\r\n\t\t\t} else if (flag == 4) {\r\n\t\t\t\tflag = 2;\r\n\t\t\t\tcurrentValue.append(j);\r\n\t\t\t} else if (flag == 5) {\r\n\t\t\t\tflag = 3;\r\n\t\t\t\tcurrentValue.append(j);\r\n\t\t\t} else if (j == '\\\"' && flag == 2) {\r\n\t\t\t\tflag = 1;\r\n\t\t\t\tcurrentValue.append(j);\r\n\t\t\t} else if (j == '\\'' && flag == 3) {\r\n\t\t\t\tflag = 1;\r\n\t\t\t\tcurrentValue.append(j);\r\n\t\t\t} else if (j == ')' && flag == 1) {\r\n\t\t\t\tflag = 0;\r\n\t\t\t\tcurrentValue.append(j);\r\n\t\t\t\thandledSQLs.add(currentValue.toString());\r\n\t\t\t\tcurrentValue = new StringBuilder();\r\n\t\t\t\tcurrentValue.append(prefix);\r\n\t\t\t} else if (j == ',' && flag == 0) {\r\n\t\t\t\tcontinue;\r\n\t\t\t} else {\r\n\t\t\t\tcurrentValue.append(j);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn handledSQLs;\r\n\t}\r\n\t/**\r\n\t * 对于插入的sql : \"insert into hotnews(title,name) values('test1',\\\"name\\\"),('(test)',\\\"(test)\\\"),('\\\\\\\"',\\\"\\\\'\\\"),(\\\")\\\",\\\"\\\\\\\"\\\\')\\\")\"：\r\n\t *  需要返回结果：\r\n\t *[[ 'test1', \"name\"],\r\n\t *\t['(test)', \"(test)\"],\r\n\t *\t['\\\"', \"\\'\"],\r\n\t *\t[\")\", \"\\\"\\')\"],\r\n\t *\t[ 1,  null]\r\n\t * 值结果的解析\r\n\t */\r\n\tpublic static Object[] parseSqlValueArrayAndSuffixStr(String origSQL, int valuesIndex) {\r\n\t\tList<List<String>> valueArray = new ArrayList<>();\r\n\t\tString valuesAndSuffixStr = origSQL.substring(valuesIndex + 6);// 6 values 长度为6\r\n\t\tint pos = 0 ;\r\n\t\tint flag  = 4;\r\n\t\tint len = valuesAndSuffixStr.length();\r\n\t\tStringBuilder currentValue = new StringBuilder();\r\n//        int colNum = 2; //\r\n\t\tchar c ;\r\n\t\tList<String> curList = new ArrayList<>();\r\n\t\tint parenCount = 0;\r\n\t\tfor( ;pos < len; pos ++) {\r\n\t\t\tc = valuesAndSuffixStr.charAt(pos);\r\n\t\t\tif (flag == 1  || flag == 2) {\r\n\t\t\t\tcurrentValue.append(c);\r\n\t\t\t\tif (c == '\\\\') {\r\n\t\t\t\t\tchar nextCode = valuesAndSuffixStr.charAt(pos + 1);\r\n\t\t\t\t\tif (nextCode == '\\'' || nextCode == '\\\"') {\r\n\t\t\t\t\t\tcurrentValue.append(nextCode);\r\n\t\t\t\t\t\tpos++;\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (c == '\\\"' && flag == 1) {\r\n\t\t\t\t\tflag = 0;\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\tif (c == '\\'' && flag == 2) {\r\n\t\t\t\t\tflag = 0;\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t} else if (flag == 5) {\r\n\t\t\t\tcurrentValue.append(c);\r\n\t\t\t\tif (c == '(') {\r\n\t\t\t\t\tparenCount++;\r\n\t\t\t\t} else if (c == ')') {\r\n\t\t\t\t\tparenCount--;\r\n\t\t\t\t}\r\n\t\t\t\tif (parenCount == 0) {\r\n\t\t\t\t\tflag = 0;\r\n\t\t\t\t}\r\n\t\t\t} else if (c == '\\\"'){\r\n\t\t\t\tcurrentValue.append(c);\r\n\t\t\t\tflag = 1;\r\n\t\t\t} else if (c == '\\'') {\r\n\t\t\t\tcurrentValue.append(c);\r\n\t\t\t\tflag = 2;\r\n\t\t\t} else if (c == '(') {\r\n\t\t\t\tif (flag == 4) {\r\n\t\t\t\t\tcurList = new ArrayList<>();\r\n\t\t\t\t\tflag = 0;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tcurrentValue.append(c);\r\n\t\t\t\t\tflag = 5;\r\n\t\t\t\t\tparenCount++;\r\n\t\t\t\t}\r\n\t\t\t} else if (flag == 4) {\r\n\t\t\t\tif (c == 'o' || c == 'O') {\r\n\t\t\t\t\tString suffixStr = valuesAndSuffixStr.substring(pos);\r\n\t\t\t\t\treturn new Object[]{valueArray, suffixStr};\r\n\t\t\t\t}\r\n\t\t\t\tcontinue;\r\n\t\t\t} else if (c == ',') {\r\n//                System.out.println(currentValue);\r\n\t\t\t\tcurList.add(currentValue.toString());\r\n\t\t\t\tcurrentValue.delete(0, currentValue.length());\r\n\t\t\t} else if (c == ')'){\r\n\t\t\t\tflag = 4;\r\n//                System.out.println(currentValue);\r\n\t\t\t\tcurList.add(currentValue.toString());\r\n\t\t\t\tcurrentValue.delete(0, currentValue.length());\r\n\t\t\t\tvalueArray.add(curList);\r\n\t\t\t}  else {\r\n\t\t\t\tcurrentValue.append(c);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn new Object[]{valueArray};\r\n\t}\r\n\t/**\r\n\t * 对于主键不在插入语句的fields中的SQL，需要改写。比如hotnews主键为id，插入语句为：\r\n\t * insert into hotnews(title) values('aaa');\r\n\t * 需要改写成：\r\n\t * insert into hotnews(id, title) values(next value for MYCATSEQ_hotnews,'aaa');\r\n\t */\r\n\tpublic static void handleBatchInsert(ServerConnection sc, SchemaConfig schema,\r\n\t\t\tint sqlType,String origSQL, int valuesIndex,String tableName, String primaryKey , List<List<String>> vauleList, String suffixStr) {\r\n\r\n\t\tfinal String pk = \"\\\\(\"+primaryKey+\",\";\r\n\t\tfinal String mycatSeqPrefix = \"(next value for MYCATSEQ_\"+tableName.toUpperCase()+\"\";\r\n\r\n\t\t/*\"VALUES\".length() ==6 */\r\n\t\tString prefix = origSQL.substring(0, valuesIndex + 6);\r\n//\r\n\t\tprefix = prefix.replaceFirst(\"\\\\(\", pk);\r\n\r\n\t\tStringBuilder sb = new StringBuilder(\"\");\r\n\t\tfor(List<String> list : vauleList) {\r\n\t\t\tsb.append(mycatSeqPrefix);\r\n\t\t\tfor(String val : list) {\r\n\t\t\t\tsb.append(\",\").append(val);\r\n\t\t\t}\r\n\t\t\tsb.append(\"),\");\r\n\t\t}\r\n\t\tsb.setCharAt(sb.length() - 1, ' ');\r\n\t\tif (suffixStr != null) {\r\n\t\t\tsb.append(suffixStr);\r\n\t\t}\r\n\t\tprocessSQL(sc, schema,prefix+sb.toString(), sqlType);\r\n\t}\r\n//\t  /**\r\n//\t  * 对于主键不在插入语句的fields中的SQL，需要改写。比如hotnews主键为id，插入语句为：\r\n//\t  * insert into hotnews(title) values('aaa');\r\n//\t  * 需要改写成：\r\n//\t  * insert into hotnews(id, title) values(next value for MYCATSEQ_hotnews,'aaa');\r\n//\t  */\r\n//    public static void handleBatchInsert(ServerConnection sc, SchemaConfig schema,\r\n//            int sqlType,String origSQL, int valuesIndex,String tableName, String primaryKey) {\r\n//\r\n//    \tfinal String pk = \"\\\\(\"+primaryKey+\",\";\r\n//        final String mycatSeqPrefix = \"(next value for MYCATSEQ_\"+tableName.toUpperCase()+\",\";\r\n//\r\n//    \t/*\"VALUES\".length() ==6 */\r\n//        String prefix = origSQL.substring(0, valuesIndex + 6);\r\n//        String values = origSQL.substring(valuesIndex + 6);\r\n//\r\n//        prefix = prefix.replaceFirst(\"\\\\(\", pk);\r\n//        values = values.replaceFirst(\"\\\\(\", mycatSeqPrefix);\r\n//        values =Pattern.compile(\",\\\\s*\\\\(\").matcher(values).replaceAll(\",\"+mycatSeqPrefix);\r\n//        processSQL(sc, schema,prefix+values, sqlType);\r\n//    }\r\n\r\n\r\n\tpublic static RouteResultset routeToMultiNode(boolean cache,RouteResultset rrs, Collection<String> dataNodes, String stmt) {\r\n\t\tRouteResultsetNode[] nodes = new RouteResultsetNode[dataNodes.size()];\r\n\t\tint i = 0;\r\n\t\tRouteResultsetNode node;\r\n\t\tfor (String dataNode : dataNodes) {\r\n\t\t\tnode = new RouteResultsetNode(dataNode, rrs.getSqlType(), stmt);\r\n\t\t\tnode.setSource(rrs);\r\n\t\t\tif(rrs.getDataNodeSlotMap().containsKey(dataNode)){\r\n\t\t\t\tnode.setSlot(rrs.getDataNodeSlotMap().get(dataNode));\r\n\t\t\t}\r\n\t\t\tif (rrs.getCanRunInReadDB() != null) {\r\n\t\t\t\tnode.setCanRunInReadDB(rrs.getCanRunInReadDB());\r\n\t\t\t}\r\n\t\t\tif(rrs.getRunOnSlave() != null){\r\n\t\t\t\tnodes[0].setRunOnSlave(rrs.getRunOnSlave());\r\n\t\t\t}\r\n\t\t\tnodes[i++] = node;\r\n\t\t}\r\n\t\trrs.setCacheAble(cache);\r\n\t\trrs.setNodes(nodes);\r\n\t\treturn rrs;\r\n\t}\r\n\r\n\tpublic static RouteResultset routeToMultiNode(boolean cache, RouteResultset rrs, Collection<String> dataNodes,\r\n\t\t\tString stmt, boolean isGlobalTable) {\r\n\r\n\t\trrs = routeToMultiNode(cache, rrs, dataNodes, stmt);\r\n\t\trrs.setGlobalTable(isGlobalTable);\r\n\t\treturn rrs;\r\n\t}\r\n\r\n\tpublic static void routeForTableMeta(RouteResultset rrs,\r\n\t\t\tSchemaConfig schema, String tableName, String sql) {\r\n\t\tString dataNode = null;\r\n\t\tif (isNoSharding(schema,tableName)) {//不分库的直接从schema中获取dataNode\r\n\t\t\tdataNode = schema.getDataNode();\r\n\t\t} else {\r\n\t\t\tdataNode = getMetaReadDataNode(schema, tableName);\r\n\t\t}\r\n\r\n\t\tRouteResultsetNode[] nodes = new RouteResultsetNode[1];\r\n\t\tnodes[0] = new RouteResultsetNode(dataNode, rrs.getSqlType(), sql);\r\n\t\tnodes[0].setSource(rrs);\r\n\t\tif(rrs.getDataNodeSlotMap().containsKey(dataNode)){\r\n\t\t\tnodes[0].setSlot(rrs.getDataNodeSlotMap().get(dataNode));\r\n\t\t}\r\n\t\tif (rrs.getCanRunInReadDB() != null) {\r\n\t\t\tnodes[0].setCanRunInReadDB(rrs.getCanRunInReadDB());\r\n\t\t}\r\n\t\tif(rrs.getRunOnSlave() != null){\r\n\t\t\tnodes[0].setRunOnSlave(rrs.getRunOnSlave());\r\n\t\t}\r\n\t\trrs.setNodes(nodes);\r\n\t}\r\n\r\n\t/**\r\n\t * 根据表名随机获取一个节点\r\n\t *\r\n\t * @param schema     数据库名\r\n\t * @param table      表名\r\n\t * @return \t\t\t  数据节点\r\n\t * @author mycat\r\n\t */\r\n\tprivate static String getMetaReadDataNode(SchemaConfig schema,\r\n\t\t\tString table) {\r\n\t\t// Table名字被转化为大写的，存储在schema\r\n\t\ttable = table.toUpperCase();\r\n\t\tString dataNode = null;\r\n\t\tMap<String, TableConfig> tables = schema.getTables();\r\n\t\tTableConfig tc;\r\n\t\tif (tables != null && (tc = tables.get(table)) != null) {\r\n\t\t\tdataNode = getAliveRandomDataNode(tc);\r\n\t\t}\r\n\t\treturn dataNode;\r\n\t}\r\n\r\n\t/**\r\n\t * 解决getRandomDataNode方法获取错误节点的问题.\r\n\t * @param tc\r\n\t * @return\r\n\t */\r\n\tpublic static String getAliveRandomDataNode(TableConfig tc) {\r\n\t\tList<String> randomDns = (List<String>)tc.getDataNodes().clone();\r\n\r\n\t\tMycatConfig mycatConfig = MycatServer.getInstance().getConfig();\r\n\t\tif (mycatConfig != null) {\r\n\t\t\tCollections.shuffle(randomDns);\r\n\t\t\tfor (String randomDn : randomDns) {\r\n\t\t\t\tPhysicalDBNode physicalDBNode = mycatConfig.getDataNodes().get(randomDn);\r\n\t\t\t\tif (physicalDBNode != null) {\r\n\t\t\t\t\tif (physicalDBNode.getDbPool().getSource().isAlive()) {\r\n\t\t\t\t\t\tfor (PhysicalDBPool pool : MycatServer.getInstance().getConfig().getDataHosts().values()) {\r\n\t\t\t\t\t\t\tPhysicalDatasource source = pool.getSource();\r\n\t\t\t\t\t\t\tif (source.getHostConfig().containDataNode(randomDn) && pool.getSource().isAlive()) {\r\n\t\t\t\t\t\t\t\treturn randomDn;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// all fail return default\r\n\t\treturn tc.getRandomDataNode();\r\n\t}\r\n\r\n\t@Deprecated\r\n\tprivate static String getRandomDataNode(TableConfig tc) {\r\n\t\t//写节点不可用，意味着读节点也不可用。\r\n\t\t//直接使用下一个 dataHost\r\n\t\tString randomDn = tc.getRandomDataNode();\r\n\t\tMycatConfig mycatConfig = MycatServer.getInstance().getConfig();\r\n\t\tif (mycatConfig != null) {\r\n\t\t\tPhysicalDBNode physicalDBNode = mycatConfig.getDataNodes().get(randomDn);\r\n\t\t\tif (physicalDBNode != null) {\r\n\t\t\t\tif (physicalDBNode.getDbPool().getSource().isAlive()) {\r\n\t\t\t\t\tfor (PhysicalDBPool pool : MycatServer.getInstance()\r\n\t\t\t\t\t\t\t.getConfig()\r\n\t\t\t\t\t\t\t.getDataHosts()\r\n\t\t\t\t\t\t\t.values()) {\r\n\t\t\t\t\t\tif (pool.getSource().getHostConfig().containDataNode(randomDn)) {\r\n\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tif (pool.getSource().isAlive()) {\r\n\t\t\t\t\t\t\treturn pool.getSource().getHostConfig().getRandomDataNode();\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t//all fail return default\r\n\t\treturn randomDn;\r\n\t}\r\n\r\n\t/**\r\n\t * 根据 ER分片规则获取路由集合\r\n\t *\r\n\t * @param stmt            执行的语句\r\n\t * @param rrs      \t\t     数据路由集合\r\n\t * @param tc\t      \t     表实体\r\n\t * @param joinKeyVal      连接属性\r\n\t * @return RouteResultset(数据路由集合)\t *\r\n\t * @throws SQLNonTransientException，IllegalShardingColumnValueException\r\n\t * @author mycat\r\n\t */\r\n\r\n\tpublic static RouteResultset routeByERParentKey(ServerConnection sc,SchemaConfig schema,\r\n\t\t\tint sqlType,String stmt,\r\n\t\t\tRouteResultset rrs, TableConfig tc, String joinKeyVal)\r\n\t\t\tthrows SQLNonTransientException {\r\n\r\n\t\t// only has one parent level and ER parent key is parent\r\n\t\t// table's partition key\r\n\t\tif (tc.isSecondLevel()\r\n\t\t\t\t//判断是否为二级子表（父表不再有父表）\r\n\t\t\t\t&& tc.getParentTC().getPartitionColumn()\r\n\t\t\t\t.equals(tc.getParentKey())) { // using\r\n\t\t\t// parent\r\n\t\t\t// rule to\r\n\t\t\t// find\r\n\t\t\t// datanode\r\n\t\t\tSet<ColumnRoutePair> parentColVal = new HashSet<ColumnRoutePair>(1);\r\n\t\t\tColumnRoutePair pair = new ColumnRoutePair(joinKeyVal);\r\n\t\t\tparentColVal.add(pair);\r\n\t\t\tSet<String> dataNodeSet = ruleCalculate(tc.getParentTC(), parentColVal,rrs.getDataNodeSlotMap());\r\n\t\t\tif (dataNodeSet.isEmpty() || dataNodeSet.size() > 1) {\r\n\t\t\t\tthrow new SQLNonTransientException(\r\n\t\t\t\t\t\t\"parent key can't find  valid datanode ,expect 1 but found: \"\r\n\t\t\t\t\t\t\t\t+ dataNodeSet.size());\r\n\t\t\t}\r\n\t\t\tString dn = dataNodeSet.iterator().next();\r\n\t\t\tif (LOGGER.isDebugEnabled()) {\r\n\t\t\t\tLOGGER.debug(\"found partion node (using parent partion rule directly) for child table to insert  \"\r\n\t\t\t\t\t\t+ dn + \" sql :\" + stmt);\r\n\t\t\t}\r\n\t\t\treturn RouterUtil.routeToSingleNode(rrs, dn, stmt);\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/**\r\n\t * @return dataNodeIndex -&gt; [partitionKeysValueTuple+]\r\n\t */\r\n\tpublic static Set<String> ruleByJoinValueCalculate(RouteResultset rrs, TableConfig tc,\r\n\t\t\tSet<ColumnRoutePair> colRoutePairSet) throws SQLNonTransientException {\r\n\r\n\t\tString joinValue = \"\";\r\n\r\n\t\tif(colRoutePairSet.size() > 1) {\r\n\t\t\tLOGGER.warn(\"joinKey can't have multi Value\");\r\n\t\t} else {\r\n\t\t\tIterator<ColumnRoutePair> it = colRoutePairSet.iterator();\r\n\t\t\tColumnRoutePair joinCol = it.next();\r\n\t\t\tjoinValue = joinCol.colValue;\r\n\t\t}\r\n\r\n\t\tSet<String> retNodeSet = new LinkedHashSet<String>();\r\n\r\n\t\tSet<String> nodeSet;\r\n\t\tif (tc.isSecondLevel()\r\n\t\t\t\t&& tc.getParentTC().getPartitionColumn()\r\n\t\t\t\t.equals(tc.getParentKey())) { // using\r\n\t\t\t// parent\r\n\t\t\t// rule to\r\n\t\t\t// find\r\n\t\t\t// datanode\r\n\r\n\t\t\tnodeSet = ruleCalculate(tc.getParentTC(),colRoutePairSet,rrs.getDataNodeSlotMap());\r\n\t\t\tif (nodeSet.isEmpty()) {\r\n\t\t\t\tthrow new SQLNonTransientException(\r\n\t\t\t\t\t\t\"parent key can't find  valid datanode ,expect 1 but found: \"\r\n\t\t\t\t\t\t\t\t+ nodeSet.size());\r\n\t\t\t}\r\n\t\t\tif (LOGGER.isDebugEnabled()) {\r\n\t\t\t\tLOGGER.debug(\"found partion node (using parent partion rule directly) for child table to insert  \"\r\n\t\t\t\t\t\t+ nodeSet + \" sql :\" + rrs.getStatement());\r\n\t\t\t}\r\n\t\t\tretNodeSet.addAll(nodeSet);\r\n\r\n//\t\t\tfor(ColumnRoutePair pair : colRoutePairSet) {\r\n//\t\t\t\tnodeSet = ruleCalculate(tc.getParentTC(),colRoutePairSet);\r\n//\t\t\t\tif (nodeSet.isEmpty() || nodeSet.size() > 1) {//an exception would be thrown, if sql was executed on more than on sharding\r\n//\t\t\t\t\tthrow new SQLNonTransientException(\r\n//\t\t\t\t\t\t\t\"parent key can't find  valid datanode ,expect 1 but found: \"\r\n//\t\t\t\t\t\t\t\t\t+ nodeSet.size());\r\n//\t\t\t\t}\r\n//\t\t\t\tString dn = nodeSet.iterator().next();\r\n//\t\t\t\tif (LOGGER.isDebugEnabled()) {\r\n//\t\t\t\t\tLOGGER.debug(\"found partion node (using parent partion rule directly) for child table to insert  \"\r\n//\t\t\t\t\t\t\t+ dn + \" sql :\" + rrs.getStatement());\r\n//\t\t\t\t}\r\n//\t\t\t\tretNodeSet.addAll(nodeSet);\r\n//\t\t\t}\r\n\t\t\treturn retNodeSet;\r\n\t\t} else {\r\n\t\t\tretNodeSet.addAll(tc.getParentTC().getDataNodes());\r\n\t\t}\r\n\r\n\t\treturn retNodeSet;\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * @return dataNodeIndex -&gt; [partitionKeysValueTuple+]\r\n\t */\r\n\tpublic static Set<String> ruleCalculate(TableConfig tc,\r\n\t\t\tSet<ColumnRoutePair> colRoutePairSet,Map<String,Integer>   dataNodeSlotMap)  {\r\n\t\tSet<String> routeNodeSet = new LinkedHashSet<String>();\r\n\t\tString col = tc.getRule().getColumn();\r\n\t\tRuleConfig rule = tc.getRule();\r\n\t\tAbstractPartitionAlgorithm algorithm = rule.getRuleAlgorithm();\r\n\t\tfor (ColumnRoutePair colPair : colRoutePairSet) {\r\n\t\t\tif (colPair.colValue != null) {\r\n\t\t\t\tInteger nodeIndx = algorithm.calculate(StringUtil.removeBackquote(colPair.colValue));\r\n\t\t\t\tif (nodeIndx == null) {\r\n\t\t\t\t\tthrow new IllegalArgumentException(\r\n\t\t\t\t\t\t\t\"can't find datanode for sharding column:\" + col\r\n\t\t\t\t\t\t\t\t\t+ \" val:\" + colPair.colValue);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tString dataNode = tc.getDataNodes().get(nodeIndx);\r\n\t\t\t\t\trouteNodeSet.add(dataNode);\r\n\t\t\t\t\tif(algorithm instanceof SlotFunction) {\r\n\t\t\t\t\t\tdataNodeSlotMap.put(dataNode,((SlotFunction) algorithm).slotValue());\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcolPair.setNodeId(nodeIndx);\r\n\t\t\t\t}\r\n\t\t\t} else if (colPair.rangeValue != null) {\r\n\t\t\t\tInteger[] nodeRange = algorithm.calculateRange(\r\n\t\t\t\t\t\tString.valueOf(colPair.rangeValue.beginValue),\r\n\t\t\t\t\t\tString.valueOf(colPair.rangeValue.endValue));\r\n\t\t\t\tif (nodeRange != null) {\r\n\t\t\t\t\t/**\r\n\t\t\t\t\t * 不能确认 colPair的 nodeid是否会有其它影响\r\n\t\t\t\t\t */\r\n\t\t\t\t\tif (nodeRange.length == 0) {\r\n\t\t\t\t\t\trouteNodeSet.addAll(tc.getDataNodes());\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tArrayList<String> dataNodes = tc.getDataNodes();\r\n\t\t\t\t\t\tString dataNode = null;\r\n\t\t\t\t\t\tfor (Integer nodeId : nodeRange) {\r\n\t\t\t\t\t\t\tdataNode = dataNodes.get(nodeId);\r\n\t\t\t\t\t\t\tif(algorithm instanceof SlotFunction) {\r\n\t\t\t\t\t\t\t\tdataNodeSlotMap.put(dataNode,((SlotFunction) algorithm).slotValue());\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\trouteNodeSet.add(dataNode);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t}\r\n\t\treturn routeNodeSet;\r\n\t}\r\n\r\n\t/**\r\n\t * 多表路由\r\n\t */\r\n\tpublic static RouteResultset tryRouteForTables(SchemaConfig schema, DruidShardingParseInfo ctx,\r\n\t\t\tRouteCalculateUnit routeUnit, RouteResultset rrs, boolean isSelect, LayerCachePool cachePool)\r\n\t\t\tthrows SQLNonTransientException {\r\n\r\n\t\tList<String> tables = ctx.getTables();\r\n\r\n\r\n\t\tif(schema.isNoSharding()||(tables.size() >= 1&&isNoSharding(schema,tables.get(0)))) {\r\n\t\t\treturn routeToSingleNode(rrs, schema.getDataNode(), ctx.getSql());\r\n\t\t}\r\n\t\t// 系统表，不会记录到DruidShardingParseInfo里面，此时(tables.size()为0，直接路由到schema默认节点\r\n\t\tif (tables.size() == 0) {\r\n\t\t\treturn routeToSingleNode(rrs, schema.getDataNode(), ctx.getSql());\r\n\t\t}\r\n\t\t\r\n\t\t//每个表对应的路由映射\r\n\t\tMap<String,Set<String>> tablesRouteMap = new HashMap<String,Set<String>>();\r\n\r\n\t\t//为全局表和单库表找路由\r\n\t\tfor(String tableName : tables) {\r\n\r\n\t\t\tTableConfig tableConfig = schema.getTables().get(tableName.toUpperCase());\r\n\r\n\t\t\tif(tableConfig == null) {\r\n\t\t\t\t//add 如果表读取不到则先将表名从别名中读取转化后再读取\r\n\t\t\t\tString alias = ctx.getTableAliasMap().get(tableName);\r\n\t\t\t\tif(!StringUtil.isEmpty(alias)){\r\n\t\t\t\t\ttableConfig = schema.getTables().get(alias.toUpperCase());\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(tableConfig == null){\r\n\t\t\t\t\tif (StringUtils.isNotEmpty(schema.getDataNode())) {\r\n\t\t\t\t\t\treturn routeToSingleNode(rrs, schema.getDataNode(), ctx.getSql());\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tString msg = \"can't find table define in schema \" + tableName + \" alias：\" + alias + \", schema:\"\r\n\t\t\t\t\t\t\t\t+ schema.getName();\r\n\t\t\t\t\t\tLOGGER.warn(msg);\r\n\t\t\t\t\t\tthrow new SQLNonTransientException(msg);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t\tif(tableConfig.isGlobalTable()) {//全局表\r\n\t\t\t\tif(tablesRouteMap.get(tableName) == null) {\r\n\t\t\t\t\ttablesRouteMap.put(tableName, new HashSet<String>());\r\n\t\t\t\t}\r\n\t\t\t\ttablesRouteMap.get(tableName).addAll(tableConfig.getDataNodes());\r\n\t\t\t} else if(tablesRouteMap.get(tableName) == null) { //余下的表都是单库表\r\n\t\t\t\ttablesRouteMap.put(tableName, new HashSet<String>());\r\n\t\t\t\ttablesRouteMap.get(tableName).addAll(tableConfig.getDataNodes());\r\n\t\t\t}\r\n\r\n\t\t\tif(tableConfig.getDistTables().size() > 0) {\r\n\t\t\t\tMap<String, List<String>> subTablesmap = rrs.getSubTableMaps();\r\n\t\t\t\tif (subTablesmap == null) {\r\n\t\t\t\t\tsubTablesmap = Maps.newHashMap();\r\n\t\t\t\t\trrs.setSubTableMaps(subTablesmap);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tsubTablesmap.put(tableName.toUpperCase(), tableConfig.getDistTables());\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\t//只有一个表的\r\n\t\tif(tables.size() == 1) {\r\n\t\t\treturn RouterUtil.tryRouteForOneTable(schema, ctx, routeUnit, tables.get(0), rrs, isSelect, cachePool);\r\n\t\t}\r\n\r\n\t\tSet<String> retNodesSet = new HashSet<String>();\r\n\r\n\t\t//分库解析信息不为空\r\n\t\tMap<String, Map<String, Set<ColumnRoutePair>>> tablesAndConditions = routeUnit.getTablesAndConditions();\r\n\t\tif(tablesAndConditions != null && tablesAndConditions.size() > 0) {\r\n\t\t\t//为分库表找路由\r\n\t\t\tRouterUtil.findRouteWithcConditionsForTables(schema, rrs, tablesAndConditions, tablesRouteMap, ctx.getSql(), cachePool, isSelect);\r\n\t\t\tif(rrs.isFinishedRoute()) {\r\n\t\t\t\treturn rrs;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\tboolean isFirstAdd = true;\r\n\t\tfor(Map.Entry<String, Set<String>> entry : tablesRouteMap.entrySet()) {\r\n\t\t\tif(entry.getValue() == null || entry.getValue().size() == 0) {\r\n\t\t\t\tthrow new SQLNonTransientException(\"parent key can't find any valid datanode \");\r\n\t\t\t} else {\r\n\t\t\t\tif(isFirstAdd) {\r\n\t\t\t\t\tretNodesSet.addAll(entry.getValue());\r\n\t\t\t\t\tisFirstAdd = false;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tretNodesSet.retainAll(entry.getValue());\r\n\t\t\t\t\tif(retNodesSet.size() == 0) {//两个表的路由无交集\r\n\t\t\t\t\t\tString errMsg = \"invalid route in sql, multi tables found but datanode has no intersection \"\r\n\t\t\t\t\t\t\t\t+ \" sql:\" + ctx.getSql();\r\n\t\t\t\t\t\tLOGGER.warn(errMsg);\r\n\t\t\t\t\t\tthrow new SQLNonTransientException(errMsg);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif(retNodesSet != null && retNodesSet.size() > 0) {\r\n\t\t\tString tableName = tables.get(0);\r\n\t\t\tTableConfig tableConfig = schema.getTables().get(tableName.toUpperCase());\r\n\t\t\tif(tableConfig.isDistTable()){\r\n\t\t\t    routeToDistTableNode(schema, rrs, ctx.getSql(), tablesAndConditions, cachePool, isSelect, null);\r\n\t\t\t\treturn rrs;\r\n\t\t\t}\r\n\r\n\t\t\tif(retNodesSet.size() > 1 && isAllGlobalTable(ctx, schema)) {\r\n\t\t\t\t// mulit routes ,not cache route result\r\n\t\t\t\tif (isSelect) {\r\n\t\t\t\t\trrs.setCacheAble(false);\r\n\t\t\t\t\tArrayList<String> retNodeList = new ArrayList<String>(retNodesSet);\r\n\t\t\t\t\tCollections.shuffle(retNodeList);//by kaiz : add shuffle\r\n\t\t\t\t\trouteToSingleNode(rrs, retNodeList.get(0), ctx.getSql());\r\n\t\t\t\t}\r\n\t\t\t\telse {//delete 删除全局表的记录\r\n\t\t\t\t\trouteToMultiNode(isSelect, rrs, retNodesSet, ctx.getSql(),true);\r\n\t\t\t\t}\r\n\r\n\t\t\t} else {\r\n\t\t\t\trouteToMultiNode(isSelect, rrs, retNodesSet, ctx.getSql());\r\n\t\t\t}\r\n\r\n\t\t}\r\n\t\treturn rrs;\r\n\r\n\t}\r\n\r\n\r\n\t/**\r\n\t *\r\n\t * 单表路由\r\n\t */\r\n\tpublic static RouteResultset tryRouteForOneTable(SchemaConfig schema, DruidShardingParseInfo ctx,\r\n\t\t\tRouteCalculateUnit routeUnit, String tableName, RouteResultset rrs, boolean isSelect,\r\n\t\t\tLayerCachePool cachePool) throws SQLNonTransientException {\r\n\r\n\t\tif (isNoSharding(schema, tableName)) {\r\n\t\t\treturn routeToSingleNode(rrs, schema.getDataNode(), ctx.getSql());\r\n\t\t}\r\n\r\n\t\tTableConfig tc = schema.getTables().get(tableName);\r\n\t\tif(tc == null) {\r\n\t\t\tString msg = \"can't find table define in schema \" + tableName + \" schema:\" + schema.getName();\r\n\t\t\tLOGGER.warn(msg);\r\n\t\t\tthrow new SQLNonTransientException(msg);\r\n\t\t}\r\n\r\n\t\tMap<String, Map<String, Set<ColumnRoutePair>>> tablesAndConditions = routeUnit.getTablesAndConditions();\r\n        if(tc.isDistTable()){\r\n            Set<String> keySet = tablesAndConditions.keySet();\r\n            //Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry = (Entry<String, Map<String, Set<ColumnRoutePair>>>) tablesAndConditions.get(keySet.toArray()[0]);\r\n            return routeToDistTableNode(schema,rrs,ctx.getSql(), tablesAndConditions, cachePool,isSelect, null);\r\n        }\r\n\r\n\t\tif(tc.isGlobalTable()) {//全局表\r\n\t\t\tif(isSelect) {\r\n\t\t\t\t// global select ,not cache route result\r\n\t\t\t\trrs.setCacheAble(false);\r\n\t\t\t\treturn routeToSingleNode(rrs, getAliveRandomDataNode(tc)/*getRandomDataNode(tc)*/, ctx.getSql());\r\n\t\t\t} else {//insert into 全局表的记录\r\n\t\t\t\treturn routeToMultiNode(false, rrs, tc.getDataNodes(), ctx.getSql(),true);\r\n\t\t\t}\r\n\t\t} else {//单表或者分库表\r\n\t\t\tif (!checkRuleRequired(schema, ctx, routeUnit, tc)) {\r\n\t\t\t\tthrow new IllegalArgumentException(\"route rule for table \"\r\n\t\t\t\t\t\t+ tc.getName() + \" is required: \" + ctx.getSql());\r\n\r\n\t\t\t}\r\n\t\t\tif(tc.getPartitionColumn() == null && !tc.isSecondLevel()) {//单表且不是childTable\r\n//\t\t\t\treturn RouterUtil.routeToSingleNode(rrs, tc.getDataNodes().get(0),ctx.getSql());\r\n\t\t\t\treturn routeToMultiNode(rrs.isCacheAble(), rrs, tc.getDataNodes(), ctx.getSql());\r\n\t\t\t} else {\r\n\t\t\t\t//每个表对应的路由映射\r\n\t\t\t\tMap<String,Set<String>> tablesRouteMap = new HashMap<String,Set<String>>();\r\n\t\t\t\tif(routeUnit.getTablesAndConditions() != null && routeUnit.getTablesAndConditions().size() > 0) {\r\n\t\t\t\t\tRouterUtil.findRouteWithcConditionsForTables(schema, rrs, routeUnit.getTablesAndConditions(), tablesRouteMap, ctx.getSql(), cachePool, isSelect);\r\n\t\t\t\t\tif(rrs.isFinishedRoute()) {\r\n\t\t\t\t\t\treturn rrs;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(tablesRouteMap.get(tableName) == null) {\r\n\t\t\t\t\treturn routeToMultiNode(rrs.isCacheAble(), rrs, tc.getDataNodes(), ctx.getSql());\r\n\t\t\t\t} else {\r\n\t\t\t\t\treturn routeToMultiNode(rrs.isCacheAble(), rrs, tablesRouteMap.get(tableName), ctx.getSql());\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static RouteResultset routeToDistTableNode(SchemaConfig schema, RouteResultset rrs,\r\n        String orgSql, Map<String, Map<String, Set<ColumnRoutePair>>> tablesAndConditions,\r\n        LayerCachePool cachePool, boolean isSelect, Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry) throws SQLNonTransientException {\r\n\r\n\r\n        String tableName = null;\r\n        if (entry != null) {\r\n            tableName = entry.getKey().toUpperCase();\r\n        } else {\r\n            List<String> tables = rrs.getTables();\r\n            tableName = tables.get(0);\r\n        }\r\n        TableConfig tableConfig = schema.getTables().get(tableName);\r\n        if(tableConfig == null) {\r\n            String msg = \"can't find table define in schema \" + tableName + \" schema:\" + schema.getName();\r\n            LOGGER.warn(msg);\r\n            throw new SQLNonTransientException(msg);\r\n        }\r\n        if(tableConfig.isGlobalTable()){\r\n            String msg = \"can't suport district table  \" + tableName + \" schema:\" + schema.getName() + \" for global table \";\r\n            LOGGER.warn(msg);\r\n            throw new SQLNonTransientException(msg);\r\n        }\r\n        String partionCol = tableConfig.getPartitionColumn();\r\n    //  String primaryKey = tableConfig.getPrimaryKey();\r\n        //boolean isLoadData=false;\r\n    \r\n        Set<String> tablesRouteSet = new HashSet<String>();\r\n    \r\n        List<String> dataNodes = tableConfig.getDataNodes();\r\n        if(dataNodes.size()>1){\r\n            String msg = \"can't suport district table  \" + tableName + \" schema:\" + schema.getName() + \" for mutiple dataNode \" + dataNodes;\r\n            LOGGER.warn(msg);\r\n            throw new SQLNonTransientException(msg);\r\n        }\r\n        String dataNode = dataNodes.get(0);\r\n    \r\n        RouteResultsetNode[] nodes = null;\r\n        //主键查找缓存暂时不实现\r\n        if(tablesAndConditions.isEmpty()){\r\n            List<String> subTables = tableConfig.getDistTables();\r\n            tablesRouteSet.addAll(subTables);\r\n    \r\n            nodes = getNode(rrs, orgSql, tablesRouteSet, dataNode, false, tableName);\r\n        } else {\r\n            if (entry == null) {\r\n                for(Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry1 : tablesAndConditions.entrySet()) {\r\n                    //boolean isFoundPartitionValue = partionCol != null && entry.getValue().get(partionCol) != null;\r\n                    setNodes(rrs, tableConfig, partionCol, tablesRouteSet, entry1);\r\n                }\r\n            } else {\r\n                setNodes(rrs, tableConfig, partionCol, tablesRouteSet, entry);\r\n            }\r\n    \r\n    \r\n            nodes = getNode(rrs, orgSql, tablesRouteSet, dataNode, true, tableName);\r\n        }\r\n        rrs.setNodes(nodes);\r\n        rrs.setSubTables(tablesRouteSet);\r\n        rrs.setFinishedRoute(true);\r\n    \r\n        return rrs;\r\n    }\r\n\t\r\n\tprivate static void setNodes(RouteResultset rrs, TableConfig tableConfig, String partionCol,\r\n        Set<String> tablesRouteSet, Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry1)\r\n        throws SQLNonTransientException {\r\n        Map<String, Set<ColumnRoutePair>> columnsMap = entry1.getValue();\r\n    \r\n        Set<ColumnRoutePair> partitionValue = columnsMap.get(partionCol);\r\n        if(partitionValue == null || partitionValue.size() == 0) {\r\n            tablesRouteSet.addAll(tableConfig.getDistTables());\r\n        } else {\r\n            for(ColumnRoutePair pair : partitionValue) {\r\n                AbstractPartitionAlgorithm algorithm = tableConfig.getRule().getRuleAlgorithm();\r\n                if(pair.colValue != null) {\r\n                    Integer tableIndex = algorithm.calculate(pair.colValue);\r\n                    if(tableIndex == null) {\r\n                        String msg = \"can't find any valid datanode :\" + tableConfig.getName()\r\n                                + \" -> \" + tableConfig.getPartitionColumn() + \" -> \" + pair.colValue;\r\n                        LOGGER.warn(msg);\r\n                        throw new SQLNonTransientException(msg);\r\n                    }\r\n                    String subTable = tableConfig.getDistTables().get(tableIndex);\r\n                    if(subTable != null) {\r\n                        tablesRouteSet.add(subTable);\r\n                        if(algorithm instanceof SlotFunction){\r\n                            rrs.getDataNodeSlotMap().put(subTable,((SlotFunction) algorithm).slotValue());\r\n                        }\r\n                    }\r\n                }\r\n                if(pair.rangeValue != null) {\r\n                    Integer[] tableIndexs = algorithm\r\n                            .calculateRange(pair.rangeValue.beginValue.toString(), pair.rangeValue.endValue.toString());\r\n                    for(Integer idx : tableIndexs) {\r\n                        String subTable = tableConfig.getDistTables().get(idx);\r\n                        if(subTable != null) {\r\n                            tablesRouteSet.add(subTable);\r\n                            if(algorithm instanceof SlotFunction){\r\n                                rrs.getDataNodeSlotMap().put(subTable,((SlotFunction) algorithm).slotValue());\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n\tprivate static RouteResultsetNode[] getNode(RouteResultset rrs, String orgSql, Set<String> tablesRouteSet,\r\n\t\t\tString dataNode, boolean is, String tableName) {\r\n\t\tObject[] subTables =  tablesRouteSet.toArray();\r\n\t\tRouteResultsetNode[] nodes = new RouteResultsetNode[subTables.length];\r\n\t\tMap<String,Integer> dataNodeSlotMap= rrs.getDataNodeSlotMap();\r\n\t\tfor(int i=0;i<nodes.length;i++){\r\n\t\t\tString table = String.valueOf(subTables[i]);\r\n\t\t\tString changeSql = orgSql;\r\n\t\t\tnodes[i] = new RouteResultsetNode(dataNode, rrs.getSqlType(), changeSql);//rrs.getStatement()\r\n\t\t\tnodes[i].setSubTableName(table);\r\n\r\n\t\t\tif (is) {\r\n\t\t\t\tMap<String, List<String>> subTableMaps = rrs.getSubTableMaps();\r\n\t\t\t\tif(subTableMaps != null) {\r\n\t\t\t\t\tList<String> list = subTableMaps.get(tableName);\r\n\t\t\t\t\tint index = 0;\r\n\t\t\t\t\tfor (String subTable : list) {\r\n\t\t\t\t\t\tif (table.equals(subTable)) {\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tindex++;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tfor (String tableSource : subTableMaps.keySet()) {\r\n\t\t\t\t\t\tMap<String, String> subTableNames = nodes[i].getSubTableNames();\r\n\t\t\t\t\t\tif (subTableNames == null) {\r\n\t\t\t\t\t\t\tsubTableNames = Maps.newHashMap();\r\n\t\t\t\t\t\t\tnodes[i].setSubTableNames(subTableNames);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (tableSource.equals(tableName)) {\r\n\t\t\t\t\t\t\tsubTableNames.put(tableSource, table);\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\tsubTableNames.put(tableSource, subTableMaps.get(tableSource).get(index));\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tMap<String, List<String>> subTableMaps = rrs.getSubTableMaps();\r\n\t\t\t\tif(subTableMaps != null) {\r\n\t\t\t\t\tfor (String tableSource : subTableMaps.keySet()) {\r\n\t\t\t\t\t\tMap<String, String> subTableNames = nodes[i].getSubTableNames();\r\n\t\t\t\t\t\tif (subTableNames == null) {\r\n\t\t\t\t\t\t\tsubTableNames = Maps.newHashMap();\r\n\t\t\t\t\t\t\tnodes[i].setSubTableNames(subTableNames);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tsubTableNames.put(tableSource, subTableMaps.get(tableSource).get(i));\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tnodes[i].setSource(rrs);\r\n\t\t\tif(rrs.getDataNodeSlotMap().containsKey(dataNode)){\r\n\t\t\t\tnodes[i].setSlot(rrs.getDataNodeSlotMap().get(dataNode));\r\n\t\t\t}\r\n\t\t\tif (rrs.getCanRunInReadDB() != null) {\r\n\t\t\t\tnodes[i].setCanRunInReadDB(rrs.getCanRunInReadDB());\r\n\t\t\t}\r\n\t\t\tif(dataNodeSlotMap.containsKey(table))  {\r\n\t\t\t\tnodes[i].setSlot(dataNodeSlotMap.get(table));\r\n\t\t\t}\r\n\t\t\tif(rrs.getRunOnSlave() != null){\r\n\t\t\t\tnodes[0].setRunOnSlave(rrs.getRunOnSlave());\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn nodes;\r\n\t}\r\n\r\n\t/**\r\n\t * 处理分库表路由\r\n\t */\r\n\tpublic static void findRouteWithcConditionsForTables(SchemaConfig schema, RouteResultset rrs,\r\n\t\t\tMap<String, Map<String, Set<ColumnRoutePair>>> tablesAndConditions,\r\n\t\t\tMap<String, Set<String>> tablesRouteMap, String sql, LayerCachePool cachePool, boolean isSelect)\r\n\t\t\tthrows SQLNonTransientException {\r\n\r\n\t\t//为分库表找路由\r\n\t\tfor(Map.Entry<String, Map<String, Set<ColumnRoutePair>>> entry : tablesAndConditions.entrySet()) {\r\n\t\t\tString tableName = entry.getKey().toUpperCase();\r\n\t\t\tTableConfig tableConfig = schema.getTables().get(tableName);\r\n\t\t\tif(tableConfig == null) {\r\n\t\t\t\tString msg = \"can't find table define in schema \"\r\n\t\t\t\t\t\t+ tableName + \" schema:\" + schema.getName();\r\n\t\t\t\tLOGGER.warn(msg);\r\n\t\t\t\tthrow new SQLNonTransientException(msg);\r\n\t\t\t}\r\n\t\t\tif(tableConfig.getDistTables()!=null && tableConfig.getDistTables().size()>0){\r\n\t\t\t    routeToDistTableNode(schema,rrs,sql, tablesAndConditions, cachePool,isSelect, entry);\r\n\t\t\t}\r\n\t\t\t//全局表或者不分库的表略过（全局表后面再计算）\r\n\t\t\tif(tableConfig.isGlobalTable() || schema.getTables().get(tableName).getDataNodes().size() == 1) {\r\n\t\t\t\tcontinue;\r\n\t\t\t} else {//非全局表：分库表、childTable、其他\r\n\t\t\t\tMap<String, Set<ColumnRoutePair>> columnsMap = entry.getValue();\r\n\t\t\t\tString joinKey = tableConfig.getJoinKey();\r\n\t\t\t\tString partionCol = tableConfig.getPartitionColumn();\r\n\t\t\t\tString primaryKey = tableConfig.getPrimaryKey();\r\n\t\t\t\tboolean isFoundPartitionValue = partionCol != null && entry.getValue().get(partionCol) != null;\r\n\t\t\t\tboolean isLoadData=false;\r\n\t\t\t\tif (LOGGER.isDebugEnabled()\r\n\t\t\t\t\t\t&& sql.startsWith(LoadData.loadDataHint)||rrs.isLoadData()) {\r\n\t\t\t\t\t//由于load data一次会计算很多路由数据，如果输出此日志会极大降低load data的性能\r\n\t\t\t\t\tisLoadData=true;\r\n\t\t\t\t}\r\n\t\t\t\tif(entry.getValue().get(primaryKey) != null && entry.getValue().size() == 1&&!isLoadData)\r\n\t\t\t\t{//主键查找\r\n\t\t\t\t\t// try by primary key if found in cache\r\n\t\t\t\t\tSet<ColumnRoutePair> primaryKeyPairs = entry.getValue().get(primaryKey);\r\n\t\t\t\t\tif (primaryKeyPairs != null) {\r\n\t\t\t\t\t\tif (LOGGER.isDebugEnabled()) {\r\n\t\t\t\t\t\t\tLOGGER.debug(\"try to find cache by primary key \");\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tString tableKey = schema.getName() + '_' + tableName;\r\n\t\t\t\t\t\ttableKey = tableKey.toUpperCase();\r\n\t\t\t\t\t\tboolean allFound = true;\r\n\t\t\t\t\t\tfor (ColumnRoutePair pair : primaryKeyPairs) {//可能id in(1,2,3)多主键\r\n\t\t\t\t\t\t\tString cacheKey = pair.colValue;\r\n\t\t\t\t\t\t\tString dataNode = (String) cachePool.get(tableKey, cacheKey);\r\n\t\t\t\t\t\t\tif (dataNode == null) {\r\n\t\t\t\t\t\t\t\tallFound = false;\r\n\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\t\tif(tablesRouteMap.get(tableName) == null) {\r\n\t\t\t\t\t\t\t\t\ttablesRouteMap.put(tableName, new HashSet<String>());\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\ttablesRouteMap.get(tableName).add(dataNode);\r\n\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (!allFound) {\r\n\t\t\t\t\t\t\t// need cache primary key ->datanode relation\r\n\t\t\t\t\t\t\tif (isSelect && tableConfig.getPrimaryKey() != null) {\r\n\t\t\t\t\t\t\t\trrs.setPrimaryKey(tableKey + '.' + tableConfig.getPrimaryKey());\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t} else {//主键缓存中找到了就执行循环的下一轮\r\n\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (isFoundPartitionValue) {//分库表\r\n                    tablesRouteMap.clear();\r\n\t\t\t\t\tSet<ColumnRoutePair> partitionValue = columnsMap.get(partionCol);\r\n\t\t\t\t\tif(partitionValue == null || partitionValue.size() == 0) {\r\n\t\t\t\t\t\tif(tablesRouteMap.get(tableName) == null) {\r\n\t\t\t\t\t\t\ttablesRouteMap.put(tableName, new HashSet<String>());\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\ttablesRouteMap.get(tableName).addAll(tableConfig.getDataNodes());\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tfor(ColumnRoutePair pair : partitionValue) {\r\n\t\t\t\t\t\t\tAbstractPartitionAlgorithm algorithm = tableConfig.getRule().getRuleAlgorithm();\r\n\t\t\t\t\t\t\tif(pair.colValue != null) {\r\n\t\t\t\t\t\t\t\tInteger nodeIndex = algorithm.calculate(StringUtil.removeBackquote(pair.colValue));\r\n\t\t\t\t\t\t\t\tif(nodeIndex == null) {\r\n\t\t\t\t\t\t\t\t\tString msg = \"can't find any valid datanode :\" + tableConfig.getName()\r\n\t\t\t\t\t\t\t\t\t\t\t+ \" -> \" + tableConfig.getPartitionColumn() + \" -> \" + pair.colValue;\r\n\t\t\t\t\t\t\t\t\tLOGGER.warn(msg);\r\n\t\t\t\t\t\t\t\t\tthrow new SQLNonTransientException(msg);\r\n\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\tArrayList<String> dataNodes = tableConfig.getDataNodes();\r\n\t\t\t\t\t\t\t\tString node;\r\n\t\t\t\t\t\t\t\tif (nodeIndex >=0 && nodeIndex < dataNodes.size()) {\r\n\t\t\t\t\t\t\t\t\tnode = dataNodes.get(nodeIndex);\r\n\r\n\t\t\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\t\t\tnode = null;\r\n\t\t\t\t\t\t\t\t\tString msg = \"Can't find a valid data node for specified node index :\"\r\n\t\t\t\t\t\t\t\t\t\t\t+ tableConfig.getName() + \" -> \" + tableConfig.getPartitionColumn()\r\n\t\t\t\t\t\t\t\t\t\t\t+ \" -> \" + pair.colValue + \" -> \" + \"Index : \" + nodeIndex;\r\n\t\t\t\t\t\t\t\t\tLOGGER.warn(msg);\r\n\t\t\t\t\t\t\t\t\tthrow new SQLNonTransientException(msg);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\tif(node != null) {\r\n\t\t\t\t\t\t\t\t\tif(tablesRouteMap.get(tableName) == null) {\r\n\t\t\t\t\t\t\t\t\t\ttablesRouteMap.put(tableName, new HashSet<String>());\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\tif(algorithm instanceof SlotFunction){\r\n\t\t\t\t\t\t\t\t\t\trrs.getDataNodeSlotMap().put(node,((SlotFunction) algorithm).slotValue());\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\ttablesRouteMap.get(tableName).add(node);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tif(pair.rangeValue != null) {\r\n\t\t\t\t\t\t\t\tInteger[] nodeIndexs = algorithm\r\n\t\t\t\t\t\t\t\t\t\t.calculateRange(pair.rangeValue.beginValue.toString(), pair.rangeValue.endValue.toString());\r\n\t\t\t\t\t\t\t\tArrayList<String> dataNodes = tableConfig.getDataNodes();\r\n\t\t\t\t\t\t\t\tString node;\r\n\t\t\t\t\t\t\t\tfor(Integer idx : nodeIndexs) {\r\n\t\t\t\t\t\t\t\t\tif (idx >= 0 && idx < dataNodes.size()) {\r\n\t\t\t\t\t\t\t\t\t\tnode = dataNodes.get(idx);\r\n\t\t\t\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\t\t\t\tString msg = \"Can't find valid data node(s) for some of specified node indexes :\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t+ tableConfig.getName() + \" -> \" + tableConfig.getPartitionColumn();\r\n\t\t\t\t\t\t\t\t\t\tLOGGER.warn(msg);\r\n\t\t\t\t\t\t\t\t\t\tthrow new SQLNonTransientException(msg);\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\tif(node != null) {\r\n\t\t\t\t\t\t\t\t\t\tif(tablesRouteMap.get(tableName) == null) {\r\n\t\t\t\t\t\t\t\t\t\t\ttablesRouteMap.put(tableName, new HashSet<String>());\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\tif(algorithm instanceof SlotFunction){\r\n\t\t\t\t\t\t\t\t\t\t\trrs.getDataNodeSlotMap().put(node,((SlotFunction) algorithm).slotValue());\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\ttablesRouteMap.get(tableName).add(node);\r\n\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t} else if(joinKey != null && columnsMap.get(joinKey) != null && columnsMap.get(joinKey).size() != 0) {//childTable  (如果是select 语句的父子表join)之前要找到root table,将childTable移除,只留下root table\r\n\t\t\t\t\tSet<ColumnRoutePair> joinKeyValue = columnsMap.get(joinKey);\r\n\r\n\t\t\t\t\tSet<String> dataNodeSet = ruleByJoinValueCalculate(rrs, tableConfig, joinKeyValue);\r\n\r\n\t\t\t\t\tif (dataNodeSet.isEmpty()) {\r\n\t\t\t\t\t\tthrow new SQLNonTransientException(\r\n\t\t\t\t\t\t\t\t\"parent key can't find any valid datanode \");\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (LOGGER.isDebugEnabled()) {\r\n\t\t\t\t\t\tLOGGER.debug(\"found partion nodes (using parent partion rule directly) for child table to update  \"\r\n\t\t\t\t\t\t\t\t+ Arrays.toString(dataNodeSet.toArray()) + \" sql :\" + sql);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (dataNodeSet.size() > 1) {\r\n\t\t\t\t\t\trouteToMultiNode(rrs.isCacheAble(), rrs, dataNodeSet, sql);\r\n\t\t\t\t\t\trrs.setFinishedRoute(true);\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\trrs.setCacheAble(true);\r\n\t\t\t\t\t\trouteToSingleNode(rrs, dataNodeSet.iterator().next(), sql);\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t} else {\r\n\t\t\t\t\t//没找到拆分字段，该表的所有节点都路由\r\n\t\t\t\t\tif(tablesRouteMap.get(tableName) == null) {\r\n\t\t\t\t\t\ttablesRouteMap.put(tableName, new HashSet<String>());\r\n\t\t\t\t\t}\r\n\t\t\t\t\tboolean isSlotFunction= tableConfig.getRule() != null && tableConfig.getRule().getRuleAlgorithm() instanceof SlotFunction;\r\n\t\t\t\t\tif(isSlotFunction){\r\n\t\t\t\t\t\tfor (String dn : tableConfig.getDataNodes()) {\r\n\t\t\t\t\t\t\trrs.getDataNodeSlotMap().put(dn,-1);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\ttablesRouteMap.get(tableName).addAll(tableConfig.getDataNodes());\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static boolean isAllGlobalTable(DruidShardingParseInfo ctx, SchemaConfig schema) {\r\n\t\tboolean isAllGlobal = false;\r\n\t\tfor(String table : ctx.getTables()) {\r\n\t\t\tTableConfig tableConfig = schema.getTables().get(table);\r\n\t\t\tif(tableConfig!=null && tableConfig.isGlobalTable()) {\r\n\t\t\t\tisAllGlobal = true;\r\n\t\t\t} else {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn isAllGlobal;\r\n\t}\r\n\r\n\t/**\r\n\t *\r\n\t * @param schema\r\n\t * @param ctx\r\n\t * @param tc\r\n\t * @return true表示校验通过，false表示检验不通过\r\n\t */\r\n\tpublic static boolean checkRuleRequired(SchemaConfig schema, DruidShardingParseInfo ctx, RouteCalculateUnit routeUnit, TableConfig tc) {\r\n\t\tif(!tc.isRuleRequired()) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tboolean hasRequiredValue = false;\r\n\t\tString tableName = tc.getName();\r\n\t\tif(routeUnit.getTablesAndConditions().get(tableName) == null || routeUnit.getTablesAndConditions().get(tableName).size() == 0) {\r\n\t\t\thasRequiredValue = false;\r\n\t\t} else {\r\n\t\t\tfor(Map.Entry<String, Set<ColumnRoutePair>> condition : routeUnit.getTablesAndConditions().get(tableName).entrySet()) {\r\n\r\n\t\t\t\tString colName = condition.getKey();\r\n\t\t\t\t//条件字段是拆分字段\r\n\t\t\t\tif(colName.equals(tc.getPartitionColumn())) {\r\n\t\t\t\t\thasRequiredValue = true;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn hasRequiredValue;\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * 增加判断支持未配置分片的表走默认的dataNode\r\n\t * @param schemaConfig\r\n\t * @param tableName\r\n\t * @return\r\n\t */\r\n\tpublic static boolean isNoSharding(SchemaConfig schemaConfig, String tableName) {\r\n\t\t// Table名字被转化为大写的，存储在schema\r\n\t\ttableName = tableName.toUpperCase();\r\n\t\tif (schemaConfig.isNoSharding()) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tif (schemaConfig.getDataNode() != null && !schemaConfig.getTables().containsKey(tableName)) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * 系统表判断,某些sql语句会查询系统表或者跟系统表关联\r\n\t * @author lian\r\n\t * @date 2016年12月2日\r\n\t * @param tableName\r\n\t * @return\r\n\t */\r\n\tpublic static boolean isSystemSchema(String tableName) {\r\n\t\t// 以information_schema， mysql开头的是系统表\r\n\t\tif (tableName.startsWith(\"INFORMATION_SCHEMA.\")\r\n\t\t\t\t|| tableName.startsWith(\"MYSQL.\")\r\n\t\t\t\t|| tableName.startsWith(\"PERFORMANCE_SCHEMA.\")) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * 判断条件是否永真\r\n\t * @param expr\r\n\t * @return\r\n\t */\r\n\tpublic static boolean isConditionAlwaysTrue(SQLExpr expr) {\r\n\t\tObject o = WallVisitorUtils.getValue(expr);\r\n\t\tif(Boolean.TRUE.equals(o)) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * 判断条件是否永假的\r\n\t * @param expr\r\n\t * @return\r\n\t */\r\n\tpublic static boolean isConditionAlwaysFalse(SQLExpr expr) {\r\n\t\tObject o = WallVisitorUtils.getValue(expr);\r\n\t\tif(Boolean.FALSE.equals(o)) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * 该方法，返回是否是ER子表\r\n\t * @param schema\r\n\t * @param origSQL\r\n\t * @param sc\r\n\t * @return\r\n\t * @throws SQLNonTransientException\r\n\t *\r\n\t * 备注说明：\r\n\t *     edit by ding.w at 2017.4.28, 主要处理 CLIENT_MULTI_STATEMENTS(insert into ; insert into)的情况\r\n\t *     目前仅支持mysql,并COM_QUERY请求包中的所有insert语句要么全部是er表，要么全部不是\r\n\t *\r\n\t *\r\n\t */\r\n\tpublic static boolean processERChildTable(final SchemaConfig schema, final String origSQL,\r\n\t\t\tfinal ServerConnection sc) throws SQLNonTransientException {\r\n\r\n\t\tMySqlStatementParser parser = new MySqlStatementParser(origSQL);\r\n\t\tList<SQLStatement> statements = parser.parseStatementList();\r\n\r\n\t\tif(statements == null || statements.isEmpty() ) {\r\n\t\t\tthrow new SQLNonTransientException(String.format(\"无效的SQL语句:%s\", origSQL));\r\n\t\t}\r\n\r\n\r\n\t\tboolean erFlag = false; //是否是er表\r\n\t\tfor(SQLStatement stmt : statements ) {\r\n\t\t\tMySqlInsertStatement insertStmt = (MySqlInsertStatement) stmt;\r\n\t\t\tString tableName = insertStmt.getTableName().getSimpleName().toUpperCase();\r\n\t\t\tfinal TableConfig tc = schema.getTables().get(tableName);\r\n\r\n\t\t\tif (null != tc && tc.isChildTable()) {\r\n\t\t\t\terFlag = true;\r\n\r\n\t\t\t\tString sql = insertStmt.toString();\r\n\r\n\t\t\t\tfinal RouteResultset rrs = new RouteResultset(sql, ServerParse.INSERT);\r\n\t\t\t\tString joinKey = tc.getJoinKey();\r\n\t\t\t\t//因为是Insert语句，用MySqlInsertStatement进行parse\r\n//\t\t\t\tMySqlInsertStatement insertStmt = (MySqlInsertStatement) (new MySqlStatementParser(origSQL)).parseInsert();\r\n\t\t\t\t//判断条件完整性，取得解析后语句列中的joinkey列的index\r\n\t\t\t\tint joinKeyIndex = getJoinKeyIndex(insertStmt.getColumns(), joinKey);\r\n\t\t\t\tif (joinKeyIndex == -1) {\r\n\t\t\t\t\tString inf = \"joinKey not provided :\" + tc.getJoinKey() + \",\" + insertStmt;\r\n\t\t\t\t\tLOGGER.warn(inf);\r\n\t\t\t\t\tthrow new SQLNonTransientException(inf);\r\n\t\t\t\t}\r\n\t\t\t\t//子表不支持批量插入\r\n\t\t\t\tif (isMultiInsert(insertStmt)) {\r\n\t\t\t\t\tString msg = \"ChildTable multi insert not provided\";\r\n\t\t\t\t\tLOGGER.warn(msg);\r\n\t\t\t\t\tthrow new SQLNonTransientException(msg);\r\n\t\t\t\t}\r\n\t\t\t\t//取得joinkey的值\r\n\t\t\t\tString joinKeyVal = insertStmt.getValues().getValues().get(joinKeyIndex).toString();\r\n\t\t\t\t//解决bug #938，当关联字段的值为char类型时，去掉前后\"'\"\r\n\t\t\t\tString realVal = joinKeyVal;\r\n\t\t\t\tif (joinKeyVal.startsWith(\"'\") && joinKeyVal.endsWith(\"'\") && joinKeyVal.length() > 2) {\r\n\t\t\t\t\trealVal = joinKeyVal.substring(1, joinKeyVal.length() - 1);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// try to route by ER parent partion key\r\n\t\t\t\t//如果是二级子表（父表不再有父表）,并且分片字段正好是joinkey字段，调用routeByERParentKey\r\n\t\t\t\tRouteResultset theRrs = RouterUtil.routeByERParentKey(sc, schema, ServerParse.INSERT, sql, rrs, tc, realVal);\r\n\t\t\t\tif (theRrs != null) {\r\n\t\t\t\t\tboolean processedInsert=false;\r\n\t\t\t\t\t//判断是否需要全局序列号\r\n\t\t\t\t\tif ( sc!=null && tc.isAutoIncrement()) {\r\n\t\t\t\t\t\tString primaryKey = tc.getPrimaryKey();\r\n\t\t\t\t\t\tprocessedInsert=processInsert(sc,schema,ServerParse.INSERT,sql,tc.getName(),primaryKey);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif(processedInsert==false){\r\n\t\t\t\t\t\trrs.setFinishedRoute(true);\r\n\t\t\t\t\t\tsc.getSession2().execute(rrs, ServerParse.INSERT);\r\n\t\t\t\t\t}\r\n\t\t\t\t\t// return true;\r\n\t\t\t\t\t//继续处理下一条\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// route by sql query root parent's datanode\r\n\t\t\t\t//如果不是二级子表或者分片字段不是joinKey字段结果为空，则启动异步线程去后台分片查询出datanode\r\n\t\t\t\t//只要查询出上一级表的parentkey字段的对应值在哪个分片即可\r\n\t\t\t\tfinal String findRootTBSql = tc.getLocateRTableKeySql().toLowerCase() + joinKeyVal;\r\n\t\t\t\tif (LOGGER.isDebugEnabled()) {\r\n\t\t\t\t\tLOGGER.debug(\"find root parent's node sql \" + findRootTBSql);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tListenableFuture<String> listenableFuture = MycatServer.getInstance().\r\n\t\t\t\t\t\tgetListeningExecutorService().submit(new Callable<String>() {\r\n\t\t\t\t\t@Override\r\n\t\t\t\t\tpublic String call() throws Exception {\r\n\t\t\t\t\t\tif (tc.getRootParent().getFetchStoreNodeByJdbc()) {\r\n\t\t\t\t\t\t\tJDBCFetchStoreNodeOfChildTableHandler jdbcFetchStoreNodeOfChildTableHandler =\r\n\t\t\t\t\t\t\t\t\tnew JDBCFetchStoreNodeOfChildTableHandler();\r\n\t\t\t\t\t\t\treturn jdbcFetchStoreNodeOfChildTableHandler\r\n\t\t\t\t\t\t\t\t\t.execute(schema.getName(), findRootTBSql, tc.getRootParent().getDataNodes());\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\tFetchStoreNodeOfChildTableHandler fetchHandler = new FetchStoreNodeOfChildTableHandler();\r\n\t\t\t\t\t\t\treturn fetchHandler.execute(schema.getName(), findRootTBSql, tc.getRootParent().getDataNodes(), sc);\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\r\n\r\n\t\t\t\tFutures.addCallback(listenableFuture, new FutureCallback<String>() {\r\n\t\t\t\t\t@Override\r\n\t\t\t\t\tpublic void onSuccess(String result) {\r\n\t\t\t\t\t\t//结果为空，证明上一级表中不存在那条记录，失败\r\n\t\t\t\t\t\tif (Strings.isNullOrEmpty(result)) {\r\n\t\t\t\t\t\t\tStringBuilder s = new StringBuilder();\r\n\t\t\t\t\t\t\tLOGGER.warn(s.append(sc.getSession2()).append(origSQL).toString() +\r\n\t\t\t\t\t\t\t\t\t\" err:\" + \"can't find (root) parent sharding node for sql:\" + origSQL);\r\n\t\t\t\t\t\t\tif(!sc.isAutocommit()) { // 处于事务下失败, 必须回滚\r\n\t\t\t\t\t\t\t\tsc.setTxInterrupt(\"can't find (root) parent sharding node for sql:\" + origSQL);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tsc.writeErrMessage(ErrorCode.ER_PARSE_ERROR, \"can't find (root) parent sharding node for sql:\" + origSQL);\r\n\t\t\t\t\t\t\treturn;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tif (LOGGER.isDebugEnabled()) {\r\n\t\t\t\t\t\t\tLOGGER.debug(\"found partion node for child table to insert \" + result + \" sql :\" + origSQL);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\t//找到分片，进行插入（和其他的一样，需要判断是否需要全局自增ID）\r\n\t\t\t\t\t\tboolean processedInsert=false;\r\n\t\t\t\t\t\tif ( sc!=null && tc.isAutoIncrement()) {\r\n\t\t\t\t\t\t\ttry {\r\n\t\t\t\t\t\t\t\tString primaryKey = tc.getPrimaryKey();\r\n\t\t\t\t\t\t\t\tprocessedInsert=processInsert(sc,schema,ServerParse.INSERT,origSQL,tc.getName(),primaryKey);\r\n\t\t\t\t\t\t\t} catch (SQLNonTransientException e) {\r\n\t\t\t\t\t\t\t\tLOGGER.warn(\"sequence processInsert error,\",e);\r\n\t\t\t\t\t\t\t\tsc.writeErrMessage(ErrorCode.ER_PARSE_ERROR , \"sequence processInsert error,\" + e.getMessage());\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif(processedInsert==false){\r\n\t\t\t\t\t\t\tRouteResultset executeRrs = RouterUtil.routeToSingleNode(rrs, result, origSQL);\r\n\t\t\t\t\t\t\tsc.getSession2().execute(executeRrs, ServerParse.INSERT);\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t@Override\r\n\t\t\t\t\tpublic void onFailure(Throwable t) {\r\n\t\t\t\t\t\tStringBuilder s = new StringBuilder();\r\n\t\t\t\t\t\tLOGGER.warn(s.append(sc.getSession2()).append(origSQL).toString() +\r\n\t\t\t\t\t\t\t\t\" err:\" + t.getMessage());\r\n\t\t\t\t\t\tsc.writeErrMessage(ErrorCode.ER_PARSE_ERROR, t.getMessage() + \" \" + s.toString());\r\n\t\t\t\t\t}\r\n\t\t\t\t}, MycatServer.getInstance().\r\n\t\t\t\t\t\tgetListeningExecutorService());\r\n\r\n\t\t\t} else if(erFlag) {\r\n\t\t\t\tthrow new SQLNonTransientException(String.format(\"%s包含不是ER分片的表\", origSQL));\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\treturn erFlag;\r\n\t}\r\n\r\n\t/**\r\n\t * 寻找joinKey的索引\r\n\t *\r\n\t * @param columns\r\n\t * @param joinKey\r\n\t * @return -1表示没找到，>=0表示找到了\r\n\t */\r\n\tprivate static int getJoinKeyIndex(List<SQLExpr> columns, String joinKey) {\r\n\t\tfor (int i = 0; i < columns.size(); i++) {\r\n\t\t\tString col = StringUtil.removeBackquote(columns.get(i).toString()).toUpperCase();\r\n\t\t\tif (col.equals(joinKey)) {\r\n\t\t\t\treturn i;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn -1;\r\n\t}\r\n\r\n\t/**\r\n\t * 是否为批量插入：insert into ...values (),()...或 insert into ...select.....\r\n\t *\r\n\t * @param insertStmt\r\n\t * @return\r\n\t */\r\n\tprivate static boolean isMultiInsert(MySqlInsertStatement insertStmt) {\r\n\t\treturn (insertStmt.getValuesList() != null && insertStmt.getValuesList().size() > 1)\r\n\t\t\t\t|| insertStmt.getQuery() != null;\r\n\t}\r\n    /**\r\n     * escape white spaces and get the real start position.\r\n     * @author kevin\r\n     * @param stmt  The sql statement.\r\n     * @param startPos  The initial start position.\r\n     * @return  int  The real start position.\r\n     */\r\n    private static int getStartPos(String stmt, int startPos) {\r\n        while (startPos < stmt.length()) {\r\n            if (!Character.isWhitespace(stmt.charAt(startPos))) {\r\n                break;\r\n            }\r\n            ++startPos;\r\n        }\r\n        return startPos;\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/server/NonBlockingSession.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server;\n\nimport java.nio.ByteBuffer;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.mysql.listener.SqlExecuteStage;\nimport io.mycat.backend.mysql.nio.handler.CommitNodeHandler;\nimport io.mycat.backend.mysql.nio.handler.KillConnectionHandler;\nimport io.mycat.backend.mysql.nio.handler.LockTablesHandler;\nimport io.mycat.backend.mysql.nio.handler.MiddlerResultHandler;\nimport io.mycat.backend.mysql.nio.handler.MultiNodeCoordinator;\nimport io.mycat.backend.mysql.nio.handler.MultiNodeQueryHandler;\nimport io.mycat.backend.mysql.nio.handler.RollbackNodeHandler;\nimport io.mycat.backend.mysql.nio.handler.RollbackReleaseHandler;\nimport io.mycat.backend.mysql.nio.handler.SingleNodeHandler;\nimport io.mycat.backend.mysql.nio.handler.UnLockTablesHandler;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.sqlcmd.SQLCmdConstant;\n\n/**\n * @author mycat\n * @author mycat\n */\npublic class NonBlockingSession implements Session {\n\n    public static final Logger LOGGER = LoggerFactory.getLogger(NonBlockingSession.class);\n\n    private final ServerConnection source;\n    //huangyiming add 避免出现jdk版本冲突\n    private final ConcurrentMap<RouteResultsetNode, BackendConnection> target;\n    // life-cycle: each sql execution\n    private volatile SingleNodeHandler singleNodeHandler;\n    private volatile MultiNodeQueryHandler multiNodeHandler;\n    private volatile RollbackNodeHandler rollbackHandler;\n    private final MultiNodeCoordinator multiNodeCoordinator;\n    private final CommitNodeHandler commitHandler;\n    private volatile String xaTXID;\n\n   //huangyiming\n  \tprivate  volatile boolean canClose = true;\n\n  \tprivate volatile MiddlerResultHandler  middlerResultHandler;\n    private boolean prepared;\n    private RouteResultset rrs;\n\n    public NonBlockingSession(ServerConnection source) {\n        this.source = source;\n        this.target = new ConcurrentHashMap<RouteResultsetNode, BackendConnection>(2, 0.75f);\n        multiNodeCoordinator = new MultiNodeCoordinator(this);\n        commitHandler = new CommitNodeHandler(this);\n    }\n\n    @Override\n    public ServerConnection getSource() {\n        return source;\n    }\n\n    @Override\n    public int getTargetCount() {\n        return target.size();\n    }\n\n    public Set<RouteResultsetNode> getTargetKeys() {\n        return target.keySet();\n    }\n\n    public BackendConnection getTarget(RouteResultsetNode key) {\n        return target.get(key);\n    }\n\n    public Map<RouteResultsetNode, BackendConnection> getTargetMap() {\n        return this.target;\n    }\n\n    public BackendConnection removeTarget(RouteResultsetNode key) {\n        return target.remove(key);\n    }\n\n    @Override\n    public void execute(RouteResultset rrs, int type) {\n        this.rrs = rrs;\n        // clear prev execute resources\n        clearHandlesResources();\n        if (LOGGER.isDebugEnabled()) {\n            StringBuilder s = new StringBuilder();\n            LOGGER.debug(s.append(source).append(rrs).toString() + \" rrs \");\n        }\n\n        // 检查路由结果是否为空\n        RouteResultsetNode[] nodes = rrs.getNodes();\n        if (nodes == null || nodes.length == 0 || nodes[0].getName() == null || nodes[0].getName().equals(\"\")) {\n            source.writeErrMessage(ErrorCode.ER_NO_DB_ERROR,\n                    \"No dataNode found ,please check tables defined in schema:\" + source.getSchema());\n            source.getListener().fireEvent(SqlExecuteStage.END);\n            return;\n        }\n        boolean autocommit = source.isAutocommit();\n        final int initCount = target.size();\n        if (nodes.length == 1) {\n            singleNodeHandler = new SingleNodeHandler(rrs, this);\n            if (this.isPrepared()) {\n                singleNodeHandler.setPrepared(true);\n            }\n\n            try {\n                if(initCount > 1){\n                    checkDistriTransaxAndExecute(rrs,1,autocommit);\n                }else{\n                    singleNodeHandler.execute();\n                }\n            } catch (Exception e) {\n                LOGGER.warn(new StringBuilder().append(source).append(rrs).toString(), e);\n                source.writeErrMessage(ErrorCode.ERR_HANDLE_DATA, e.toString());\n                source.getListener().fireEvent(SqlExecuteStage.END);\n            }\n\n        } else {\n\n            multiNodeHandler = new MultiNodeQueryHandler(type, rrs, autocommit, this);\n            if (this.isPrepared()) {\n                multiNodeHandler.setPrepared(true);\n            }\n            try {\n                if(((type == ServerParse.DELETE || type == ServerParse.INSERT || type == ServerParse.UPDATE) && !rrs.isGlobalTable() && nodes.length > 1)||initCount > 1) {\n                    checkDistriTransaxAndExecute(rrs,2,autocommit);\n                } else {\n                    multiNodeHandler.execute();\n                }\n            } catch (Exception e) {\n                LOGGER.warn(new StringBuilder().append(source).append(rrs).toString(), e);\n                source.writeErrMessage(ErrorCode.ERR_HANDLE_DATA, e.toString());\n                source.getListener().fireEvent(SqlExecuteStage.END);\n            }\n        }\n\n        if (this.isPrepared()) {\n            this.setPrepared(false);\n        }\n    }\n\n    private void checkDistriTransaxAndExecute(RouteResultset rrs, int type,boolean autocommit) throws Exception {\n        switch(MycatServer.getInstance().getConfig().getSystem().getHandleDistributedTransactions()) {\n            case 1:\n                source.writeErrMessage(ErrorCode.ER_NOT_ALLOWED_COMMAND, \"Distributed transaction is disabled!\");\n                if(!autocommit){\n                    source.setTxInterrupt(\"Distributed transaction is disabled!\");\n                }\n                break;\n            case 2:\n                LOGGER.warn(\"Distributed transaction detected! RRS:\" + rrs);\n                if(type == 1){\n                    singleNodeHandler.execute();\n                }\n                else{\n                    multiNodeHandler.execute();\n                }\n                break;\n            default:\n                if(type == 1){\n                    singleNodeHandler.execute();\n                }\n                else{\n                    multiNodeHandler.execute();\n                }\n        }\n    }\n\n    private void checkDistriTransaxAndExecute() {\n        if(!isALLGlobal()){\n            switch(MycatServer.getInstance().getConfig().getSystem().getHandleDistributedTransactions()) {\n                case 1:\n                    source.writeErrMessage(ErrorCode.ER_NOT_ALLOWED_COMMAND, \"Distributed transaction is disabled!Please rollback!\");\n                    source.setTxInterrupt(\"Distributed transaction is disabled!\");\n                    break;\n                case 2:\n                    multiNodeCoordinator.executeBatchNodeCmd(SQLCmdConstant.COMMIT_CMD);\n                    LOGGER.warn(\"Distributed transaction detected! Targets:\" + target);\n                    break;\n                default:\n                    multiNodeCoordinator.executeBatchNodeCmd(SQLCmdConstant.COMMIT_CMD);\n\n            }\n        } else {\n            multiNodeCoordinator.executeBatchNodeCmd(SQLCmdConstant.COMMIT_CMD);\n        }\n    }\n\n    public void commit() {\n        final int initCount = target.size();\n        if (initCount <= 0) {\n            ByteBuffer buffer = source.allocate();\n            buffer = source.writeToBuffer(OkPacket.OK, buffer);\n            source.write(buffer);\n            /* 1. 如果开启了 xa 事务 */\n            if(getXaTXID()!=null){\n\t\t\t\tsetXATXEnabled(false);\n\t\t\t}\n            /* 2. preAcStates 为true,事务结束后,需要设置为true。preAcStates 为ac上一个状态    */\n            if(source.isPreAcStates()&&!source.isAutocommit()){\n            \tsource.setAutocommit(true);\n            }\n            return;\n        } else if (initCount == 1) {\n        \t//huangyiming add 避免出现jdk版本冲突\n            // BackendConnection con = target.values().iterator().next();\n            commitHandler.commit();\n        } else {\n\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"multi node commit to send ,total \" + initCount);\n            }\n            checkDistriTransaxAndExecute();\n        }\n\n    }\n\n    private boolean isALLGlobal(){\n        for(RouteResultsetNode routeResultsetNode:target.keySet()){\n            if(routeResultsetNode.getSource()==null){\n                return false;\n            }\n            else if(!routeResultsetNode.getSource().isGlobalTable()){\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public void rollback() {\n        final int initCount = target.size();\n        if (initCount <= 0) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"no session bound connections found ,no need send rollback cmd \");\n            }\n            ByteBuffer buffer = source.allocate();\n            buffer = source.writeToBuffer(OkPacket.OK, buffer);\n            source.write(buffer);\n            /* 1. 如果开启了 xa 事务 */\n            if(getXaTXID()!=null){\n\t\t\t\tsetXATXEnabled(false);\n\t\t\t}\n            /* 2. preAcStates 为true,事务结束后,需要设置为true。preAcStates 为ac上一个状态    */\n            if(source.isPreAcStates()&&!source.isAutocommit()){\n            \tsource.setAutocommit(true);\n            }\n            return;\n        }\n\n        rollbackHandler = new RollbackNodeHandler(this);\n        rollbackHandler.rollback();\n    }\n\n\t/**\n\t * 执行lock tables语句方法\n\t * @author songdabin\n\t * @date 2016-7-9\n\t * @param rrs\n\t */\n\tpublic void lockTable(RouteResultset rrs) {\n\t\t// 检查路由结果是否为空\n\t\tRouteResultsetNode[] nodes = rrs.getNodes();\n\t\tif (nodes == null || nodes.length == 0 || nodes[0].getName() == null\n\t\t\t\t|| nodes[0].getName().equals(\"\")) {\n\t\t\tsource.writeErrMessage(ErrorCode.ER_NO_DB_ERROR,\n\t\t\t\t\t\"No dataNode found ,please check tables defined in schema:\"\n\t\t\t\t\t\t\t+ source.getSchema());\n\t\t\treturn;\n\t\t}\n\t\tLockTablesHandler handler = new LockTablesHandler(this, rrs);\n\t\tsource.setLocked(true);\n\t\ttry {\n\t\t\thandler.execute();\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.warn(new StringBuilder().append(source).append(rrs).toString(), e);\n\t\t\tsource.writeErrMessage(ErrorCode.ERR_HANDLE_DATA, e.toString());\n\t\t}\n\t}\n\n\t/**\n\t * 执行unlock tables语句方法\n\t * @author songdabin\n\t * @date 2016-7-9\n\t * @param sql\n\t */\n\tpublic void unLockTable(String sql) {\n\t\tUnLockTablesHandler handler = new UnLockTablesHandler(this, this.source.isAutocommit(), sql);\n\t\thandler.execute();\n\t}\n\n    @Override\n    public void cancel(FrontendConnection sponsor) {\n\n    }\n\n    /**\n     * {@link ServerConnection#isClosed()} must be true before invoking this\n     */\n    public void terminate() {\n        for (BackendConnection node : target.values()) {\n            node.close(\"client closed \");\n        }\n        target.clear();\n        clearHandlesResources();\n    }\n\n    public void closeAndClearResources(String reason) {\n        for (BackendConnection node : target.values()) {\n            node.closeWithoutRsp(reason);\n        }\n        target.clear();\n        clearHandlesResources();\n    }\n\n    public void closeConnection(BackendConnection con, String reason) {\n        Iterator<Entry<RouteResultsetNode, BackendConnection>> itor = target.entrySet().iterator();\n        while (itor.hasNext()) {\n            BackendConnection theCon = itor.next().getValue();\n            if (theCon == con) {\n                itor.remove();\n                con.close(reason);\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(\"realse connection \" + con);\n                }\n                break;\n            }\n        }\n\n    }\n\n    public void releaseConnectionIfSafe(BackendConnection conn, boolean debug,\n                                        boolean needRollback) {\n        RouteResultsetNode node = (RouteResultsetNode) conn.getAttachment();\n\n        if (node != null) {\n        \t/*  分表 在\n        \t *    1. 没有开启事务\n        \t *    2. 读取走的从节点\n        \t *    3. 没有执行过更新sql\n        \t *    也需要释放连接\n        \t */\n//            if (node.isDisctTable()) {\n//                return;\n//            }\n            if (MycatServer.getInstance().getConfig().getSystem().isStrictTxIsolation()) {\n                // 如果是严格隔离级别模式的话,不考虑是否已经执行了modifiedSql,直接不释放连接\n                if ((!this.source.isAutocommit() && !conn.isFromSlaveDB()) || this.source.isLocked()) {\n                    return;\n                }\n            }\n            if ((this.source.isAutocommit() || conn.isFromSlaveDB()\n                    || !conn.isModifiedSQLExecuted()) && !this.source.isLocked()) {\n                releaseConnection((RouteResultsetNode) conn.getAttachment(), LOGGER.isDebugEnabled(),\n                        needRollback);\n            }\n        }\n    }\n\n    public void releaseConnection(RouteResultsetNode rrn, boolean debug,\n                                  final boolean needRollback) {\n\n        BackendConnection c = target.remove(rrn);\n        if (c != null) {\n            if (debug) {\n                //LOGGER.debug(\"release connection \" + c);\n                String sql =  rrn.getStatement();\n                if(sql!=null){\n                    sql = sql.replaceAll(\"[\\r\\n]+\", \"\");\n                }\n                LOGGER.debug(\"releaseConnection Connection@{} [id={}] for node={}, sql={}\",\n                    new Object[]{c.hashCode(), c.getId(), rrn.getName(), sql});\n            }\n            if (c.getAttachment() != null) {\n                c.setAttachment(null);\n            }\n            if (!c.isClosedOrQuit()) {\n                if (c.isAutocommit()) {\n                    c.release();\n                } else\n                //if (needRollback)\n                {\n                    c.setResponseHandler(new RollbackReleaseHandler());\n                    c.rollback();\n                }\n                //else {\n\t\t\t\t//\tc.release();\n\t\t\t\t//}\n            }\n        }\n    }\n\n    public void releaseConnections(final boolean needRollback) {\n        boolean debug = LOGGER.isDebugEnabled();\n\n        for (RouteResultsetNode rrn : target.keySet()) {\n            releaseConnection(rrn, debug, needRollback);\n        }\n    }\n\n    public void releaseConnection(BackendConnection con) {\n        Iterator<Entry<RouteResultsetNode, BackendConnection>> itor = target\n                .entrySet().iterator();\n        while (itor.hasNext()) {\n            BackendConnection theCon = itor.next().getValue();\n            if (theCon == con) {\n                itor.remove();\n                con.release();\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(\"realse connection \" + con);\n                }\n                break;\n            }\n        }\n\n    }\n\n    /**\n     * @return previous bound connection\n     */\n    public BackendConnection bindConnection(RouteResultsetNode key,\n                                            BackendConnection conn) {\n        // System.out.println(\"bind connection \"+conn+\n        // \" to key \"+key.getName()+\" on sesion \"+this);\n        if(LOGGER.isDebugEnabled()){\n            String sql =  key.getStatement();\n            if(sql!=null){\n                sql = sql.replaceAll(\"[\\r\\n]+\", \"\");\n            }\n            LOGGER.debug(\"bindConnection Connection@{} [id={}] for node={}, sql={}\",\n                new Object[]{conn.hashCode(), conn.getId(), key.getName(), sql});\n        }\n\n        return target.put(key, conn);\n    }\n\n    public boolean tryExistsCon(final BackendConnection conn, RouteResultsetNode node) {\n        if (conn == null) {\n            return false;\n        }\n\n        boolean canReUse = false;\n        // conn 是 slave db 的，并且 路由结果显示，本次sql可以重用该 conn\n        if (conn.isFromSlaveDB() && (node.canRunnINReadDB(getSource().isAutocommit())\n                && (node.getRunOnSlave() == null || node.getRunOnSlave()))) {\n            canReUse = true;\n        }\n\n        // conn 是 master db 的，并且路由结果显示，本次sql可以重用该conn\n        if (!conn.isFromSlaveDB() && (node.getRunOnSlave() == null || !node.getRunOnSlave())) {\n            canReUse = true;\n        }\n\n        if (canReUse) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"found connections in session to use \" + conn\n                        + \" for \" + node);\n            }\n            conn.setAttachment(node);\n            return true;\n        } else {\n            // Previous connection and can't use anymore ,release it\n            if (LOGGER.isWarnEnabled()) {\n                LOGGER.warn(\"Release previous connection,can't be used in trasaction  \"\n                        + conn + \" for \" + node);\n            }\n            releaseConnection(node, LOGGER.isDebugEnabled(), false);\n        }\n        return false;\n    }\n\n//\tpublic boolean tryExistsCon(final BackendConnection conn,\n//\t\t\tRouteResultsetNode node) {\n//\n//\t\tif (conn == null) {\n//\t\t\treturn false;\n//\t\t}\n//\t\tif (!conn.isFromSlaveDB()\n//\t\t\t\t|| node.canRunnINReadDB(getSource().isAutocommit())) {\n//\t\t\tif (LOGGER.isDebugEnabled()) {\n//\t\t\t\tLOGGER.debug(\"found connections in session to use \" + conn\n//\t\t\t\t\t\t+ \" for \" + node);\n//\t\t\t}\n//\t\t\tconn.setAttachment(node);\n//\t\t\treturn true;\n//\t\t} else {\n//\t\t\t// slavedb connection and can't use anymore ,release it\n//\t\t\tif (LOGGER.isDebugEnabled()) {\n//\t\t\t\tLOGGER.debug(\"release slave connection,can't be used in trasaction  \"\n//\t\t\t\t\t\t+ conn + \" for \" + node);\n//\t\t\t}\n//\t\t\treleaseConnection(node, LOGGER.isDebugEnabled(), false);\n//\t\t}\n//\t\treturn false;\n//\t}\n\n    protected void kill() {\n        boolean hooked = false;\n        AtomicInteger count = null;\n        Map<RouteResultsetNode, BackendConnection> killees = null;\n        for (RouteResultsetNode node : target.keySet()) {\n            BackendConnection c = target.get(node);\n            if (c != null) {\n                if (!hooked) {\n                    hooked = true;\n                    killees = new HashMap<RouteResultsetNode, BackendConnection>();\n                    count = new AtomicInteger(0);\n                }\n                killees.put(node, c);\n                count.incrementAndGet();\n            }\n        }\n        if (hooked) {\n            for (Entry<RouteResultsetNode, BackendConnection> en : killees\n                    .entrySet()) {\n                KillConnectionHandler kill = new KillConnectionHandler(\n                        en.getValue(), this);\n                MycatConfig conf = MycatServer.getInstance().getConfig();\n                PhysicalDBNode dn = conf.getDataNodes().get(\n                        en.getKey().getName());\n                try {\n                    dn.getConnectionFromSameSource(null, true, en.getValue(),\n                            kill, en.getKey());\n                } catch (Exception e) {\n                    LOGGER.error(\n                            \"get killer connection failed for \" + en.getKey(),\n                            e);\n                    kill.connectionError(e, null);\n                }\n            }\n        }\n    }\n\n    private void clearHandlesResources() {\n        SingleNodeHandler singleHander = singleNodeHandler;\n        if (singleHander != null) {\n            singleHander.clearResources();\n            singleNodeHandler = null;\n        }\n        MultiNodeQueryHandler multiHandler = multiNodeHandler;\n        if (multiHandler != null) {\n            multiHandler.clearResources();\n            multiNodeHandler = null;\n        }\n    }\n\n    public void clearResources(final boolean needRollback) {\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"clear session resources \" + this);\n        }\n        this.releaseConnections(needRollback);\n        clearHandlesResources();\n    }\n\n    public boolean closed() {\n        return source.isClosed();\n    }\n\n    private String genXATXID() {\n        return MycatServer.getInstance().getXATXIDGLOBAL();\n    }\n\n    public void setXATXEnabled(boolean xaTXEnabled) {\n\n        if (xaTXEnabled) {\n        \tLOGGER.info(\"XA Transaction enabled ,con \" + this.getSource());\n        \tif(this.xaTXID == null){\n        \t\txaTXID = genXATXID();\n        \t}\n        }else{\n        \tLOGGER.info(\"XA Transaction disabled ,con \" + this.getSource());\n        \tthis.xaTXID = null;\n        }\n    }\n\n    public String getXaTXID() {\n        return xaTXID;\n    }\n\n    public boolean isPrepared() {\n        return prepared;\n    }\n\n    public void setPrepared(boolean prepared) {\n        this.prepared = prepared;\n    }\n\n\n\tpublic boolean isCanClose() {\n\t\treturn canClose;\n\t}\n\n\tpublic void setCanClose(boolean canClose) {\n\t\tthis.canClose = canClose;\n\t}\n\n\tpublic MiddlerResultHandler getMiddlerResultHandler() {\n\t\treturn middlerResultHandler;\n\t}\n\n\tpublic void setMiddlerResultHandler(MiddlerResultHandler middlerResultHandler) {\n\t\tthis.middlerResultHandler = middlerResultHandler;\n\t}\n\n    public void setAutoCommitStatus() {\n\t\t/* 1.  事务结束后,xa事务结束    */\n\t\tif(this.getXaTXID()!=null){\n\t\t\tthis.setXATXEnabled(false);\n\t\t}\n\t\t/* 2. preAcStates 为true,事务结束后,需要设置为true。preAcStates 为ac上一个状态    */\n\t\tif(this.getSource().isPreAcStates()&&!this.getSource().isAutocommit()){\n\t\t\tthis.getSource().setAutocommit(true);\n        }\n\t\tthis.getSource().clearTxInterrupt();\n\n    }\n\t@Override\n\tpublic String toString() {\n\t\t// TODO Auto-generated method stub\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (BackendConnection backCon : target.values()) {\n\t\t\tsb.append(backCon).append(\"\\r\\n\");\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n    public RouteResultset getRrs() {\n        return rrs;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/ServerConnection.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server;\n\nimport java.io.IOException;\nimport java.nio.channels.NetworkChannel;\nimport java.util.Queue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.listener.DefaultSqlExecuteStageListener;\nimport io.mycat.backend.mysql.listener.SqlExecuteStageListener;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.server.handler.MysqlProcHandler;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.response.Heartbeat;\nimport io.mycat.server.response.InformationSchemaProfiling;\nimport io.mycat.server.response.InformationSchemaProfilingSqlyog;\nimport io.mycat.server.response.Ping;\nimport io.mycat.server.util.SchemaUtil;\nimport io.mycat.util.SplitUtil;\nimport io.mycat.util.TimeUtil;\n\n/**\n * @author mycat\n */\npublic class ServerConnection extends FrontendConnection {\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(ServerConnection.class);\n\tprivate long authTimeout = SystemConfig.DEFAULT_AUTH_TIMEOUT;\n\n\t/** 保存SET SQL_SELECT_LIMIT的值, default 解析为-1. */\n\tprivate volatile  int sqlSelectLimit = -1;\n\tprivate volatile  boolean txReadonly;\n\tprivate volatile int txIsolation;\n\tprivate volatile boolean autocommit;\n\tprivate volatile boolean preAcStates; //上一个ac状态,默认为true\n\tprivate volatile boolean txInterrupted;\n\tprivate volatile String txInterrputMsg = \"\";\n\tprivate long lastInsertId;\n\tprivate NonBlockingSession session;\n\t/**\n\t * 标志是否执行了lock tables语句，并处于lock状态\n\t */\n\tprivate volatile boolean isLocked = false;\n    private Queue<SqlEntry> executeSqlQueue;\n    private SqlExecuteStageListener listener;\n\t\n\tpublic ServerConnection(NetworkChannel channel)\n\t\t\tthrows IOException {\n\t\tsuper(channel);\n\t\tthis.txInterrupted = false;\n\t\tthis.autocommit = true;\n\t\tthis.preAcStates = true;\n\t\tthis.txReadonly = false;\n        this.executeSqlQueue = new LinkedBlockingQueue<>();\n        this.listener = new DefaultSqlExecuteStageListener(this);\n\t}\n\n\t@Override\n\tpublic boolean isIdleTimeout() {\n\t\tif (isAuthenticated) {\n\t\t\treturn super.isIdleTimeout();\n\t\t} else {\n\t\t\treturn TimeUtil.currentTimeMillis() > Math.max(lastWriteTime, lastReadTime) + this.authTimeout;\n\t\t}\n\t}\n\n\tpublic long getAuthTimeout() {\n\t\treturn authTimeout;\n\t}\n\n\tpublic void setAuthTimeout(long authTimeout) {\n\t\tthis.authTimeout = authTimeout;\n\t}\n\n\tpublic int getTxIsolation() {\n\t\treturn txIsolation;\n\t}\n\n\tpublic void setTxIsolation(int txIsolation) {\n\t\tthis.txIsolation = txIsolation;\n\t}\n\n\tpublic boolean isAutocommit() {\n\t\treturn autocommit;\n\t}\n\n\tpublic void setAutocommit(boolean autocommit) {\n\t\tthis.autocommit = autocommit;\n\t}\n\n\tpublic boolean isTxReadonly() {\n\t\treturn txReadonly;\n\t}\n\n\tpublic void setTxReadonly(boolean txReadonly) {\n\t\tthis.txReadonly = txReadonly;\n\t}\n\n\tpublic int getSqlSelectLimit() {\n\t\treturn sqlSelectLimit;\n\t}\n\n\tpublic void setSqlSelectLimit(int sqlSelectLimit) {\n\t\tthis.sqlSelectLimit = sqlSelectLimit;\n\t}\n\n\tpublic long getLastInsertId() {\n\t\treturn lastInsertId;\n\t}\n\n\tpublic void setLastInsertId(long lastInsertId) {\n\t\tthis.lastInsertId = lastInsertId;\n\t}\n\n\t/**\n\t * 设置是否需要中断当前事务\n\t */\n\tpublic void setTxInterrupt(String txInterrputMsg) {\n\t\tif (!autocommit && !txInterrupted) {\n\t\t\ttxInterrupted = true;\n\t\t\tthis.txInterrputMsg = txInterrputMsg;\n\t\t}\n\t}\n\t\n\t/**\n\t * \n\t * 清空食事务中断\n\t * */\n\tpublic void clearTxInterrupt() {\n\t\tif (!autocommit && txInterrupted) {\n\t\t\ttxInterrupted = false;\n\t\t\tthis.txInterrputMsg = \"\";\n\t\t}\n\t}\n\t\n\tpublic boolean isTxInterrupted()\n\t{\n\t\treturn txInterrupted;\n\t}\n\tpublic NonBlockingSession getSession2() {\n\t\treturn session;\n\t}\n\n\tpublic void setSession2(NonBlockingSession session2) {\n\t\tthis.session = session2;\n\t}\n\t\n\tpublic boolean isLocked() {\n\t\treturn isLocked;\n\t}\n\n\tpublic void setLocked(boolean isLocked) {\n\t\tthis.isLocked = isLocked;\n\t}\n\n\t@Override\n\tpublic void ping() {\n\t\tPing.response(this);\n\t}\n\n\t@Override\n\tpublic void heartbeat(byte[] data) {\n\t\tHeartbeat.response(this, data);\n\t}\n\n    public void execute(String sql, int type) {\n\t\t//连接状态检查\n\t\tif (this.isClosed()) {\n\t\t\tLOGGER.warn(\"ignore execute ,server connection is closed \" + this);\n\t\t\treturn;\n\t\t}\n\t\t// 事务状态检查\n\t\tif (txInterrupted) {\n\t\t\twriteErrMessage(ErrorCode.ER_YES,\n\t\t\t\t\t\"Transaction error, need to rollback.\" + txInterrputMsg);\n\t\t\treturn;\n\t\t}\n\n\t\t// 检查当前使用的DB\n\t\tString db = this.schema;\n\t\tboolean isDefault = true;\n\t\tif (db == null) {\n\t\t\tdb = SchemaUtil.detectDefaultDb(sql, type);\n\t\t\tif (db == null) {\n\t\t\t\tdb = MycatServer.getInstance().getConfig().getUsers().get(user).getDefaultSchema();\n\t\t\t\tif (db == null) {\n\t\t\t\t\twriteErrMessage(ErrorCode.ERR_BAD_LOGICDB,\n\t\t\t\t\t\t\t\"No MyCAT Database selected\");\n\t\t\t\t\treturn ;\n\t\t\t\t}\n\t\t\t}\n\t\t\tisDefault = false;\n\t\t}\n\t\t\n\t\t// 兼容PhpAdmin's, 支持对MySQL元数据的模拟返回\n\t\t//// TODO: 2016/5/20 支持更多information_schema特性\n//\t\tif (ServerParse.SELECT == type\n//\t\t\t\t&& db.equalsIgnoreCase(\"information_schema\") ) {\n//\t\t\tMysqlInformationSchemaHandler.handle(sql, this);\n//\t\t\treturn;\n//\t\t}\n\n\t\tif (ServerParse.SELECT == type \n\t\t\t\t&& sql.contains(\"mysql\") \n\t\t\t\t&& sql.contains(\"proc\")) {\n\t\t\t\n\t\t\tSchemaUtil.SchemaInfo schemaInfo = SchemaUtil.parseSchema(sql);\n\t\t\tif (schemaInfo != null \n\t\t\t\t\t&& \"mysql\".equalsIgnoreCase(schemaInfo.schema)\n\t\t\t\t\t&& \"proc\".equalsIgnoreCase(schemaInfo.table)) {\n\t\t\t\t\n\t\t\t\t// 兼容MySQLWorkbench\n\t\t\t\tMysqlProcHandler.handle(sql, this);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t\n\t\tSchemaConfig schema = MycatServer.getInstance().getConfig().getSchemas().get(db);\n\t\tif (schema == null) {\n\t\t\twriteErrMessage(ErrorCode.ERR_BAD_LOGICDB,\n\t\t\t\t\t\"Unknown MyCAT Database '\" + db + \"'\");\n\t\t\treturn;\n\t\t}\n\n\t\t//fix navicat   SELECT STATE AS `State`, ROUND(SUM(DURATION),7) AS `Duration`, CONCAT(ROUND(SUM(DURATION)/*100,3), '%') AS `Percentage` FROM INFORMATION_SCHEMA.PROFILING WHERE QUERY_ID= GROUP BY STATE ORDER BY SEQ\n\t\tif(ServerParse.SELECT == type &&sql.contains(\" INFORMATION_SCHEMA.PROFILING \")&&sql.contains(\"CONCAT(ROUND(SUM(DURATION)/\"))\n\t\t{\n\t\t\tInformationSchemaProfiling.response(this);\n\t\t\treturn;\n\t\t}\n\n\t\t//fix sqlyog select state, round(sum(duration),5) as `duration (summed) in sec` from information_schema.profiling where query_id = 0 group by state order by `duration (summed) in sec` desc\n\t\tif(ServerParse.SELECT == type &&sql.contains(\" information_schema.profiling \")&&sql.contains(\"duration (summed) in sec\"))\n\t\t{\n\t\t\tInformationSchemaProfilingSqlyog.response(this);\n\t\t\treturn;\n\t\t}\n\t\t/* 当已经设置默认schema时，可以通过在sql中指定其它schema的方式执行\n\t\t * 相关sql，已经在mysql客户端中验证。\n\t\t * 所以在此处增加关于sql中指定Schema方式的支持。\n\t\t */\n\t\tif (isDefault && schema.isCheckSQLSchema() && isNormalSql(type)) {\n\t\t\tSchemaUtil.SchemaInfo schemaInfo = SchemaUtil.parseSchema(sql);\n\t\t\tif (schemaInfo != null && schemaInfo.schema != null && !schemaInfo.schema.equals(db)) {\n\t\t\t\tSchemaConfig schemaConfig = MycatServer.getInstance().getConfig().getSchemas().get(schemaInfo.schema);\n\t\t\t\tif (schemaConfig != null)\n\t\t\t\t\tschema = schemaConfig;\n\t\t\t}\n\t\t}\n\n\t\trouteEndExecuteSQL(sql, type, schema);\n\n\t}\n\t\n\tprivate boolean isNormalSql(int type) {\n\t\treturn ServerParse.SELECT==type||ServerParse.INSERT==type||ServerParse.UPDATE==type||ServerParse.DELETE==type||ServerParse.DDL==type;\n\t}\n\n    public RouteResultset routeSQL(String sql, int type) {\n\n\t\t// 检查当前使用的DB\n\t\tString db = this.schema;\n\t\tif (db == null) {\n\t\t\tdb = SchemaUtil.detectDefaultDb(sql, type);\n\t\t\tif (db == null){\n\t\t\t\tdb = MycatServer.getInstance().getConfig().getUsers().get(user).getDefaultSchema();\n\t\t\t\tif (db == null) {\n\t\t\t\t\twriteErrMessage(ErrorCode.ERR_BAD_LOGICDB,\n\t\t\t\t\t\t\t\"No MyCAT Database selected\");\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\tSchemaConfig schema = MycatServer.getInstance().getConfig()\n\t\t\t\t.getSchemas().get(db);\n\t\tif (schema == null) {\n\t\t\twriteErrMessage(ErrorCode.ERR_BAD_LOGICDB,\n\t\t\t\t\t\"Unknown MyCAT Database '\" + db + \"'\");\n\t\t\treturn null;\n\t\t}\n\n\t\t// 路由计算\n\t\tRouteResultset rrs = null;\n\t\ttry {\n\t\t\trrs = MycatServer\n\t\t\t\t\t.getInstance()\n\t\t\t\t\t.getRouterservice()\n\t\t\t\t\t.route(MycatServer.getInstance().getConfig().getSystem(),\n\t\t\t\t\t\t\tschema, type, sql, this.charset, this);\n\n\t\t} catch (Exception e) {\n\t\t\tStringBuilder s = new StringBuilder();\n\t\t\tLOGGER.warn(s.append(this).append(sql).toString() + \" err:\" + e.toString(),e);\n\t\t\tString msg = e.getMessage();\n\t\t\twriteErrMessage(ErrorCode.ER_PARSE_ERROR, msg == null ? e.getClass().getSimpleName() : msg);\n\t\t\treturn null;\n\t\t}\n\t\treturn rrs;\n\t}\n\n\n\n\n\tpublic void routeEndExecuteSQL(String sql, final int type, final SchemaConfig schema) {\n\t\t// 路由计算\n\t\tRouteResultset rrs = null;\n\t\ttry {\n\t\t\trrs = MycatServer\n\t\t\t\t\t.getInstance()\n\t\t\t\t\t.getRouterservice()\n\t\t\t\t\t.route(MycatServer.getInstance().getConfig().getSystem(),\n\t\t\t\t\t\t\tschema, type, sql, this.charset, this);\n\n\t\t} catch (Exception e) {\n\t\t\tStringBuilder s = new StringBuilder();\n\t\t\tLOGGER.warn(s.append(this).append(sql).toString() + \" err:\" + e.toString(),e);\n\t\t\tString msg = e.getMessage();\n\t\t\twriteErrMessage(ErrorCode.ER_PARSE_ERROR, msg == null ? e.getClass().getSimpleName() : msg);\n\t\t\treturn;\n\t\t}\n\t\tif (rrs != null) {\n            // #支持mariadb驱动useBatchMultiSend=true,连续接收到的sql先放入队列，等待前面处理完成后再继续处理。\n            // 参考https://mariadb.com/kb/en/option-batchmultisend-description/\n            boolean executeNow = false;\n            synchronized (this.executeSqlQueue) {\n                executeNow = this.executeSqlQueue.isEmpty();\n                this.executeSqlQueue.add(new SqlEntry(sql, type, rrs));\n                if (LOGGER.isDebugEnabled()) {\n                    LOGGER.debug(\"add queue,executeSqlQueue size {}\", executeSqlQueue.size());\n                }\n            }\n\n            if (executeNow) {\n                this.executeSqlId++;\n                session.execute(rrs, rrs.isSelectForUpdate() ? ServerParse.UPDATE : type);\n            }\n        }\n    }\n\n\t/**\n\t * 提交事务\n\t */\n\tpublic void commit() {\n\t\tif (txInterrupted) {\n\t\t\tLOGGER.warn(\"receive commit ,but found err message in Transaction {}\",this);\n\t\t\tthis.rollback();\n//\t\t\twriteErrMessage(ErrorCode.ER_YES,\n//\t\t\t\t\t\"Transaction error, need to rollback.\");\n\t\t} else {\n\t\t\tsession.commit();\n\t\t}\n\t}\n\n\t/**\n\t * 回滚事务\n\t */\n\tpublic void rollback() {\n\t\t// 状态检查\n\t\tif (txInterrupted) {\n\t\t\ttxInterrupted = false;\n\t\t}\n\n\t\t// 执行回滚\n\t\tsession.rollback();\n\t}\n\t/**\n\t * 执行lock tables语句方法\n\t * @param sql\n\t */\n\tpublic void lockTable(String sql) {\n\t\t// 事务中不允许执行lock table语句\n\t\tif (!autocommit) {\n\t\t\twriteErrMessage(ErrorCode.ER_YES, \"can't lock table in transaction!\");\n\t\t\treturn;\n\t\t}\n\t\t// 已经执行了lock table且未执行unlock table之前的连接不能再次执行lock table命令\n\t\tif (isLocked) {\n\t\t\twriteErrMessage(ErrorCode.ER_YES, \"can't lock multi-table\");\n\t\t\treturn;\n\t\t}\n\t\tRouteResultset rrs = routeSQL(sql, ServerParse.LOCK);\n\t\tif (rrs != null) {\n\t\t\tsession.lockTable(rrs);\n\t\t}\n\t}\n\t\n\t/**\n\t * 执行unlock tables语句方法\n\t * @param sql\n\t */\n\tpublic void unLockTable(String sql) {\n\t\tsql = sql.replaceAll(\"\\n\", \" \").replaceAll(\"\\t\", \" \");\n\t\tString[] words = SplitUtil.split(sql, ' ', true);\n\t\tif (words.length==2 && (\"table\".equalsIgnoreCase(words[1]) || \"tables\".equalsIgnoreCase(words[1]))) {\n\t\t\tisLocked = false;\n\t\t\tsession.unLockTable(sql);\n\t\t} else {\n\t\t\twriteErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"Unknown command\");\n\t\t}\n\t\t\n\t}\n\n\t/**\n\t * 撤销执行中的语句\n\t * \n\t * @param sponsor\n\t *            发起者为null表示是自己\n\t */\n\tpublic void cancel(final FrontendConnection sponsor) {\n\t\tprocessor.getExecutor().execute(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tsession.cancel(sponsor);\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void close(String reason) {\n\t\tsuper.close(reason);\n\t\tsession.terminate();\n\t\tif(getLoadDataInfileHandler()!=null)\n\t\t{\n\t\t\tgetLoadDataInfileHandler().clear();\n\t\t}\n\t}\n\n\t/**\n\t * add huangyiming 检测字符串中某字符串出现次数\n\t * @param srcText\n\t * @param findText\n\t * @return\n\t */\n\tpublic static int appearNumber(String srcText, String findText) {\n\t    int count = 0;\n\t    Pattern p = Pattern.compile(findText);\n\t    Matcher m = p.matcher(srcText);\n\t    while (m.find()) {\n\t        count++;\n\t    }\n\t    return count;\n\t}\n\t@Override\n\tpublic String toString() {\n\t\t\n\t\treturn \"ServerConnection [id=\" + id + \", schema=\" + schema + \", host=\"\n\t\t\t\t+ host + \", user=\" + user + \",txIsolation=\" + txIsolation\n\t\t\t\t+ \", autocommit=\" + autocommit + \", schema=\" + schema+ \", executeSql=\" + executeSql + \"]\" +\n\t\t\t\tthis.getSession2();\n\t\t\n\t}\n\n\tpublic boolean isPreAcStates() {\n\t\treturn preAcStates;\n\t}\n\n\tpublic void setPreAcStates(boolean preAcStates) {\n\t\tthis.preAcStates = preAcStates;\n\t}\n\n    public SqlExecuteStageListener getListener() {\n        return listener;\n    }\n\n    public void setListener(SqlExecuteStageListener listener) {\n        this.listener = listener;\n    }\n\n    @Override\n    public void checkQueueFlow() {\n        RouteResultset rrs = session.getRrs();\n        if (rrs != null && rrs.getNodes().length > 1 && session.getRrs().needMerge()) {\n            // 多节点合并结果集语句需要拉取所有数据，无法流控\n            return;\n        } else {\n            // 非合并结果集语句进行流量控制检查。\n            flowController.check(session.getTargetMap());\n        }\n    }\n\n    @Override\n    public void resetConnection() {\n        // 1 简单点直接关闭后端连接。若按照mysql官方的提交事务或回滚事务，mycat都会回包给应用，引发包乱序。\n        session.closeAndClearResources(\"receive com_reset_connection\");\n\n        // 2 重置用户变量\n        this.txInterrupted = false;\n        this.autocommit = true;\n        this.preAcStates = true;\n        this.txReadonly = false;\n        this.lastInsertId = 0;\n\n        super.resetConnection();\n    }\n    /**\n     * sql执行完成后回调函数\n     */\n    public void onEventSqlCompleted() {\n        SqlEntry sqlEntry = null;\n        synchronized (this.executeSqlQueue) {\n            this.executeSqlQueue.poll();// 弹出已经执行成功的\n            sqlEntry = this.executeSqlQueue.peek();\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"poll queue,executeSqlQueue size {}\", this.executeSqlQueue.size());\n            }\n        }\n        if (sqlEntry != null) {\n            this.executeSqlId++;\n            session.execute(sqlEntry.rrs, sqlEntry.rrs.isSelectForUpdate() ? ServerParse.UPDATE : sqlEntry.type);\n        }\n    }\n\n    private class SqlEntry {\n        public String sql;\n        public int type;\n        public RouteResultset rrs;\n\n        public SqlEntry(String sql, int type, RouteResultset rrs) {\n            this.sql = sql;\n            this.type = type;\n            this.rrs = rrs;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/ServerConnectionFactory.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server;\n\nimport java.io.IOException;\nimport java.nio.channels.NetworkChannel;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.MycatPrivileges;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.net.factory.FrontendConnectionFactory;\nimport io.mycat.server.handler.ServerLoadDataInfileHandler;\nimport io.mycat.server.handler.ServerPrepareHandler;\n\n/**\n * @author mycat\n */\npublic class ServerConnectionFactory extends FrontendConnectionFactory {\n\n    @Override\n    protected FrontendConnection getConnection(NetworkChannel channel) throws IOException {\n        SystemConfig sys = MycatServer.getInstance().getConfig().getSystem();\n        ServerConnection c = new ServerConnection(channel);\n        MycatServer.getInstance().getConfig().setSocketParams(c, true);\n        c.setAuthTimeout(sys.getAuthTimeout());\n        c.setPrivileges(MycatPrivileges.instance());\n        c.setQueryHandler(new ServerQueryHandler(c));\n        c.setLoadDataInfileHandler(new ServerLoadDataInfileHandler(c));\n        c.setPrepareHandler(new ServerPrepareHandler(c,sys.getMaxPreparedStmtCount()));\n        c.setTxIsolation(sys.getTxIsolation());\n        c.setSession2(new NonBlockingSession(c));\n        return c;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/ServerQueryHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.net.handler.FrontendQueryHandler;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.route.RouteService;\nimport io.mycat.server.handler.BeginHandler;\nimport io.mycat.server.handler.CommandHandler;\nimport io.mycat.server.handler.Explain2Handler;\nimport io.mycat.server.handler.ExplainHandler;\nimport io.mycat.server.handler.KillHandler;\nimport io.mycat.server.handler.MigrateHandler;\nimport io.mycat.server.handler.SavepointHandler;\nimport io.mycat.server.handler.SelectHandler;\nimport io.mycat.server.handler.SetHandler;\nimport io.mycat.server.handler.ShowHandler;\nimport io.mycat.server.handler.StartHandler;\nimport io.mycat.server.handler.UseHandler;\nimport io.mycat.server.parser.ServerParse;\n\n/**\n * @author mycat\n */\npublic class ServerQueryHandler implements FrontendQueryHandler {\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t.getLogger(ServerQueryHandler.class);\n\n\tprivate final ServerConnection source;\n\tprotected Boolean readOnly;\n\n\tpublic void setReadOnly(Boolean readOnly) {\n\t\tthis.readOnly = readOnly;\n\t}\n\n\tpublic ServerQueryHandler(ServerConnection source) {\n\t\tthis.source = source;\n\t}\n\n\t@Override\n\tpublic void query(String sql) {\n\n\t\tServerConnection c = this.source;\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(new StringBuilder().append(c).append(sql).toString());\n\t\t}\n\t\t//\n\t\tint rs = ServerParse.parse(sql);\n\t\tint sqlType = rs & 0xff;\n\n\t\tswitch (sqlType) {\n\t\t//explain sql\n\t\tcase ServerParse.EXPLAIN:\n\t\t\tExplainHandler.handle(sql, c, rs >>> 8);\n\t\t\tbreak;\n\t\t//explain2 datanode=? sql=?\n\t\tcase ServerParse.EXPLAIN2:\n\t\t\tExplain2Handler.handle(sql, c, rs >>> 8);\n\t\t\tbreak;\n\t\tcase ServerParse.COMMAND:\n\t\t\tCommandHandler.handle(sql, c, 16);\n\t\t\tbreak;\n\t\tcase ServerParse.SET:\n\t\t\tSetHandler.handle(sql, c, rs >>> 8);\n\t\t\tbreak;\n\t\tcase ServerParse.SHOW:\n\t\t\tShowHandler.handle(sql, c, rs >>> 8);\n\t\t\tbreak;\n\t\tcase ServerParse.SELECT:\n\t\t\tSelectHandler.handle(sql, c, rs >>> 8);\n\t\t\tbreak;\n\t\tcase ServerParse.START:\n\t\t\tStartHandler.handle(sql, c, rs >>> 8);\n\t\t\tbreak;\n\t\tcase ServerParse.BEGIN:\n\t\t\tBeginHandler.handle(sql, c);\n\t\t\tbreak;\n\t\t//不支持oracle的savepoint事务回退点\n\t\tcase ServerParse.SAVEPOINT:\n\t\t\tSavepointHandler.handle(sql, c);\n\t\t\tbreak;\n\t\tcase ServerParse.KILL:\n\t\t\tKillHandler.handle(sql, rs >>> 8, c);\n\t\t\tbreak;\n\t\t//不支持KILL_Query\n\t\tcase ServerParse.KILL_QUERY:\n\t\t\tLOGGER.warn(new StringBuilder().append(\"Unsupported command:\").append(sql).toString());\n\t\t\tc.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR,\"Unsupported command\");\n\t\t\tbreak;\n\t\tcase ServerParse.USE:\n\t\t\tUseHandler.handle(sql, c, rs >>> 8);\n\t\t\tbreak;\n\t\tcase ServerParse.COMMIT:\n\t\t\tc.commit();\n\t\t\tbreak;\n\t\tcase ServerParse.ROLLBACK:\n\t\t\tc.rollback();\n\t\t\tbreak;\n\t\tcase ServerParse.HELP:\n\t\t\tLOGGER.warn(new StringBuilder().append(\"Unsupported command:\").append(sql).toString());\n\t\t\tc.writeErrMessage(ErrorCode.ER_SYNTAX_ERROR, \"Unsupported command\");\n\t\t\tbreak;\n\t\tcase ServerParse.MYSQL_CMD_COMMENT:\n\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\tbreak;\n\t\tcase ServerParse.MYSQL_COMMENT:\n\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\tbreak;\n        case ServerParse.LOAD_DATA_INFILE_SQL:\n\t\t\tif(RouteService.isHintSql(sql) > -1){ // 目前仅支持注解 datanode,原理为直接将导入sql发送到指定mysql节点\n\t\t\t\tc.execute(sql , ServerParse.LOAD_DATA_INFILE_SQL);\n\t\t\t}else{\n\t\t\t\tc.loadDataInfileStart(sql);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ServerParse.MIGRATE: {\n\t\t    try {\n                MigrateHandler.handle(sql, c);\n            }catch (Throwable e){\n\t\t        //MigrateHandler中InterProcessMutex slaveIDsLock 会连接zk,zk连接不上会导致类加载失败,\n                // 此后再调用此命令,将会出现类未定义,所以最终还是需要重启mycat\n\t\t        e.printStackTrace();\n                String msg = \"Mycat is not connected to zookeeper!!\\n\";\n                msg += \"Please start zookeeper and restart mycat so that this mycat can temporarily execute the migration command.If other mycat does not connect to this zookeeper, they will not be able to perceive changes in the migration task.\\n\";\n                msg += \"After starting zookeeper,you can command tas follow:\\n\\nmigrate -table=schema.test -add=dn2,dn3 -force=true\\n\\nto perform the migration.\\n\";\n                LOGGER.error(e.getMessage());\n                LOGGER.error(msg);\n                c.writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR, msg);\n            }\n\t\t\tbreak;\n\t\t}\n\t\tcase ServerParse.LOCK:\n        \tc.lockTable(sql);\n        \tbreak;\n        case ServerParse.UNLOCK:\n        \tc.unLockTable(sql);\n        \tbreak;\n\t\tdefault:\n\t\t\tif(readOnly){\n\t\t\t\tLOGGER.warn(new StringBuilder().append(\"User readonly:\").append(sql).toString());\n\t\t\t\tc.writeErrMessage(ErrorCode.ER_USER_READ_ONLY, \"User readonly\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tc.execute(sql, rs & 0xff);\n\t\t}\n\n\t\tswitch (sqlType) {\n\t\t\tcase ServerParse.SELECT:\n\t\t\tcase ServerParse.DELETE:\n\t\t\tcase ServerParse.UPDATE:\n\t\t\tcase ServerParse.INSERT:\n\t\t\tcase ServerParse.COMMAND:\n\t\t\t\t// curd 在后面会更新\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tc.setExecuteSql(null);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/Session.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server;\n\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.route.RouteResultset;\n\n/**\n * @author mycat\n */\npublic interface Session {\n\n    /**\n     * 取得源端连接\n     */\n    FrontendConnection getSource();\n\n    /**\n     * 取得当前目标端数量\n     */\n    int getTargetCount();\n\n    /**\n     * 开启一个会话执行\n     */\n    void execute(RouteResultset rrs, int type);\n\n    /**\n     * 提交一个会话执行\n     */\n    void commit();\n\n    /**\n     * 回滚一个会话执行\n     */\n    void rollback();\n\n    /**\n     * 取消一个正在执行中的会话\n     * \n     * @param sponsor\n     *            如果发起者为null，则表示由自己发起。\n     */\n    void cancel(FrontendConnection sponsor);\n\n    /**\n     * 终止会话，必须在关闭源端连接后执行该方法。\n     */\n    void terminate();\n    \n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/BeginHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport io.mycat.server.ServerConnection;\n\n/**\n * @author mycat\n */\npublic final class BeginHandler {\n    private static final byte[] AC_OFF = new byte[] { 7, 0, 0, 1, 0, 0, 0, 0,\n            0, 0, 0 };\n    public static void handle(String stmt, ServerConnection c) {\n        if (c.isAutocommit())\n        {\n            c.write(c.writeToBuffer(AC_OFF, c.allocate()));\n        }else\n        {\n            c.getSession2().commit() ;\n        }\n        c.setAutocommit(false);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/CommandHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\nimport java.sql.SQLNonTransientException;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.mysql.nio.handler.SimpleLogHandler;\nimport io.mycat.backend.mysql.nio.handler.SingleNodeHandler;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.server.NonBlockingSession;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.util.SchemaUtil;\nimport io.mycat.statistic.stat.QueryResult;\nimport io.mycat.statistic.stat.QueryResultDispatcher;\n/**\n * <pre>\n * 用于解决mysql 协议中com_field_list命令的支持\n * https://dev.mysql.com/doc/internals/en/com-field-list.html\n * </pre>\n * @author stones_he@163.com\n */\npublic class CommandHandler {\n\tprivate static final Logger logger = LoggerFactory.getLogger(CommandHandler.class);\n\t//\n\tpublic static void handle(String stmt, ServerConnection c, int offset) {\n\t\tString table = stmt.substring(offset).trim();\n\t\t//int sqlType = ServerParse.parse(stmt) & 0xff;\n\t\tString db = c.getSchema();\n\t\tif (db == null) {\n\t\t\tdb = SchemaUtil.detectDefaultDb(stmt, ServerParse.COMMAND);\n\t\t\tif (db == null) {\n\t\t\t\tc.writeErrMessage(ErrorCode.ER_NO_DB_ERROR, \"No database selected\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tSchemaConfig schema = MycatServer.getInstance().getConfig().getSchemas().get(db);\n\t\tif (schema == null) {\n\t\t\tc.writeErrMessage(ErrorCode.ER_BAD_DB_ERROR, \"Unknown database '\" + db + \"'\");\n\t\t\treturn;\n\t\t}\n\t\tSystemConfig system = MycatServer.getInstance().getConfig().getSystem();\n\t\tRouteResultset rrs = null;\n\t\ttry {\n\t\t\trrs = MycatServer\t.getInstance()\n\t\t\t\t\t\t\t\t.getRouterservice()\n\t\t\t\t\t\t\t\t.route(system, schema, ServerParse.COMMAND, stmt, c.getCharset(), c);\n\t\t\tif (rrs == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (SQLNonTransientException e) {\n\t\t\tlogger.warn(\"\", e);\n\t\t\tc.writeErrMessage(ErrorCode.ERR_HANDLE_DATA, e.toString());\n\t\t}\n\t\t//\n\t\tCommandExecResultHandler handler = new CommandExecResultHandler(rrs, c.getSession2(), table);\n\t\ttry {\n\t\t\thandler.execute();\n\t\t} catch (Exception e1) {\n\t\t\tlogger.warn(\"\", e1);\n\t\t\tc.writeErrMessage(ErrorCode.ERR_HANDLE_DATA, e1.toString());\n\t\t}\n\t}\n}\nclass CommandExecResultHandler extends SingleNodeHandler {\n\tprivate static final Logger logger = LoggerFactory.getLogger(CommandExecResultHandler.class);\n\tprivate String table;\n\tpublic CommandExecResultHandler(RouteResultset rrs, NonBlockingSession session, String table) {\n\t\tsuper(rrs, session);\n\t\tthis.table = table;\n\t}\n\tprivate void rowEofResponse0(byte[] eof, BackendConnection conn) {\n\t\tlogger.debug(\"CommandExecResultHandler.rowEofResponse eof: {}\", SimpleLogHandler.bytesToHex(eof));\n\t\t// \n\t\tServerConnection source = getSession().getSource();\n\t\tconn.recordSql(source.getHost(), source.getSchema(), getRouteResultsetNode().getStatement());\n\t\t// 判断是调用存储过程的话不能在这里释放链接\n\t\tif (!getRouteResultset().isCallStatement() || (getRouteResultset().isCallStatement() && getRouteResultset()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.getProcedure()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.isResultSimpleValue())) {\n\t\t\tgetSession().releaseConnectionIfSafe(conn, logger.isDebugEnabled(), false);\n\t\t\tendRunning();\n\t\t}\n\t\t//\n\t\tint resultSize = source.getWriteQueue().size() * MycatServer.getInstance()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.getConfig()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.getSystem()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.getBufferPoolPageSize();\n\t\tresultSize = resultSize + getBuffer().position();\n\t\tif (!errorRepsponsed.get() && !getSession().closed() && source.canResponse()) {\n\t\t\tsource.write(getBuffer());\n\t\t}\n\t\tsource.setExecuteSql(null);\n\t\t//查询结果派发\n        QueryResult queryResult = new QueryResult(getSession().getSource().getSchema(),\n                getSession().getSource().getUser(),\n                getRouteResultset().getSqlType(), getRouteResultset().getStatement(), affectedRows, netInBytes,\n                netOutBytes, startTime, System.currentTimeMillis(), resultSize, source.getHost());\n\t\tQueryResultDispatcher.dispatchQuery(queryResult);\n\t}\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields, byte[] eof, BackendConnection conn) {\n\t\tlogger.debug(\"CommandExecResultHandler.fieldEofResponse header: {}\", SimpleLogHandler.bytesToHex(header));\n\t\tfor (byte[] field : fields) {\n\t\t\tlogger.debug(\"CommandExecResultHandler.fieldEofResponse fields: {}\", SimpleLogHandler.bytesToHex(field));\n\t\t}\n\t\tlogger.debug(\"CommandExecResultHandler.fieldEofResponse eof: {}\", SimpleLogHandler.bytesToHex(eof));\n\t\t//\n\t\tfieldEofResponse0(header, fields, eof, conn);\n\t\trowEofResponse0(eof, conn);\n\t}\n\tprivate void fieldEofResponse0(byte[] header, List<byte[]> fields, byte[] eof, BackendConnection conn) {\n\t\tbyte packetId = 0;\n\t\tthis.netOutBytes += header.length;\n\t\tfor (int i = 0, len = fields.size(); i < len; ++i) {\n\t\t\tbyte[] field = fields.get(i);\n\t\t\tthis.netOutBytes += field.length;\n\t\t}\n\t\theader[3] = ++packetId;\n\t\tServerConnection source = getSession().getSource();\n\t\tbuffer = source.writeToBuffer(header, allocBuffer());\n\t\tfor (int i = 0, len = fields.size(); i < len; ++i) {\n\t\t\tbyte[] field = fields.get(i);\n\t\t\tfield[3] = ++packetId;\n\t\t\t// 保存field信息\n\t\t\tFieldPacket fieldPk = new FieldPacket();\n\t\t\tfieldPk.read(field);\n\t\t\tfieldPackets.add(fieldPk);\n\t\t\tbuffer = source.writeToBuffer(field, buffer);\n\t\t}\n\t\tfieldCount = fieldPackets.size();\n\t\teof[3] = ++packetId;\n\t\tthis.netOutBytes += eof.length;\n\t\tbuffer = source.writeToBuffer(eof, buffer);\n\t}\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CommandExecResultHandler [node=\" + getRouteResultsetNode() + \", table=\" + table + \"]\";\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/Explain2Handler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport java.nio.ByteBuffer;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.backend.mysql.nio.handler.SingleNodeHandler;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author rainbow\n */\npublic class Explain2Handler {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(Explain2Handler.class);\n    private static final String FORMAT_JSON = \"FORMAT=JSON\";\n\tprivate static final RouteResultsetNode[] EMPTY_ARRAY = new RouteResultsetNode[1];\n\tprivate static final int FIELD_COUNT = 2;\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tstatic {\n\t\tfields[0] = PacketUtil.getField(\"SQL\",\n\t\t\t\tFields.FIELD_TYPE_VAR_STRING);\n\t\tfields[1] = PacketUtil.getField(\"MSG\",\n\t\t\t\tFields.FIELD_TYPE_VAR_STRING);\n\t}\n\n\tpublic static void handle(String stmt, ServerConnection c, int offset) {\n\n\t\ttry {\n\t\t\tstmt = stmt.substring(offset);\n\t\t\tif(!stmt.toLowerCase().contains(\"datanode=\") || !stmt.toLowerCase().contains(\"sql=\")){\n\t\t\t\tshowerror(stmt, c, \"explain2 datanode=? sql=?\");\n\t\t\t\treturn ;\n\t\t\t}\n            String dataNode = stmt.substring(stmt.indexOf(\"datanode=\") + \"datanode=\".length(), stmt.indexOf(\"sql=\"))\n                    .trim();\n            boolean isJsonFormat = stmt.toUpperCase().contains(FORMAT_JSON);\n            String formatStr = isJsonFormat ? FORMAT_JSON : \"\";\n            String sql = \"explain \" + formatStr + \" \" + stmt.substring(stmt.indexOf(\"sql=\") + 4, stmt.length()).trim();\n\t\t\t\n\t\t\tif(dataNode == null || dataNode.isEmpty() || sql == null || sql.isEmpty()){\n\t\t\t\tshowerror(stmt, c, \"dataNode or sql is null or empty\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tRouteResultsetNode node = new RouteResultsetNode(dataNode, ServerParse.SELECT, sql);\n\t\t\tRouteResultset\trrs =  new RouteResultset(sql, ServerParse.SELECT);\n\t\t\tnode.setSource(rrs);\n\t\t\tEMPTY_ARRAY[0] = node; \n\t\t\trrs.setNodes(EMPTY_ARRAY);\n\t\t\tSingleNodeHandler singleNodeHandler = new SingleNodeHandler(rrs, c.getSession2());\n\t\t\tsingleNodeHandler.execute();\n\t\t} catch (Exception e) {\n\t\t\tlogger.error(e.getMessage(), e.getCause());\n\t\t\tshowerror(stmt, c, e.getMessage());\n\t\t}\n\t}\n\t\n\tprivate static void showerror(String stmt, ServerConnection c, String msg){\n\t\tByteBuffer buffer = c.allocate();\n\t\t// write header\n\t\tResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n\t\tbyte packetId = header.packetId;\n\t\tbuffer = header.write(buffer, c,true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tfield.packetId = ++packetId;\n\t\t\tbuffer = field.write(buffer, c,true);\n\t\t}\n\n\t\t// write eof\n\t\tEOFPacket eof = new EOFPacket();\n\t\teof.packetId = ++packetId;\n\t\tbuffer = eof.write(buffer, c,true);\n\n\t\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(StringUtil.encode(stmt, c.getCharset()));\n\t\trow.add(StringUtil.encode(msg, c.getCharset()));\n\t\trow.packetId = ++packetId;\n\t\tbuffer = row.write(buffer, c,true);\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c,true);\n\n\t\t// post write\n\t\tc.write(buffer);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/ExplainHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport java.nio.ByteBuffer;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.Fields;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.util.SchemaUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class ExplainHandler {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(ExplainHandler.class);\n    private final static Pattern pattern = Pattern.compile(\"(?:(\\\\s*next\\\\s+value\\\\s+for\\\\s*MYCATSEQ_(\\\\w+))(,|\\\\)|\\\\s)*)+\", Pattern.CASE_INSENSITIVE);\n\tprivate static final RouteResultsetNode[] EMPTY_ARRAY = new RouteResultsetNode[0];\n\tprivate static final int FIELD_COUNT = 2;\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tstatic {\n\t\tfields[0] = PacketUtil.getField(\"DATA_NODE\",\n\t\t\t\tFields.FIELD_TYPE_VAR_STRING);\n\t\tfields[1] = PacketUtil.getField(\"SQL\", Fields.FIELD_TYPE_VAR_STRING);\n\t}\n\n\tpublic static void handle(String stmt, ServerConnection c, int offset) {\n\t\tstmt = stmt.substring(offset).trim();\n\n\t\tRouteResultset rrs = getRouteResultset(c, stmt);\n\t\tif (rrs == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n\t\tbyte packetId = header.packetId;\n\t\tbuffer = header.write(buffer, c,true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tfield.packetId = ++packetId;\n\t\t\tbuffer = field.write(buffer, c,true);\n\t\t}\n\n\t\t// write eof\n\t\tEOFPacket eof = new EOFPacket();\n\t\teof.packetId = ++packetId;\n\t\tbuffer = eof.write(buffer, c,true);\n\n\t\t// write rows\n\t\tRouteResultsetNode[] rrsn =  rrs.getNodes();\n\t\tfor (RouteResultsetNode node : rrsn) {\n\t\t\tRowDataPacket row = getRow(node, c.getCharset());\n\t\t\trow.packetId = ++packetId;\n\t\t\tbuffer = row.write(buffer, c,true);\n\t\t}\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c,true);\n\n\t\t// post write\n\t\tc.write(buffer);\n\n\t}\n\n\tprivate static RowDataPacket getRow(RouteResultsetNode node, String charset) {\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(StringUtil.encode(node.getName(), charset));\n\t\trow.add(StringUtil.encode(node.getStatement().replaceAll(\"[\\\\t\\\\n\\\\r]\", \" \"), charset));\n\t\treturn row;\n\t}\n\n\tprivate static RouteResultset getRouteResultset(ServerConnection c,\n\t\t\tString stmt) {\n\t\tString db = c.getSchema();\n        int sqlType = ServerParse.parse(stmt) & 0xff;\n\t\tif (db == null) {\n            db = SchemaUtil.detectDefaultDb(stmt, sqlType);\n\n            if(db==null)\n            {\n                c.writeErrMessage(ErrorCode.ER_NO_DB_ERROR, \"No database selected\");\n                return null;\n            }\n\t\t}\n\t\tSchemaConfig schema = MycatServer.getInstance().getConfig()\n\t\t\t\t.getSchemas().get(db);\n\t\tif (schema == null) {\n\t\t\tc.writeErrMessage(ErrorCode.ER_BAD_DB_ERROR, \"Unknown database '\"\n\t\t\t\t\t+ db + \"'\");\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\n            if(ServerParse.INSERT==sqlType&&isMycatSeq(stmt, schema))\n            {\n                c.writeErrMessage(ErrorCode.ER_PARSE_ERROR, \"insert sql using mycat seq,you must provide primaryKey value for explain\");\n                return null;\n            }\n            SystemConfig system = MycatServer.getInstance().getConfig().getSystem();\n            return MycatServer.getInstance().getRouterservice()\n\t\t\t\t\t.route(system,schema, sqlType, stmt, c.getCharset(), c);\n\t\t} catch (Exception e) {\n\t\t\tStringBuilder s = new StringBuilder();\n\t\t\tlogger.warn(s.append(c).append(stmt).toString()+\" error:\"+ e);\n\t\t\tString msg = e.getMessage();\n\t\t\tc.writeErrMessage(ErrorCode.ER_PARSE_ERROR, msg == null ? e\n\t\t\t\t\t.getClass().getSimpleName() : msg);\n\t\t\treturn null;\n\t\t}\n\t}\n\n    private static boolean isMycatSeq(String stmt, SchemaConfig schema)\n    {\n        if(pattern.matcher(stmt).find()) {\n\t\t\treturn true;\n\t\t}\n        SQLStatementParser parser =new MySqlStatementParser(stmt);\n        MySqlInsertStatement statement = (MySqlInsertStatement) parser.parseStatement();\n        String tableName=   statement.getTableName().getSimpleName();\n        TableConfig tableConfig= schema.getTables().get(tableName.toUpperCase());\n        if(tableConfig==null) {\n\t\t\treturn false;\n\t\t}\n        if(tableConfig.isAutoIncrement())\n        {\n            boolean isHasIdInSql=false;\n            String primaryKey = tableConfig.getPrimaryKey();\n            List<SQLExpr> columns = statement.getColumns();\n            for (SQLExpr column : columns)\n            {\n                String columnName = column.toString();\n                if(primaryKey.equalsIgnoreCase(columnName))\n                {\n                    isHasIdInSql = true;\n                    break;\n                }\n            }\n            if(!isHasIdInSql) {\n\t\t\t\treturn true;\n\t\t\t}\n        }\n\n\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/KillHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.net.NIOProcessor;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class KillHandler {\n\n    public static void handle(String stmt, int offset, ServerConnection c) {\n        String id = stmt.substring(offset).trim();\n        if (StringUtil.isEmpty(id)) {\n            c.writeErrMessage(ErrorCode.ER_NO_SUCH_THREAD, \"NULL connection id\");\n        } else {\n            // get value\n            long value = 0;\n            try {\n                value = Long.parseLong(id);\n            } catch (NumberFormatException e) {\n                c.writeErrMessage(ErrorCode.ER_NO_SUCH_THREAD, \"Invalid connection id:\" + id);\n                return;\n            }\n\n            // kill myself\n            if (value == c.getId()) {\n                getOkPacket().write(c);\n                c.write(c.allocate());\n                return;\n            }\n\n            // get connection and close it\n            FrontendConnection fc = null;\n            NIOProcessor[] processors = MycatServer.getInstance().getProcessors();\n            for (NIOProcessor p : processors) {\n                if ((fc = p.getFrontends().get(value)) != null) {\n                    break;\n                }\n            }\n            if (fc != null) {\n                fc.close(\"killed\");\n                getOkPacket().write(c);\n            } else {\n                c.writeErrMessage(ErrorCode.ER_NO_SUCH_THREAD, \"Unknown connection id:\" + id);\n            }\n        }\n    }\n\n    private static OkPacket getOkPacket() {\n        OkPacket packet = new OkPacket();\n        packet.packetId = 1;\n        packet.affectedRows = 0;\n        packet.serverStatus = 2;\n        return packet;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/MigrateHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport com.alibaba.fastjson.JSON;\nimport com.google.common.base.CharMatcher;\nimport com.google.common.base.Splitter;\nimport com.google.common.base.Strings;\nimport com.google.common.collect.Lists;\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.Fields;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.migrate.MigrateTask;\nimport io.mycat.migrate.MigrateTaskWatch;\nimport io.mycat.migrate.MigrateUtils;\nimport io.mycat.migrate.TaskNode;\nimport io.mycat.net.mysql.*;\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\nimport io.mycat.route.function.PartitionByCRC32PreSlot;\nimport io.mycat.route.function.PartitionByCRC32PreSlot.Range;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.ObjectUtil;\nimport io.mycat.util.StringUtil;\nimport io.mycat.util.ZKUtils;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.api.transaction.CuratorTransactionFinal;\nimport org.apache.curator.framework.recipes.locks.InterProcessMutex;\nimport org.joda.time.LocalDateTime;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\n\nimport static io.mycat.config.loader.zkprocess.comm.ZkParamCfg.ZK_CFG_FLAG;\n\n/**\n * todo remove watch\n *\n * @author nange\n */\npublic final class MigrateHandler {\n    private static final Logger LOGGER = LoggerFactory.getLogger(\"MigrateHandler\");\n\n    //可以优化成多个锁\n    private static final InterProcessMutex slaveIDsLock = new InterProcessMutex(ZKUtils.getConnection(), ZKUtils.getZKBasePath() + \"lock/slaveIDs.lock\");\n    private static final int FIELD_COUNT = 1;\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static volatile boolean forceInit = false;\n\n\n    static {\n        fields[0] = PacketUtil.getField(\"TASK_ID\",\n                Fields.FIELD_TYPE_VAR_STRING);\n\n    }\n\n    private static String getUUID() {\n        String s = UUID.randomUUID().toString();\n        //去掉“-”符号\n        return s.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24);\n    }\n\n    public static void handle(String stmt, ServerConnection c) {\n        Map<String, String> map = parse(stmt);\n\n        String table = map.get(\"table\");\n        String add = map.get(\"add\");\n        String timeoutString = map.get(\"timeout\");\n        String charset = map.get(\"charset\");\n        boolean forceBinlog = false;//这个命令禁止使用因为binlog stream的实现依赖mysqldump\n        int timeout = 120;// minute\n        String schema = \"\";\n        if (table == null) {\n            writeErrMessage(c, \"table cannot be null\");\n            return;\n        }\n        if (table.contains(\".\")) {\n            String[] split = table.split(\"\\\\.\");\n            schema = split[0];\n            table = split[1];\n        }\n        if (add == null) {\n            writeErrMessage(c, \"add cannot be null\");\n            return;\n        }\n        if (timeoutString != null) {\n            try {\n                timeout = Integer.parseInt(timeoutString);\n                if (timeout <= 0) {\n                    throw new NumberFormatException(\"\");\n                }\n            } catch (Exception e) {\n                writeErrMessage(c, String.format(\"timeout:%s format is wrong,it should be 1-\" + Integer.MAX_VALUE + \" (unit:minute)\", timeoutString));\n                return;\n            }\n        }\n        if (StringUtil.isEmpty(charset)) {\n            charset = Charset.defaultCharset().name();\n        }\n        if (!Charset.isSupported(charset)) {\n            writeErrMessage(c, \"Not support charset \" + charset);\n            return;\n        }\n        ZkConfig zkConfig = ZkConfig.getInstance();\n        boolean loadZk = \"true\".equalsIgnoreCase(zkConfig.getValue(ZK_CFG_FLAG));\n        boolean force = \"true\".equalsIgnoreCase(map.get(\"force\"));\n        CuratorFramework zk = ZKUtils.getConnection();\n        if (!loadZk) {\n            if (!force) {\n                String msg = \"\";\n                msg += \"Mycat can temporarily execute the migration command.If other mycat does not connect to this zookeeper, they will not be able to perceive changes in the migration task.\\n\";\n                msg += \"You can command as follow:\\n\\nmigrate -table=schema.test -add=dn2,dn3 -force=true\\n\\nto perform the migration.\\n\";\n                LOGGER.error(msg);\n                writeErrMessage(c, msg);\n                return;\n            }\n            //因为loadZk在mycat启动时候没有监听zk里migrate路径，这里需要把这个监听补上\n            //借用slaveIDsLock对象作为同步锁\n            boolean changed = false;\n            if (!forceInit) {\n                synchronized (slaveIDsLock) {\n                    if (!forceInit) {\n                        forceInit = true;\n                        changed = true;\n                    }\n                }\n            }\n            if (changed) {\n                MigrateTaskWatch.start();\n            }\n        }\n        if (zk == null) {\n            writeErrMessage(c, \"Mycat is not connected to zookeeper\");\n            return;\n        }\n        String taskID = getUUID();\n        try {\n            if (StringUtil.isEmpty(schema)) {\n                schema = c.getSchema();\n            }\n            if (StringUtil.isEmpty(schema)) {\n                writeErrMessage(c, \"No database selected\");\n                return;\n            }\n            SchemaConfig schemaConfig = MycatServer.getInstance().getConfig().getSchemas().get(schema);\n            if (schemaConfig == null) {\n                writeErrMessage(c, String.format(\"Unknown database '\" + schema + \"'\", table.toUpperCase(), schema));\n                return;\n            }\n            TableConfig tableConfig = schemaConfig.getTables().get(table.toUpperCase());\n            if (tableConfig == null) {\n                writeErrMessage(c, String.format(\"Table '%s' doesn't define in schema '%s'\\n\", table.toUpperCase(), schema));\n                return;\n            }\n            AbstractPartitionAlgorithm algorithm = tableConfig.getRule().getRuleAlgorithm();\n            if (!(algorithm instanceof PartitionByCRC32PreSlot)) {\n                writeErrMessage(c, \"table: \" + table + \" rule is not be PartitionByCRC32PreSlot\");\n                return;\n            }\n            Map<Integer, List<Range>> integerListMap = ((PartitionByCRC32PreSlot) algorithm).getRangeMap();\n            integerListMap = (Map<Integer, List<Range>>) ObjectUtil.copyObject(integerListMap);\n\n            ArrayList<String> oldDataNodes = tableConfig.getDataNodes();\n            Map<String, PhysicalDBNode> allDataNodes = MycatServer.getInstance().getConfig().getDataNodes();\n            List<String> newDataNodes = Splitter.on(\",\").omitEmptyStrings().trimResults().splitToList(add);\n            for (String newDataNode : newDataNodes) {\n                if (tableConfig.getDataNodes().contains(newDataNode)) {\n                    writeErrMessage(c, \"The dataNode \" + newDataNode+\" that needs to be added already exists\\n\");\n                    return;\n                }\n                if(!allDataNodes.containsKey(newDataNode)){\n                    writeErrMessage(c, \"The dataNode \" + newDataNode+\" does not exist\\n\");\n                    return;\n                }\n            }\n\n            Map<String, List<MigrateTask>> tasks = MigrateUtils\n                    .balanceExpand(table, integerListMap, oldDataNodes, newDataNodes, PartitionByCRC32PreSlot.DEFAULT_SLOTS_NUM);\n\n            CuratorTransactionFinal transactionFinal = null;\n            String taskBase = ZKUtils.getZKBasePath() + \"migrate/\" + schema;\n            String taskPath = taskBase + \"/\" + taskID;\n            CuratorFramework client = ZKUtils.getConnection();\n\n            //校验 之前同一个表的迁移任务未完成，则jzhi禁止继续\n            if (client.checkExists().forPath(taskBase) != null) {\n                List<String> childTaskList = client.getChildren().forPath(taskBase);\n                for (String child : childTaskList) {\n                    String path = taskBase + \"/\" + child;\n                    String str = new String(ZKUtils.getConnection().getData().forPath(path));\n                    if (!isJson(str)) {\n                        writeErrMessage(c, path + \"in zookeeper is abnormal state,please repair manual!\");\n                        return;\n                    }\n                    TaskNode taskNode = JSON\n                            .parseObject(str, TaskNode.class);\n                    if (taskNode.getSchema().equalsIgnoreCase(schema) && table.equalsIgnoreCase(taskNode.getTable())\n                            && taskNode.getStatus() < 5) {\n                        writeErrMessage(c, \"table: \" + table + \" previous migrate task is still running,on the same time one table only one task\");\n                        return;\n                    }\n                }\n            }\n            String backupPath = backup();\n            client.create().creatingParentsIfNeeded().forPath(taskPath);\n            TaskNode taskNode = new TaskNode();\n            taskNode.setSchema(schema);\n            taskNode.setSql(stmt);\n            taskNode.setTable(table);\n            taskNode.setAdd(add);\n            taskNode.setStatus(0);\n            taskNode.setTimeout(timeout);\n            taskNode.setCharset(charset);\n            taskNode.setForceBinlog(forceBinlog);\n            taskNode.setBackupFile(backupPath);\n\n            Map<String, Integer> fromNodeSlaveIdMap = new HashMap<>();\n\n            List<MigrateTask> allTaskList = new ArrayList<>();\n            for (Map.Entry<String, List<MigrateTask>> entry : tasks.entrySet()) {\n                String key = entry.getKey();\n                List<MigrateTask> value = entry.getValue();\n                for (MigrateTask migrateTask : value) {\n                    migrateTask.setSchema(schema);\n\n                    //分配slaveid只需要一个dataHost分配一个即可，后续任务执行模拟从节点只需要一个dataHost一个\n                    String dataHost = getDataHostNameFromNode(migrateTask.getFrom());\n                    if (fromNodeSlaveIdMap.containsKey(dataHost)) {\n                        migrateTask.setSlaveId(fromNodeSlaveIdMap.get(dataHost));\n                    } else {\n                        migrateTask.setSlaveId(getSlaveIdFromZKForDataNode(migrateTask.getFrom()));\n                        fromNodeSlaveIdMap.put(dataHost, migrateTask.getSlaveId());\n                    }\n\n                }\n                allTaskList.addAll(value);\n\n            }\n\n\n            transactionFinal = client.inTransaction().setData().forPath(taskPath, JSON.toJSONBytes(taskNode)).and();\n\n\n            //合并成dataHost级别任务\n            Map<String, List<MigrateTask>> dataHostMigrateMap = mergerTaskForDataHost(allTaskList);\n\n            String boosterDataHosts = ZkConfig.getInstance().getValue(ZkParamCfg.MYCAT_BOOSTER_DATAHOSTS);\n            Set<String> dataNodes = new HashSet<>(Splitter.on(\",\").trimResults().omitEmptyStrings().splitToList(boosterDataHosts));\n            boolean isFirst = true;\n            for (String s : dataHostMigrateMap.keySet()) {\n                if (!dataNodes.contains(s)) {\n                    if (isFirst) {\n                        LOGGER.warn(\"--------------------------------check dataNode--------------------------------\");\n                        isFirst = false;\n                    }\n                    LOGGER.warn(\"dataNode %s will be not participate in migration\");\n                }\n            }\n\n            for (Map.Entry<String, List<MigrateTask>> entry : dataHostMigrateMap.entrySet()) {\n                String key = entry.getKey();\n                List<MigrateTask> value = entry.getValue();\n                String path = taskPath + \"/\" + key;\n                transactionFinal = transactionFinal.create().forPath(path, JSON.toJSONBytes(value)).and();\n            }\n            transactionFinal.commit();\n        } catch (Exception e) {\n            LOGGER.error(\"migrate error\", e);\n            writeErrMessage(c, \"migrate error:\" + e);\n            return;\n        }\n\n        writePackToClient(c, taskID);\n        LOGGER.info(\"--------------------------------task created success--------------------------------\");\n        LOGGER.info(\"task start\", new Date());\n    }\n\n    private static void writePackToClient(ServerConnection c, String taskID) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n        byte packetId = header.packetId;\n        buffer = header.write(buffer, c, true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            field.packetId = ++packetId;\n            buffer = field.write(buffer, c, true);\n        }\n\n        // write eof\n        EOFPacket eof = new EOFPacket();\n        eof.packetId = ++packetId;\n        buffer = eof.write(buffer, c, true);\n\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(StringUtil.encode(taskID, c.getCharset()));\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c, true);\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c, true);\n\n        // post write\n        c.write(buffer);\n    }\n\n\n    private static String getDataHostNameFromNode(String dataNode) {\n        return MycatServer.getInstance().getConfig().getDataNodes().get(dataNode).getDbPool().getHostName();\n    }\n\n    private static Map<String, List<MigrateTask>> mergerTaskForDataHost(List<MigrateTask> migrateTaskList) {\n        Map<String, List<MigrateTask>> taskMap = new HashMap<>();\n        for (MigrateTask migrateTask : migrateTaskList) {\n            String dataHost = getDataHostNameFromNode(migrateTask.getFrom());\n            if (taskMap.containsKey(dataHost)) {\n                taskMap.get(dataHost).add(migrateTask);\n            } else {\n                taskMap.put(dataHost, Lists.newArrayList(migrateTask));\n            }\n        }\n\n\n        return taskMap;\n    }\n\n    private static int getSlaveIdFromZKForDataNode(String dataNode) {\n        PhysicalDBNode dbNode = MycatServer.getInstance().getConfig().getDataNodes().get(dataNode);\n        String slaveIDs = dbNode.getDbPool().getSlaveIDs();\n        if (Strings.isNullOrEmpty(slaveIDs))\n            throw new RuntimeException(\"dataHost:\" + dbNode.getDbPool().getHostName() + \" do not config the salveIDs field\");\n\n        List<Integer> allSlaveIDList = parseSlaveIDs(slaveIDs);\n\n        String taskPath = ZKUtils.getZKBasePath() + \"slaveIDs/\" + dbNode.getDbPool().getHostName();\n        try {\n            slaveIDsLock.acquire(30, TimeUnit.SECONDS);\n            Set<Integer> zkSlaveIdsSet = new HashSet<>();\n            if (ZKUtils.getConnection().checkExists().forPath(taskPath) != null) {\n                List<String> zkHasSlaveIDs = ZKUtils.getConnection().getChildren().forPath(taskPath);\n                for (String zkHasSlaveID : zkHasSlaveIDs) {\n                    zkSlaveIdsSet.add(Integer.parseInt(zkHasSlaveID));\n                }\n            }\n            for (Integer integer : allSlaveIDList) {\n                if (!zkSlaveIdsSet.contains(integer)) {\n                    ZKUtils.getConnection().create().creatingParentsIfNeeded().forPath(taskPath + \"/\" + integer);\n                    return integer;\n                }\n            }\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        } finally {\n            try {\n                slaveIDsLock.release();\n            } catch (Exception e) {\n                LOGGER.error(\"error:\", e);\n            }\n        }\n\n        throw new RuntimeException(\"cannot get the slaveID  for dataHost :\" + dbNode.getDbPool().getHostName());\n    }\n\n    private static List<Integer> parseSlaveIDs(String slaveIDs) {\n        List<Integer> allSlaveList = new ArrayList<>();\n        List<String> stringList = Splitter.on(\",\").omitEmptyStrings().trimResults().splitToList(slaveIDs);\n        for (String id : stringList) {\n            if (id.contains(\"-\")) {\n                List<String> idRangeList = Splitter.on(\"-\").omitEmptyStrings().trimResults().splitToList(id);\n                if (idRangeList.size() != 2)\n                    throw new RuntimeException(id + \"slaveIds range must be 2  size\");\n                for (int i = Integer.parseInt(idRangeList.get(0)); i <= Integer.parseInt(idRangeList.get(1)); i++) {\n                    allSlaveList.add(i);\n                }\n\n            } else {\n                allSlaveList.add(Integer.parseInt(id));\n            }\n        }\n        return allSlaveList;\n    }\n\n\n    private static OkPacket getOkPacket() {\n        OkPacket packet = new OkPacket();\n        packet.packetId = 1;\n        packet.affectedRows = 0;\n        packet.serverStatus = 2;\n        return packet;\n    }\n\n    public static void writeErrMessage(ServerConnection c, String msg) {\n        c.writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR, msg);\n    }\n\n    public static void main(String[] args) {\n        String sql = \"migrate    -table=test  -add=dn2,dn3,dn4  \" + \" \\n -additional=\\\"a=b\\\"\";\n        Map map = parse(sql);\n        System.out.println();\n        for (int i = 0; i < 100; i++) {\n            System.out.println(i % 5);\n        }\n\n        TaskNode taskNode = new TaskNode();\n        taskNode.setSql(sql);\n\n\n        System.out.println(new String(JSON.toJSONBytes(taskNode)));\n    }\n\n    private static Map<String, String> parse(String sql) {\n        Map<String, String> map = new HashMap<>();\n        List<String> rtn = Splitter.on(CharMatcher.whitespace()).omitEmptyStrings().splitToList(sql);\n        for (String s : rtn) {\n            if (s.contains(\"=\")) {\n                int dindex = s.indexOf(\"=\");\n                if (s.startsWith(\"-\")) {\n                    String key = s.substring(1, dindex).toLowerCase().trim();\n                    String value = s.substring(dindex + 1).trim();\n                    map.put(key, value);\n                } else if (s.startsWith(\"--\")) {\n                    String key = s.substring(2, dindex).toLowerCase().trim();\n                    String value = s.substring(dindex + 1).trim();\n                    map.put(key, value);\n                }\n            }\n        }\n        return map;\n    }\n\n    public static String backup() throws Exception {\n        LocalDateTime now = LocalDateTime.now();\n        Path path = Paths.get(SystemConfig.getHomePath()).resolve(\"backup_\" + now.getYear() + \"_\" + now.getMonthOfYear() + \"_\" + now.getDayOfMonth() + \"_\" + now.getHourOfDay() + \"_\" + now.getMinuteOfHour());\n        if (!Files.exists(path)) {\n            Files.createDirectory(path);\n        }\n        List<String> strings = ZKUtils.getConnection().getChildren().forPath(ZKUtils.getZKBasePath() + \"ruledata\");\n        for (String s : strings) {\n            byte[] bytes = ZKUtils.getConnection().getData().forPath(ZKUtils.getZKBasePath() + \"ruledata/\" + s);\n            Files.write(path.resolve(s), bytes);\n        }\n        byte[] bytes = ZKUtils.getConnection().getData().forPath(ZKUtils.getZKBasePath() + \"schema/schema\");\n        Files.write(path.resolve(\"schema.json\"), bytes);\n        bytes = ZKUtils.getConnection().getData().forPath(ZKUtils.getZKBasePath() + \"rules/function\");\n        Files.write(path.resolve(\"function.json\"), bytes);\n        return path.toAbsolutePath().toString();\n    }\n\n    private static boolean isJson(String str) {\n        if (StringUtil.isEmpty(str)) return false;\n        str = str.trim();\n        if (str.startsWith(\"{\") && str.endsWith(\"}\")) return true;\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/MysqlInformationSchemaHandler.java",
    "content": "package io.mycat.server.handler;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.util.SchemaUtil;\n\n\n/**\n * 对 PhpAdmin's 控制台操作进行支持 \n * \n * 如：SELECT * FROM information_schema.CHARACTER_SETS 等相关语句进行模拟返回\n * \n * @author zhuam\n *\n */\npublic class MysqlInformationSchemaHandler {\n\t\n\t/**\n\t * 写入数据包\n\t * @param field_count\n\t * @param fields\n\t * @param c\n\t */\n\tprivate static void doWrite(int field_count, FieldPacket[] fields, ServerConnection c) {\n\t\t\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tResultSetHeaderPacket header = PacketUtil.getHeader(field_count);\n\t\tbyte packetId = header.packetId;\n\t\tbuffer = header.write(buffer, c, true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tfield.packetId = ++packetId;\n\t\t\tbuffer = field.write(buffer, c, true);\n\t\t}\n\n\t\t// write eof\n\t\tEOFPacket eof = new EOFPacket();\n\t\teof.packetId = ++packetId;\n\t\tbuffer = eof.write(buffer, c, true);\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c, true);\n\n\t\t// post write\n\t\tc.write(buffer);\n\t\t\n\t}\n\t\n\tpublic static void handle(String sql, ServerConnection c) {\n\t\t\n\t\tSchemaUtil.SchemaInfo schemaInfo = SchemaUtil.parseSchema(sql);\n\t\tif ( schemaInfo != null ) {\n\t\t\t\n\t\t\tif ( schemaInfo.table.toUpperCase().equals(\"CHARACTER_SETS\") ) {\n\t\t\t\t\n\t\t\t\t//模拟列头\n\t\t\t\tint field_count = 4;\n\t\t\t    FieldPacket[] fields = new FieldPacket[field_count];\n\t\t\t    fields[0] = PacketUtil.getField(\"CHARACTER_SET_NAME\", Fields.FIELD_TYPE_VAR_STRING);\n\t\t\t\tfields[1] = PacketUtil.getField(\"DEFAULT_COLLATE_NAME\", Fields.FIELD_TYPE_VAR_STRING);\n\t\t\t    fields[2] = PacketUtil.getField(\"DESCRIPTION\", Fields.FIELD_TYPE_VAR_STRING);\n\t\t\t\tfields[3] = PacketUtil.getField(\"MAXLEN\", Fields.FIELD_TYPE_LONG);\n\t\t\t\t\n\t\t\t\tdoWrite(field_count, fields, c);\t\t\t\t\n\t\t\t\t\n\t\t\t} else {\n\t\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\t}\t\t\t\n\t\t\t\n\t\t} else {\n\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t}\t\t\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/MysqlProcHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.server.ServerConnection;\n\npublic class MysqlProcHandler\n{\n    private static final int FIELD_COUNT = 2;\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\n    static\n    {\n        fields[0] = PacketUtil.getField(\"name\",\n                Fields.FIELD_TYPE_VAR_STRING);\n        fields[1] = PacketUtil.getField(\"type\", Fields.FIELD_TYPE_VAR_STRING);\n    }\n\n    public static void handle(String stmt, ServerConnection c)\n    {\n\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n        byte packetId = header.packetId;\n        buffer = header.write(buffer, c, true);\n\n        // write fields\n        for (FieldPacket field : fields)\n        {\n            field.packetId = ++packetId;\n            buffer = field.write(buffer, c, true);\n        }\n\n        // write eof\n        EOFPacket eof = new EOFPacket();\n        eof.packetId = ++packetId;\n        buffer = eof.write(buffer, c, true);\n\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c, true);\n\n        // post write\n        c.write(buffer);\n\n    }\n\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/SavepointHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.server.ServerConnection;\n\n/**\n * @author mycat\n */\npublic final class SavepointHandler {\n\n    public static void handle(String stmt, ServerConnection c) {\n        c.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, \"Unsupported statement\");\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/SelectHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport io.mycat.route.parser.util.ParseUtil;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.parser.ServerParseSelect;\nimport io.mycat.server.response.ClientHeartbeatResponse;\nimport io.mycat.server.response.SelectDatabase;\nimport io.mycat.server.response.SelectIdentity;\nimport io.mycat.server.response.SelectLastInsertId;\nimport io.mycat.server.response.SelectTxReadOnly;\nimport io.mycat.server.response.SelectUser;\nimport io.mycat.server.response.SelectVersion;\nimport io.mycat.server.response.SelectVersionComment;\nimport io.mycat.server.response.SessionIncrement;\nimport io.mycat.server.response.SessionIsolation;\n\n/**\n * @author mycat\n */\npublic final class SelectHandler {\n    private static final int HEART_BEAT_SQL_MAX_LENGTH = \"SELECT 1 FROM DUAL\".length();\n    private static Set<String> CLIENT_HEART_BEAT_SQLS = new HashSet<String>(2);\n    static {\n        CLIENT_HEART_BEAT_SQLS.add(\"SELECT 1\");\n        CLIENT_HEART_BEAT_SQLS.add(\"SELECT 1 FROM DUAL\");\n    }\n\n\n\tpublic static void handle(String stmt, ServerConnection c, int offs) {\n\t\tint offset = offs;\n\t\tc.setExecuteSql(null);\n\t\tswitch (ServerParseSelect.parse(stmt, offs)) {\n\t\tcase ServerParseSelect.VERSION_COMMENT:\n\t\t\tSelectVersionComment.response(c);\n\t\t\tbreak;\n\t\tcase ServerParseSelect.DATABASE:\n\t\t\tSelectDatabase.response(c);\n\t\t\tbreak;\n\t\tcase ServerParseSelect.USER:\n\t\t\tSelectUser.response(c);\n\t\t\tbreak;\n\t\tcase ServerParseSelect.VERSION:\n\t\t\tSelectVersion.response(c);\n\t\t\tbreak;\n\t\tcase ServerParseSelect.SESSION_INCREMENT:\n\t\t\tSessionIncrement.response(c);\n\t\t\tbreak;\n\t\tcase ServerParseSelect.SESSION_ISOLATION:\n\t\t\tSessionIsolation.response(c);\n\t\t\tbreak;\n\t\tcase ServerParseSelect.LAST_INSERT_ID:\n\t\t\t// offset = ParseUtil.move(stmt, 0, \"select\".length());\n\t\t\tloop:for (int l=stmt.length(); offset < l; ++offset) {\n\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\tcase ' ':\n\t\t\t\t\tcontinue;\n\t\t\t\tcase '/':\n\t\t\t\tcase '#':\n\t\t\t\t\toffset = ParseUtil.comment(stmt, offset);\n\t\t\t\t\tcontinue;\n\t\t\t\tcase 'L':\n\t\t\t\tcase 'l':\n\t\t\t\t\tbreak loop;\n\t\t\t\t}\n\t\t\t}\n\t\t\toffset = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, offset);\n\t\t\toffset = ServerParseSelect.skipAs(stmt, offset);\n\t\t\tSelectLastInsertId.response(c, stmt, offset);\n\t\t\tbreak;\n\t\tcase ServerParseSelect.IDENTITY:\n\t\t\t// offset = ParseUtil.move(stmt, 0, \"select\".length());\n\t\t\tloop:for (int l=stmt.length(); offset < l; ++offset) {\n\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\tcase ' ':\n\t\t\t\t\tcontinue;\n\t\t\t\tcase '/':\n\t\t\t\tcase '#':\n\t\t\t\t\toffset = ParseUtil.comment(stmt, offset);\n\t\t\t\t\tcontinue;\n\t\t\t\tcase '@':\n\t\t\t\t\tbreak loop;\n\t\t\t\t}\n\t\t\t}\n\t\t\tint indexOfAtAt = offset;\n\t\t\toffset += 2;\n\t\t\toffset = ServerParseSelect.indexAfterIdentity(stmt, offset);\n\t\t\tString orgName = stmt.substring(indexOfAtAt, offset);\n\t\t\toffset = ServerParseSelect.skipAs(stmt, offset);\n\t\t\tSelectIdentity.response(c, stmt, offset, orgName);\n\t\t\tbreak;\n            case ServerParseSelect.SELECT_VAR_ALL:\n\t\t\t\tc.execute(stmt, ServerParse.SELECT);\n\t\t\t\tbreak;\n\t\t\tcase ServerParseSelect.SESSION_TX_READ_ONLY:\n\t\t\t\tSelectTxReadOnly.response(c);\n\t\t\t\tbreak;\n\t\tdefault:\n\t\t\tc.setExecuteSql(stmt);\n            if (isClientHeartbeatSql(stmt)) {\n                ClientHeartbeatResponse.response(c);\n            } else {\n                c.execute(stmt, ServerParse.SELECT);\n            }\n\t\t}\n\t}\n\n    private static boolean isClientHeartbeatSql(String sql) {\n        if (sql.length() > HEART_BEAT_SQL_MAX_LENGTH) {\n            return false;\n        } else {\n            return CLIENT_HEART_BEAT_SQLS.contains(sql.toUpperCase());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/ServerLoadDataInfileHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.expr.*;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement;\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\nimport com.google.common.collect.Lists;\nimport com.google.common.io.Files;\nimport com.univocity.parsers.csv.CsvParser;\nimport com.univocity.parsers.csv.CsvParserSettings;\nimport io.mycat.MycatServer;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.net.handler.LoadDataInfileHandler;\nimport io.mycat.net.mysql.BinaryPacket;\nimport io.mycat.net.mysql.RequestFilePacket;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.route.function.SlotFunction;\nimport io.mycat.route.parser.druid.DruidShardingParseInfo;\nimport io.mycat.route.parser.druid.MycatStatementParser;\nimport io.mycat.route.parser.druid.RouteCalculateUnit;\nimport io.mycat.route.util.RouterUtil;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.sqlengine.mpp.LoadData;\nimport io.mycat.util.ObjectUtil;\nimport io.mycat.util.StringUtil;\nimport org.apache.commons.csv.CSVFormat;\nimport org.apache.commons.csv.CSVRecord;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.*;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\nimport java.sql.SQLNonTransientException;\nimport java.util.*;\n\n/**\n * mysql命令行客户端也需要启用local file权限，加参数--local-infile=1\n * jdbc则正常，不用设置\n * load data sql中的CHARACTER SET 'gbk'   其中的字符集必须引号括起来，否则druid解析出错\n */\npublic final class ServerLoadDataInfileHandler implements LoadDataInfileHandler {\n    private static final Logger LOGGER = LoggerFactory.getLogger(ServerLoadDataInfileHandler.class);\n    private ServerConnection serverConnection;\n    private String sql;\n    private String fileName;\n    private List<SQLExpr> varColumns;\n    private byte packID = 0;\n    private MySqlLoadDataInFileStatement statement;\n\n    private Map<String, LoadData> routeResultMap = new HashMap<>();\n\n    private LoadData loadData;\n    private ByteArrayOutputStream tempByteBuffer;\n    private long tempByteBuffrSize = 0;\n    private String tempFile;\n    private boolean isHasStoreToFile = false;\n    private String tempPath;\n    private String tableName;\n    private TableConfig tableConfig;\n    private int partitionColumnIndex = -1;\n    private LayerCachePool tableId2DataNodeCache;\n    private SchemaConfig schema;\n    private boolean isStartLoadData = false;\n\n    private boolean shoudAddSlot = false;\n\n    public int getPackID() {\n        return packID;\n    }\n\n    public void setPackID(byte packID) {\n        this.packID = packID;\n    }\n\n    public ServerLoadDataInfileHandler(ServerConnection serverConnection) {\n        this.serverConnection = serverConnection;\n\n    }\n\n    private static String parseFileName(String sql) {\n        if (sql.contains(\"'\")) {\n            int beginIndex = sql.indexOf(\"'\");\n            return sql.substring(beginIndex + 1, sql.indexOf(\"'\", beginIndex + 1));\n        } else if (sql.contains(\"\\\"\")) {\n            int beginIndex = sql.indexOf(\"\\\"\");\n            return sql.substring(beginIndex + 1, sql.indexOf(\"\\\"\", beginIndex + 1));\n        }\n        return null;\n    }\n\n\n    private void parseLoadDataPram() {\n        loadData = new LoadData();\n        SQLTextLiteralExpr rawLineEnd = (SQLTextLiteralExpr) statement.getLinesTerminatedBy();\n        String lineTerminatedBy = rawLineEnd == null ? \"\\n\" : rawLineEnd.getText();\n        loadData.setLineTerminatedBy(lineTerminatedBy);\n\n        SQLTextLiteralExpr rawFieldEnd = (SQLTextLiteralExpr) statement.getColumnsTerminatedBy();\n        String fieldTerminatedBy = rawFieldEnd == null ? \"\\t\" : rawFieldEnd.getText();\n        loadData.setFieldTerminatedBy(fieldTerminatedBy);\n\n        SQLTextLiteralExpr rawEnclosed = (SQLTextLiteralExpr) statement.getColumnsEnclosedBy();\n        String enclose = rawEnclosed == null ? null : rawEnclosed.getText();\n        loadData.setEnclose(enclose);\n\n        SQLTextLiteralExpr escapseExpr = (SQLTextLiteralExpr) statement.getColumnsEscaped();\n        String escapse = escapseExpr == null ? \"\\\\\" : escapseExpr.getText();\n        loadData.setEscape(escapse);\n        String charset = statement.getCharset() != null ? statement.getCharset() : serverConnection.getCharset();\n        loadData.setCharset(charset);\n        loadData.setFileName(fileName);\n    }\n\n\n    @Override\n    public void start(String sql) {\n        clear();\n        this.sql = sql;\n\n\n        SQLStatementParser parser = new MycatStatementParser(sql);\n        statement = (MySqlLoadDataInFileStatement) parser.parseStatement();\n        if (statement.getSetList().size() > 0) {\n            varColumns = parseParam2RealColumn(statement);\n        }\n        fileName = parseFileName(sql);\n\n        if (fileName == null) {\n            serverConnection.writeErrMessage(ErrorCode.ER_FILE_NOT_FOUND, \" file name is null !\");\n            clear();\n            return;\n        }\n        schema = MycatServer.getInstance().getConfig()\n                .getSchemas().get(serverConnection.getSchema());\n        if (schema == null) {\n            throw new RuntimeException(\"please sql:use schema  before load data\");\n        }\n        tableId2DataNodeCache = (LayerCachePool) MycatServer.getInstance().getCacheService().getCachePool(\"TableID2DataNodeCache\");\n        tableName = statement.getTableName().getSimpleName().toUpperCase();\n        tableConfig = schema.getTables().get(tableName);\n        if (tableConfig.getRule() != null && tableConfig.getRule().getRuleAlgorithm() instanceof SlotFunction) {\n            shoudAddSlot = true;\n        }\n        tempPath = SystemConfig.getHomePath() + File.separator + \"temp\" + File.separator + serverConnection.getId() + File.separator;\n        tempFile = tempPath + \"clientTemp.txt\";\n        tempByteBuffer = new ByteArrayOutputStream();\n\n        List<SQLExpr> columns = statement.getColumns();\n        if (tableConfig != null) {\n            String pColumn = getPartitionColumn();\n            //如果有变量set表达式，columns里面会包含变量的名称，改用varColumns以获取真实的列名称\n            List<SQLExpr> columnsTmp = varColumns == null ? columns : varColumns;\n            if (pColumn != null && columnsTmp != null && columns.size() > 0) {\n                for (int i = 0, columnsSize = columnsTmp.size(); i < columnsSize; i++) {\n                    String column = StringUtil.removeBackquote(columnsTmp.get(i).toString());\n                    if (pColumn.equalsIgnoreCase(column)) {\n                        partitionColumnIndex = i;\n                    }\n                    if (\"_slot\".equalsIgnoreCase(column)) {\n                        shoudAddSlot = false;\n                    }\n                }\n\n            }\n        }\n        if (shoudAddSlot) {\n            columns.add(new SQLIdentifierExpr(\"_slot\"));\n        }\n        parseLoadDataPram();\n        if (statement.isLocal()) {\n            isStartLoadData = true;\n            //向客户端请求发送文件\n            ByteBuffer buffer = serverConnection.allocate();\n            RequestFilePacket filePacket = new RequestFilePacket();\n            filePacket.fileName = fileName.getBytes();\n            filePacket.packetId = 1;\n            filePacket.write(buffer, serverConnection, true);\n        } else {\n            if (!new File(fileName).exists()) {\n                serverConnection.writeErrMessage(ErrorCode.ER_FILE_NOT_FOUND, fileName + \" is not found!\");\n                clear();\n            } else {\n                parseFileByLine(fileName, loadData.getCharset(), loadData.getLineTerminatedBy());\n                RouteResultset rrs = buildResultSet(routeResultMap);\n                if (rrs != null) {\n                    flushDataToFile();\n                    isStartLoadData = false;\n                    serverConnection.getSession2().execute(rrs, ServerParse.LOAD_DATA_INFILE_SQL);\n                }\n\n            }\n        }\n    }\n\n    /**\n     *\n     * load data infile使用set子句时将用户变量改成真实的列名\n     * 比如下面语句LOAD DATA INFILE 'file.txt'\n     INTO TABLE t1\n     (column1, @var1,@var2)\n     SET column2 = @var1/100,columns=100/(@var2/200));\n     最后真实的列应该是{column1,column2,columns}\n     语法参考： 参考https://dev.mysql.com/doc/refman/5.7/en/load-data.html\n     * @param statement\n     * @return\n     */\n    private List<SQLExpr> parseParam2RealColumn(MySqlLoadDataInFileStatement statement) {\n        String HAS_FILTERED = \"HAS_FILTERED\";\n        List<SQLExpr> realColumnList = new ArrayList<>(statement.getColumns().size());\n\n        for (SQLExpr columnExpr : statement.getColumns()) {\n            boolean hasSetColumn = false;\n            if (columnExpr instanceof SQLVariantRefExpr) {\n                SQLVariantRefExpr varColumn = (SQLVariantRefExpr) columnExpr;\n                for (SQLExpr setExpr : statement.getSetList()) {\n                    if (setExpr.getAttribute(HAS_FILTERED) != null) {\n                        // 已经被过滤过\n                        continue;\n                    }\n\n                    // 找到包含变量的set表达式\n                    if (setExpr.toString().contains(varColumn.toString())) {\n                        if (setExpr instanceof SQLBinaryOpExpr) {\n                            realColumnList.add(((SQLBinaryOpExpr) setExpr).getLeft());\n                            hasSetColumn = true;\n                        }\n                        setExpr.putAttribute(HAS_FILTERED, true);// 标记已经被过滤过\n                        break;\n                    }\n                }\n            }\n            if (!hasSetColumn) {\n                realColumnList.add(columnExpr);\n            }\n        }\n\n        return realColumnList;\n    }\n\n    @Override\n    public void handle(byte[] data) {\n\n        try {\n            if (sql == null) {\n                serverConnection.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR,\n                        \"Unknown command\");\n                clear();\n                return;\n            }\n            BinaryPacket packet = new BinaryPacket();\n            ByteArrayInputStream inputStream = new ByteArrayInputStream(data, 0, data.length);\n            packet.read(inputStream);\n\n            saveByteOrToFile(packet.data, false);\n\n\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n\n\n    }\n\n    private synchronized void saveByteOrToFile(byte[] data, boolean isForce) {\n\n        if (data != null) {\n            tempByteBuffrSize = tempByteBuffrSize + data.length;\n            try {\n                tempByteBuffer.write(data);\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n        }\n\n        if ((isForce && isHasStoreToFile) || tempByteBuffrSize > 200 * 1024 * 1024)    //超过200M 存文件\n        {\n            FileOutputStream channel = null;\n            try {\n                File file = new File(tempFile);\n                Files.createParentDirs(file);\n                channel = new FileOutputStream(file, true);\n\n                tempByteBuffer.writeTo(channel);\n                tempByteBuffer = new ByteArrayOutputStream();\n                tempByteBuffrSize = 0;\n                isHasStoreToFile = true;\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            } finally {\n                try {\n                    if (channel != null) {\n                        channel.close();\n                    }\n\n                } catch (IOException ignored) {\n\n                }\n            }\n\n\n        }\n    }\n\n\n    private RouteResultset tryDirectRoute(String sql, String[] lineList) {\n\n        RouteResultset rrs = new RouteResultset(sql, ServerParse.INSERT);\n        rrs.setLoadData(true);\n        if (tableConfig == null && schema.getDataNode() != null) {\n            //走默认节点\n            RouteResultsetNode rrNode = new RouteResultsetNode(schema.getDataNode(), ServerParse.INSERT, sql);\n            rrNode.setSource(rrs);\n            rrs.setNodes(new RouteResultsetNode[]{rrNode});\n            return rrs;\n        } else if (tableConfig != null && tableConfig.isGlobalTable()) {\n            ArrayList<String> dataNodes = tableConfig.getDataNodes();\n            RouteResultsetNode[] rrsNodes = new RouteResultsetNode[dataNodes.size()];\n            for (int i = 0, dataNodesSize = dataNodes.size(); i < dataNodesSize; i++) {\n                String dataNode = dataNodes.get(i);\n                RouteResultsetNode rrNode = new RouteResultsetNode(dataNode, ServerParse.INSERT, sql);\n                rrsNodes[i] = rrNode;\n                if (rrs.getDataNodeSlotMap().containsKey(dataNode)) {\n                    rrsNodes[i].setSlot(rrs.getDataNodeSlotMap().get(dataNode));\n                }\n                rrsNodes[i].setSource(rrs);\n            }\n\n            rrs.setNodes(rrsNodes);\n            return rrs;\n        } else if (tableConfig != null) {\n            DruidShardingParseInfo ctx = new DruidShardingParseInfo();\n            ctx.addTable(tableName);\n\n\n            if (partitionColumnIndex == -1 || partitionColumnIndex >= lineList.length) {\n                return null;\n            } else {\n                String value = lineList[partitionColumnIndex];\n                RouteCalculateUnit routeCalculateUnit = new RouteCalculateUnit();\n                routeCalculateUnit.addShardingExpr(tableName, getPartitionColumn(), parseFieldString(value, loadData.getEnclose()));\n                ctx.addRouteCalculateUnit(routeCalculateUnit);\n                try {\n                    SortedSet<RouteResultsetNode> nodeSet = new TreeSet<RouteResultsetNode>();\n                    for (RouteCalculateUnit unit : ctx.getRouteCalculateUnits()) {\n                        RouteResultset rrsTmp = RouterUtil.tryRouteForTables(schema, ctx, unit, rrs, false, tableId2DataNodeCache);\n                        if (rrsTmp != null) {\n                            for (RouteResultsetNode node : rrsTmp.getNodes()) {\n                                nodeSet.add(node);\n                            }\n                        }\n                    }\n\n                    RouteResultsetNode[] nodes = new RouteResultsetNode[nodeSet.size()];\n                    int i = 0;\n                    for (Iterator<RouteResultsetNode> iterator = nodeSet.iterator(); iterator.hasNext(); ) {\n                        nodes[i] = (RouteResultsetNode) iterator.next();\n                        i++;\n                    }\n\n                    rrs.setNodes(nodes);\n                    return rrs;\n                } catch (SQLNonTransientException e) {\n                    throw new RuntimeException(e);\n                }\n            }\n\n\n        }\n\n        return null;\n    }\n\n\n    private void parseOneLine(List<SQLExpr> columns, String tableName, String[] line, boolean toFile, String lineEnd) {\n\n        RouteResultset rrs = tryDirectRoute(sql, line);\n        if (rrs == null || rrs.getNodes() == null || rrs.getNodes().length == 0) {\n\n            String insertSql = makeSimpleInsert(columns, line, tableName, true);\n            rrs = serverConnection.routeSQL(insertSql, ServerParse.INSERT);\n        }\n\n\n        if (rrs == null || rrs.getNodes() == null || rrs.getNodes().length == 0) {\n            //无路由处理\n        } else {\n            for (RouteResultsetNode routeResultsetNode : rrs.getNodes()) {\n                String name = routeResultsetNode.getName();\n                LoadData data = routeResultMap.get(name);\n                if (data == null) {\n                    data = new LoadData();\n                    data.setCharset(loadData.getCharset());\n                    data.setEnclose(loadData.getEnclose());\n                    data.setFieldTerminatedBy(loadData.getFieldTerminatedBy());\n                    data.setLineTerminatedBy(loadData.getLineTerminatedBy());\n                    data.setEscape(loadData.getEscape());\n                    routeResultMap.put(name, data);\n                }\n\n                String jLine = joinField(line, data);\n                if (shoudAddSlot) {\n                    jLine = jLine + loadData.getFieldTerminatedBy() + routeResultsetNode.getSlot();\n                }\n                if (data.getData() == null) {\n                    data.setData(Lists.newArrayList(jLine));\n                } else {\n\n                    data.getData().add(jLine);\n\n                }\n\n                if (toFile\n                        //避免当导入数据跨多分片时内存溢出的情况\n                        && data.getData().size() > 10000) {\n                    saveDataToFile(data, name);\n                }\n\n            }\n        }\n    }\n\n    private void flushDataToFile() {\n        for (Map.Entry<String, LoadData> stringLoadDataEntry : routeResultMap.entrySet()) {\n            LoadData value = stringLoadDataEntry.getValue();\n            if (value.getFileName() != null && value.getData() != null && value.getData().size() > 0) {\n                saveDataToFile(value, stringLoadDataEntry.getKey());\n            }\n        }\n\n    }\n\n    private void saveDataToFile(LoadData data, String dnName) {\n        if (data.getFileName() == null) {\n            String dnPath = tempPath + dnName + \".txt\";\n            data.setFileName(dnPath);\n        }\n\n        File dnFile = new File(data.getFileName());\n        try {\n            if (!dnFile.exists()) {\n                Files.createParentDirs(dnFile);\n            }\n            Files.append(joinLine(data.getData(), data), dnFile, Charset.forName(loadData.getCharset()));\n\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        } finally {\n            data.setData(null);\n\n        }\n\n\n    }\n\n    private String joinLine(List<String> data, LoadData loadData) {\n        StringBuilder sb = new StringBuilder();\n        for (String s : data) {\n            sb.append(s).append(loadData.getLineTerminatedBy());\n        }\n        return sb.toString();\n    }\n\n\n    private String joinField(String[] src, LoadData loadData) {\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0, srcLength = src.length; i < srcLength; i++) {\n            String s = src[i] != null ? src[i] : \"\";\n            if (loadData.getEnclose() == null) {\n                sb.append(s);\n            } else {\n                sb.append(loadData.getEnclose()).append(s.replace(loadData.getEnclose(), loadData.getEscape() + loadData.getEnclose())).append(loadData.getEnclose());\n            }\n            if (i != srcLength - 1) {\n                sb.append(loadData.getFieldTerminatedBy());\n            }\n        }\n\n        return sb.toString();\n\n    }\n\n\n    private RouteResultset buildResultSet(Map<String, LoadData> routeMap) {\n        statement.setLocal(true);//强制local\n        SQLLiteralExpr fn = new SQLCharExpr(fileName);    //默认druid会过滤掉路径的分隔符，所以这里重新设置下\n        statement.setFileName(fn);\n        String srcStatement = statement.toString();\n        RouteResultset rrs = new RouteResultset(srcStatement, ServerParse.LOAD_DATA_INFILE_SQL);\n        rrs.setLoadData(true);\n        rrs.setStatement(srcStatement);\n        rrs.setAutocommit(serverConnection.isAutocommit());\n        rrs.setFinishedRoute(true);\n        int size = routeMap.size();\n        RouteResultsetNode[] routeResultsetNodes = new RouteResultsetNode[size];\n        int index = 0;\n        for (String dn : routeMap.keySet()) {\n            RouteResultsetNode rrNode = new RouteResultsetNode(dn, ServerParse.LOAD_DATA_INFILE_SQL, srcStatement);\n            rrNode.setSource(rrs);\n            rrNode.setTotalNodeSize(size);\n            rrNode.setStatement(srcStatement);\n            LoadData newLoadData = new LoadData();\n            ObjectUtil.copyProperties(loadData, newLoadData);\n            newLoadData.setLocal(true);\n            LoadData loadData1 = routeMap.get(dn);\n            //  if (isHasStoreToFile)\n            if (loadData1.getFileName() != null)//此处判断是否有保存分库load的临时文件dn1.txt/dn2.txt，不是判断是否有clientTemp.txt\n            {\n                newLoadData.setFileName(loadData1.getFileName());\n            } else {\n                newLoadData.setData(loadData1.getData());\n            }\n            rrNode.setLoadData(newLoadData);\n\n            routeResultsetNodes[index] = rrNode;\n            index++;\n        }\n        rrs.setNodes(routeResultsetNodes);\n        return rrs;\n    }\n\n\n    private String makeSimpleInsert(List<SQLExpr> columns, String[] fields, String table, boolean isAddEncose) {\n        StringBuilder sb = new StringBuilder();\n        sb.append(LoadData.loadDataHint).append(\"insert into \").append(table.toUpperCase());\n        if (columns != null && columns.size() > 0) {\n            sb.append(\"(\");\n            for (int i = 0, columnsSize = columns.size(); i < columnsSize; i++) {\n                SQLExpr column = columns.get(i);\n                sb.append(column.toString());\n                if (i != columnsSize - 1) {\n                    sb.append(\",\");\n                }\n            }\n            sb.append(\") \");\n        }\n\n        sb.append(\" values (\");\n        for (int i = 0, columnsSize = fields.length; i < columnsSize; i++) {\n            String column = fields[i];\n            if (isAddEncose) {\n                sb.append(\"'\").append(parseFieldString(column, loadData.getEnclose())).append(\"'\");\n            } else {\n                sb.append(column);\n            }\n            if (i != columnsSize - 1) {\n                sb.append(\",\");\n            }\n        }\n        sb.append(\")\");\n        return sb.toString();\n    }\n\n    private String parseFieldString(String value, String encose) {\n        if (encose == null || \"\".equals(encose) || value == null) {\n            return value;\n        } else if (value.startsWith(encose) && value.endsWith(encose)) {\n            return value.substring(encose.length() - 1, value.length() - encose.length());\n        }\n        return value;\n    }\n\n\n    @Override\n    public void end(byte packID) {\n        isStartLoadData = false;\n        this.packID = packID;\n        //load in data空包 结束\n        saveByteOrToFile(null, true);\n        List<SQLExpr> columns = statement.getColumns();\n        String tableName = statement.getTableName().getSimpleName();\n        if (isHasStoreToFile) {\n            parseFileByLine(tempFile, loadData.getCharset(), loadData.getLineTerminatedBy());\n        } else {\n            String reader = new String(tempByteBuffer.toByteArray(), Charset.forName(loadData.getCharset()));\n\n                    CSVFormat csvFormat = CSVFormat.newFormat(\n                    loadData.getFieldTerminatedBy().charAt(0))\n                    .withRecordSeparator(loadData.getLineTerminatedBy()).\n                            withSkipHeaderRecord(false).withTrim(false);\n\n            if (loadData.getEnclose() != null) {\n                csvFormat = csvFormat.withQuote(loadData.getEnclose().charAt(0));\n            }\n            if (loadData.getEscape() != null) {\n                csvFormat = csvFormat.withQuote(loadData.getEscape().charAt(0));\n            }\n            /*\n             *  fix bug #1074 : LOAD DATA local INFILE导入的所有Boolean类型全部变成了false\n             *  不可见字符将在CsvParser被当成whitespace过滤掉, 使用settings.trimValues(false)来避免被过滤掉\n             *  TODO : 设置trimValues(false)之后, 会引起字段值前后的空白字符无法被过滤!\n             */\n            try {\n                Iterable<CSVRecord> records = csvFormat.parse(new StringReader(reader));\n                String[] row = null;\n\n                for (CSVRecord record : records) {\n                    int size = record.size();\n                    row = new String[size];\n                    for (int i = 0; i < size; i++) {\n                        row[i] = record.get(i);\n                    }\n                    parseOneLine(columns, tableName, row, false, null);\n                }\n            } catch (Throwable e) {\n                LOGGER.error(\"解析csv格式错误\", e);\n            }\n        }\n\n        RouteResultset rrs = buildResultSet(routeResultMap);\n        if (rrs != null) {\n            flushDataToFile();\n            serverConnection.getSession2().execute(rrs, ServerParse.LOAD_DATA_INFILE_SQL);\n        }\n\n\n        // sendOk(++packID);\n\n\n    }\n\n\n    private void parseFileByLine(String file, String encode, String split) {\n        List<SQLExpr> columns = statement.getColumns();\n        CsvParserSettings settings = new CsvParserSettings();\n        settings.setMaxColumns(65535);\n        settings.setMaxCharsPerColumn(65535);\n        settings.getFormat().setLineSeparator(loadData.getLineTerminatedBy());\n        settings.getFormat().setDelimiter(loadData.getFieldTerminatedBy().charAt(0));\n        if (loadData.getEnclose() != null) {\n            settings.getFormat().setQuote(loadData.getEnclose().charAt(0));\n        }\n        if (loadData.getEscape() != null) {\n            settings.getFormat().setQuoteEscape(loadData.getEscape().charAt(0));\n        }\n        settings.getFormat().setNormalizedNewline(loadData.getLineTerminatedBy().charAt(0));\n        /*\n         *  fix #1074 : LOAD DATA local INFILE导入的所有Boolean类型全部变成了false\n         *  不可见字符将在CsvParser被当成whitespace过滤掉, 使用settings.trimValues(false)来避免被过滤掉\n         *  TODO : 设置trimValues(false)之后, 会引起字段值前后的空白字符无法被过滤!\n         */\n        settings.trimValues(false);\n        CsvParser parser = new CsvParser(settings);\n        InputStreamReader reader = null;\n        FileInputStream fileInputStream = null;\n        try {\n\n            fileInputStream = new FileInputStream(file);\n            reader = new InputStreamReader(fileInputStream, encode);\n            parser.beginParsing(reader);\n            String[] row = null;\n\n            while ((row = parser.parseNext()) != null) {\n                parseOneLine(columns, tableName, row, true, loadData.getLineTerminatedBy());\n            }\n\n\n        } catch (FileNotFoundException | UnsupportedEncodingException e) {\n            throw new RuntimeException(e);\n        } finally {\n            parser.stopParsing();\n            if (fileInputStream != null) {\n                try {\n                    fileInputStream.close();\n                } catch (IOException e) {\n                    throw new RuntimeException(e);\n                }\n            }\n            if (reader != null) {\n                try {\n                    reader.close();\n                } catch (IOException e) {\n                    throw new RuntimeException(e);\n                }\n            }\n\n        }\n\n    }\n\n\n    public void clear() {\n        isStartLoadData = false;\n        tableId2DataNodeCache = null;\n        schema = null;\n        tableConfig = null;\n        isHasStoreToFile = false;\n        packID = 0;\n        tempByteBuffrSize = 0;\n        tableName = null;\n        partitionColumnIndex = -1;\n        varColumns = null;\n        if (tempFile != null) {\n            File temp = new File(tempFile);\n            if (temp.exists()) {\n                temp.delete();\n            }\n        }\n        if (tempPath != null && new File(tempPath).exists()) {\n            deleteFile(tempPath);\n        }\n        tempByteBuffer = null;\n        loadData = null;\n        sql = null;\n        fileName = null;\n        statement = null;\n        routeResultMap.clear();\n    }\n\n    @Override\n    public byte getLastPackId() {\n        return packID;\n    }\n\n    @Override\n    public boolean isStartLoadData() {\n        return isStartLoadData;\n    }\n\n    private String getPartitionColumn() {\n        String pColumn;\n        if (tableConfig.isSecondLevel()\n                && tableConfig.getParentTC().getPartitionColumn()\n                .equals(tableConfig.getParentKey())) {\n            pColumn = tableConfig.getJoinKey();\n        } else {\n            pColumn = tableConfig.getPartitionColumn();\n        }\n        return pColumn;\n    }\n\n    /**\n     * 删除目录及其所有子目录和文件\n     *\n     * @param dirPath 要删除的目录路径\n     * @throws Exception\n     */\n    private static void deleteFile(String dirPath) {\n        File fileDirToDel = new File(dirPath);\n        if (!fileDirToDel.exists()) {\n            return;\n        }\n        if (fileDirToDel.isFile()) {\n            fileDirToDel.delete();\n            return;\n        }\n        File[] fileList = fileDirToDel.listFiles();\n\n        for (int i = 0; i < fileList.length; i++) {\n            File file = fileList[i];\n            if (file.isFile() && file.exists()) {\n                boolean delete = file.delete();\n            } else if (file.isDirectory()) {\n                deleteFile(file.getAbsolutePath());\n                file.delete();\n            }\n        }\n        fileDirToDel.delete();\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/ServerPrepareHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport com.alibaba.druid.sql.SQLUtils;\nimport com.alibaba.druid.sql.ast.SQLReplaceable;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.*;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey;\nimport com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey;\nimport com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.escape.Escaper;\nimport com.google.common.escape.Escapers;\nimport com.google.common.escape.Escapers.Builder;\n\nimport io.mycat.backend.mysql.BindValue;\nimport io.mycat.backend.mysql.ByteUtil;\nimport io.mycat.backend.mysql.PreparedStatement;\nimport io.mycat.backend.mysql.nio.handler.PrepareRequestHandler;\nimport io.mycat.backend.mysql.nio.handler.PrepareRequestHandler.PrepareRequestCallback;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.Fields;\nimport io.mycat.net.handler.FrontendPrepareHandler;\nimport io.mycat.net.mysql.ExecutePacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.LongDataPacket;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.net.mysql.ResetPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.response.PreparedStmtResponse;\nimport io.mycat.util.HexFormatUtil;\n\n/**\n * @author mycat, CrazyPig, zhuam\n */\npublic class ServerPrepareHandler implements FrontendPrepareHandler {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ServerPrepareHandler.class);\n\n    private static Escaper varcharEscaper = null;\n\n    static {\n        Builder escapeBuilder = Escapers.builder();\n        escapeBuilder.addEscape('\\0', \"\\\\0\");\n        escapeBuilder.addEscape('\\'', \"\\\\'\");\n        escapeBuilder.addEscape('\\b', \"\\\\b\");\n        escapeBuilder.addEscape('\\n', \"\\\\n\");\n        escapeBuilder.addEscape('\\r', \"\\\\r\");\n        escapeBuilder.addEscape('\\\"', \"\\\\\\\"\");\n        escapeBuilder.addEscape('$', \"\\\\$\");\n        escapeBuilder.addEscape('\\\\', \"\\\\\\\\\");\n        varcharEscaper = escapeBuilder.build();\n    }\n\n    private ServerConnection source;\n\n    // java int是32位，long是64位；mysql协议里面定义的statementId是32位，因此用Integer\n    private static final AtomicInteger PSTMT_ID_GENERATOR = new AtomicInteger(0);\n    //    private static final Map<String, PreparedStatement> pstmtForSql = new ConcurrentHashMap<>();\n    private static final Map<Long, PreparedStatement> pstmtForId = new ConcurrentHashMap<>();\n    private int maxPreparedStmtCount;\n\n    public ServerPrepareHandler(ServerConnection source, int maxPreparedStmtCount) {\n        this.source = source;\n        this.maxPreparedStmtCount = maxPreparedStmtCount;\n    }\n\n    @Override\n    public void prepare(String sql) {\n\n        LOGGER.debug(\"use server prepare, sql: \" + sql);\n        PreparedStatement pstmt = null;\n        if (pstmt == null) {\n            // 解析获取字段个数和参数个数\n            int columnCount = 0;\n            int paramCount = getParamCount(sql);\n            if (paramCount > maxPreparedStmtCount) {\n                source.writeErrMessage(ErrorCode.ER_PS_MANY_PARAM,\n                        \"Prepared statement contains too many placeholders\");\n                return;\n            }\n            pstmt = new PreparedStatement(PSTMT_ID_GENERATOR.incrementAndGet(), sql,\n                    paramCount);\n            pstmtForId.put(pstmt.getId(), pstmt);\n            LOGGER.info(\"preparestatement  parepare id:{}\", pstmt.getId());\n        }\n        PreparedStmtResponse.response(pstmt, source);\n    }\n\n\n    @Override\n    public void sendLongData(byte[] data) {\n        LongDataPacket packet = new LongDataPacket();\n        packet.read(data);\n        long pstmtId = packet.getPstmtId();\n        LOGGER.info(\"preparestatement  long data id:{}\", pstmtId);\n        PreparedStatement pstmt = pstmtForId.get(pstmtId);\n        if (pstmt != null) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"send long data to prepare sql : \" + pstmtForId.get(pstmtId));\n            }\n            long paramId = packet.getParamId();\n            try {\n                pstmt.appendLongData(paramId, packet.getLongData());\n            } catch (IOException e) {\n                source.writeErrMessage(ErrorCode.ERR_FOUND_EXCEPTION, e.getMessage());\n            }\n        }\n    }\n\n    @Override\n    public void reset(byte[] data) {\n        ResetPacket packet = new ResetPacket();\n        packet.read(data);\n        long pstmtId = packet.getPstmtId();\n        LOGGER.info(\"preparestatement  long data id:{}\", pstmtId);\n        PreparedStatement pstmt = pstmtForId.get(pstmtId);\n        if (pstmt != null) {\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"reset prepare sql : \" + pstmtForId.get(pstmtId));\n            }\n            pstmt.resetLongData();\n            source.write(OkPacket.OK);\n        } else {\n            source.writeErrMessage(ErrorCode.ERR_FOUND_EXCEPTION,\n                    \"can not reset prepare statement : \" + pstmtForId.get(pstmtId));\n        }\n    }\n\n    @Override\n    public void execute(byte[] data) {\n        long pstmtId = ByteUtil.readUB4(data, 5);\n        PreparedStatement pstmt = null;\n        LOGGER.info(\"preparestatement  execute id:{}\", pstmtId);\n        if ((pstmt = pstmtForId.get(pstmtId)) == null) {\n            source.writeErrMessage(ErrorCode.ER_ERROR_WHEN_EXECUTING_COMMAND,\n                    \"Unknown pstmtId when executing.\");\n        } else {\n            ExecutePacket packet = new ExecutePacket(pstmt);\n            try {\n                packet.read(data, source.getCharset());\n            } catch (UnsupportedEncodingException e) {\n                source.writeErrMessage(ErrorCode.ER_ERROR_WHEN_EXECUTING_COMMAND, e.getMessage());\n                return;\n            }\n            BindValue[] bindValues = packet.values;\n            // 还原sql中的动态参数为实际参数值\n            String sql = prepareStmtBindValue(pstmt, bindValues);\n            // 执行sql\n            source.getSession2().setPrepared(true);\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"execute prepare sql: \" + sql);\n            }\n\n            pstmt.resetLongData();\n            source.query(sql);\n        }\n    }\n\n\n    @Override\n    public void close(byte[] data) {\n        long pstmtId = ByteUtil.readUB4(data, 5); // 获取prepare stmt id\n        LOGGER.info(\"preparestatement  close id:{}\", pstmtId);\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"close prepare stmt, stmtId = \" + pstmtId);\n        }\n        PreparedStatement pstmt = pstmtForId.remove(pstmtId);\n    }\n\n    @Override\n    public void clear() {\n        this.pstmtForId.clear();\n//    this.pstmtForSql.clear();\n    }\n\n\n    // 获取预处理sql中预处理参数个数\n    private int getParamCount(String sql) {\n        char[] cArr = sql.toCharArray();\n        int count = 0;\n        for (int i = 0; i < cArr.length; i++) {\n            if (cArr[i] == '?') {\n                count++;\n            }\n        }\n        return count;\n    }\n\n    /**\n     * 组装sql语句,替换动态参数为实际参数值\n     */\n    private String prepareStmtBindValue(PreparedStatement pstmt, BindValue[] bindValues) {\n        String sql = pstmt.getStatement();\n        SQLStatement sqlStatement = SQLUtils.parseSingleMysqlStatement(sql);\n        boolean primitiveArg = !(sqlStatement instanceof SQLSelectStatement);\n        int[] paramTypes = pstmt.getParametersType();\n        sqlStatement.accept(new MySqlASTVisitorAdapter() {\n            @Override\n            public boolean visit(SQLVariantRefExpr x) {\n                BindValue bindValue = bindValues[x.getIndex()];\n                Object o = null;\n                if (bindValue.isNull) {\n                    SQLReplaceable parent = (SQLReplaceable) x.getParent();\n                    parent.replace(x, new SQLNullExpr());\n                    return false;\n                } else {\n                    if (primitiveArg && bindValue.value instanceof byte[]) {\n                        SQLReplaceable parent = (SQLReplaceable) x.getParent();\n                        parent.replace(x, new SQLHexExpr(HexFormatUtil.bytesToHexString((byte[]) bindValue.value)));\n                        return false;\n                    }\n                    switch (paramTypes[x.getIndex()] & 0xff) {\n                        case Fields.FIELD_TYPE_TINY:\n                            o = bindValue.byteBinding;\n                            break;\n                        case Fields.FIELD_TYPE_SHORT:\n                            o = bindValue.shortBinding;\n                            break;\n                        case Fields.FIELD_TYPE_LONG:\n                            o = bindValue.intBinding;\n                            break;\n                        case Fields.FIELD_TYPE_LONGLONG:\n                            o = (bindValue.longBinding);\n                            break;\n                        case Fields.FIELD_TYPE_FLOAT:\n                            o = bindValue.floatBinding;\n                            break;\n                        case Fields.FIELD_TYPE_DOUBLE:\n                            o = bindValue.doubleBinding;\n                            break;\n                        case Fields.FIELD_TYPE_TIME:\n                        case Fields.FIELD_TYPE_DATE:\n                        case Fields.FIELD_TYPE_DATETIME:\n                        case Fields.FIELD_TYPE_TIMESTAMP:\n                            o = bindValue.value;\n                            break;\n                        default:\n                            if (bindValue.value instanceof byte[]) {\n                                SQLReplaceable parent = (SQLReplaceable) x.getParent();\n                                byte[] bytes = (byte[]) bindValue.value;\n                                parent.replace(x, new SQLCharExpr(new String(bytes)));\n                                return false;\n                            }\n                            if (bindValue.value instanceof String) {\n                                SQLReplaceable parent = (SQLReplaceable) x.getParent();\n                                String value = (String) bindValue.value;\n                                parent.replace(x, new SQLCharExpr(value));\n                                return false;\n                            }\n                            throw new UnsupportedOperationException(\"unsupport \" + bindValue.value);\n                    }\n\n                    SQLReplaceable parent = (SQLReplaceable) x.getParent();\n                    parent.replace(x, SQLExprUtils.fromJavaObject(o));\n                    return false;\n                }\n            }\n\n        });\n        return sqlStatement.toString();\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/SetHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.Isolations;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.route.parser.util.ParseUtil;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.parser.ServerParseSet;\nimport io.mycat.server.response.CharacterSet;\nimport io.mycat.util.SetIgnoreUtil;\n\nimport static io.mycat.server.parser.ServerParseSet.AUTOCOMMIT_OFF;\nimport static io.mycat.server.parser.ServerParseSet.AUTOCOMMIT_ON;\nimport static io.mycat.server.parser.ServerParseSet.CHARACTER_SET_CLIENT;\nimport static io.mycat.server.parser.ServerParseSet.CHARACTER_SET_CONNECTION;\nimport static io.mycat.server.parser.ServerParseSet.CHARACTER_SET_RESULTS;\nimport static io.mycat.server.parser.ServerParseSet.NAMES;\nimport static io.mycat.server.parser.ServerParseSet.SQL_SELECT_LIMIT;\nimport static io.mycat.server.parser.ServerParseSet.TX_READONLY;\nimport static io.mycat.server.parser.ServerParseSet.TX_READWRITE;\nimport static io.mycat.server.parser.ServerParseSet.TX_READ_COMMITTED;\nimport static io.mycat.server.parser.ServerParseSet.TX_READ_UNCOMMITTED;\nimport static io.mycat.server.parser.ServerParseSet.TX_REPEATED_READ;\nimport static io.mycat.server.parser.ServerParseSet.TX_SERIALIZABLE;\nimport static io.mycat.server.parser.ServerParseSet.XA_FLAG_OFF;\nimport static io.mycat.server.parser.ServerParseSet.XA_FLAG_ON;\n\n/**\n * SET 语句处理\n * \n * @author mycat\n * @author zhuam\n */\npublic final class SetHandler {\n\t\n\tprivate static final Logger logger = LoggerFactory.getLogger(SetHandler.class);\n\t\n\tprivate static final byte[] AC_OFF = new byte[] { 7, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 };\n\t\t\n\tpublic static void handle(String stmt, ServerConnection c, int offset) {\n\t\t// System.out.println(\"SetHandler: \"+stmt);\n\t\tint rs = ServerParseSet.parse(stmt, offset);\n\t\tswitch (rs & 0xff) {\n\t\tcase AUTOCOMMIT_ON:\n\t\t\tif (c.isAutocommit()) {\n\t\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\t} else {\n\t\t\t\tc.setPreAcStates(true);\n\t\t\t\tc.commit();\n\t\t\t\tc.setAutocommit(true);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase AUTOCOMMIT_OFF: {\n\t\t\tif (c.isAutocommit()) {\n\t\t\t\tc.setAutocommit(false);\n\t\t\t\tc.setPreAcStates(false);\n\t\t\t}\n\t\t\tc.write(c.writeToBuffer(AC_OFF, c.allocate()));\n\t\t\tbreak;\n\t\t}\n\t\tcase XA_FLAG_ON: {\n\t\t\tif (c.isAutocommit()) {\n\t\t\t\tc.writeErrMessage(ErrorCode.ERR_WRONG_USED,\n\t\t\t\t\t\t\"set xa cmd on can't used in autocommit connection \");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tc.getSession2().setXATXEnabled(true);\n\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\tbreak;\n\t\t}\n\t\tcase XA_FLAG_OFF: {\n\t\t\tc.writeErrMessage(ErrorCode.ERR_WRONG_USED,\n\t\t\t\t\t\"set xa cmd off not for external use \");\n\t\t\treturn;\n\t\t}\n\t\tcase TX_READ_UNCOMMITTED: {\n\t\t\tc.setTxIsolation(Isolations.READ_UNCOMMITTED);\n\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\tbreak;\n\t\t}\n\t\tcase TX_READ_COMMITTED: {\n\t\t\tc.setTxIsolation(Isolations.READ_COMMITTED);\n\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\tbreak;\n\t\t}\n\t\tcase TX_REPEATED_READ: {\n\t\t\tc.setTxIsolation(Isolations.REPEATED_READ);\n\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\tbreak;\n\t\t}\n\t\tcase TX_SERIALIZABLE: {\n\t\t\tc.setTxIsolation(Isolations.SERIALIZABLE);\n\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\tbreak;\n\t\t}\n\t\tcase TX_READONLY: {\n\t\t\tc.setTxReadonly(true);\n\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\tbreak;\n\t\t}\n\t\tcase TX_READWRITE: {\n\t\t\tc.setTxReadonly(false);\n\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\tbreak;\n\t\t}\n\t\tcase NAMES:\n\t\t\tString charset = stmt.substring(rs >>> 8).trim();\n\t\t   int index=\tcharset.indexOf(\",\")  ;\n\t\t\tif(index>-1) {\n\t\t\t\t//支持rails框架自动生成的SET NAMES utf8,  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483, @@SESSION.sql_mode = 'STRICT_ALL_TABLES'\n\t\t\tcharset=charset.substring(0,index)\t;\n\t\t\t}\n\t\t\tif(charset.startsWith(\"'\")&&charset.endsWith(\"'\"))\n\t\t\t{\n\t\t\t\tcharset=charset.substring(1,charset.length()-1)  ;\n\t\t\t}\n\t\t\tif (c.setCharset(charset)) {\n\t\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\t} else {\n\t\t\t\t\n\t\t\t\t/**\n\t\t\t\t * TODO：修复 phpAyAdmin's 的发包问题\n\t\t\t\t * 如： SET NAMES 'utf8' COLLATE 'utf8_general_ci' 错误\n\t\t\t\t */\t\n\t\t\t\tint beginIndex = stmt.toLowerCase().indexOf(\"names\");\n\t\t\t\tint endIndex = stmt.toLowerCase().indexOf(\"collate\");\n\t\t\t\tif ( beginIndex > -1 && endIndex > -1 ) {\t\t\t\t\t\n\t\t\t\t\tcharset = stmt.substring(beginIndex + \"names\".length(), endIndex);\t\t\t\t\t\n\t\t\t\t\t//重试一次\n\t\t\t\t\tif (c.setCharset( charset.trim() )) {\n\t\t\t\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tc.writeErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, \"Unknown charset '\" + charset + \"'\");\n\t\t\t\t\t}\t\n\t\t\t\t\t\n\t\t\t\t} else {\t\t\t\t\n\t\t\t\t\tc.writeErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, \"Unknown charset '\" + charset + \"'\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SQL_SELECT_LIMIT:\n\t\t\tString limit = ParseUtil.parseString(stmt);\n\t\t\tint sqlSelectLimit = -1;\n\t\t\tif (\"default\".equalsIgnoreCase(limit)) {\n\t\t\t\tsqlSelectLimit = -1;\n\t\t\t} else {\n\t\t\t\ttry{\n\t\t\t\t\tsqlSelectLimit = Integer.parseInt(limit);\n\t\t\t\t} catch ( Exception  ex) {\n\t\t\t\t\tc.writeErrMessage(ErrorCode.ER_YES, \"Unsupported statement:\"+ex.getMessage());\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tc.setSqlSelectLimit(sqlSelectLimit);\n\t\t\tc.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n\t\t\tbreak;\n\t\tcase CHARACTER_SET_CLIENT:\n\t\tcase CHARACTER_SET_CONNECTION:\n\t\tcase CHARACTER_SET_RESULTS:\n\t\t\tCharacterSet.response(stmt, c, rs);\n\t\t\tbreak;\n\t\tdefault:\t\t\t\n\t\t\t boolean ignore = SetIgnoreUtil.isIgnoreStmt(stmt);\n             if ( ignore ) {\n     \t\t\tStringBuilder s = new StringBuilder();\n    \t\t\tlogger.warn(s.append(c).append(stmt).append(\" is not recoginized and ignored\").toString());\n\t\t\t\t c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n             }else{\n\t\t\t\t c.execute(stmt, ServerParse.UPDATE);\n\t\t\t }\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/ShowCache.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport java.nio.ByteBuffer;\nimport java.util.Map;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.cache.CachePool;\nimport io.mycat.cache.CacheService;\nimport io.mycat.cache.CacheStatic;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.Fields;\nimport io.mycat.manager.ManagerConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\npublic class ShowCache {\n\n\tprivate static final int FIELD_COUNT = 8;\n\tprivate static final ResultSetHeaderPacket header = PacketUtil\n\t\t\t.getHeader(FIELD_COUNT);\n\tprivate static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\tprivate static final EOFPacket eof = new EOFPacket();\n\tstatic {\n\t\tint i = 0;\n\t\tbyte packetId = 0;\n\t\theader.packetId = ++packetId;\n\n\t\tfields[i] = PacketUtil.getField(\"CACHE\", Fields.FIELD_TYPE_VAR_STRING);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"MAX\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"CUR\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"ACCESS\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"HIT\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"PUT\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"LAST_ACCESS\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\tfields[i] = PacketUtil.getField(\"LAST_PUT\", Fields.FIELD_TYPE_LONG);\n\t\tfields[i++].packetId = ++packetId;\n\t\teof.packetId = ++packetId;\n\t}\n\n\tpublic static void execute(ManagerConnection c) {\n\n\t\tByteBuffer buffer = c.allocate();\n\n\t\t// write header\n\t\tbuffer = header.write(buffer, c,true);\n\n\t\t// write fields\n\t\tfor (FieldPacket field : fields) {\n\t\t\tbuffer = field.write(buffer, c,true);\n\t\t}\n\n\t\t// write eof\n\t\tbuffer = eof.write(buffer, c,true);\n\n\t\t// write rows\n\t\tbyte packetId = eof.packetId;\n\t\tCacheService cacheService = MycatServer.getInstance().getCacheService();\n\t\tfor (Map.Entry<String, CachePool> entry : cacheService\n\t\t\t\t.getAllCachePools().entrySet()) {\n\t\t\tString cacheName=entry.getKey();\n\t\t\tCachePool cachePool = entry.getValue();\n\t\t\tif (cachePool instanceof LayerCachePool) {\n\t\t\t\tfor (Map.Entry<String, CacheStatic> staticsEntry : ((LayerCachePool) cachePool)\n\t\t\t\t\t\t.getAllCacheStatic().entrySet()) {\n\t\t\t\t\tRowDataPacket row = getRow(cacheName+'.'+staticsEntry.getKey(),\n\t\t\t\t\t\t\tstaticsEntry.getValue(), c.getCharset());\n\t\t\t\t\trow.packetId = ++packetId;\n\t\t\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tRowDataPacket row = getRow(cacheName,\n\t\t\t\t\t\tcachePool.getCacheStatic(), c.getCharset());\n\t\t\t\trow.packetId = ++packetId;\n\t\t\t\tbuffer = row.write(buffer, c,true);\n\t\t\t}\n\t\t}\n\n\t\t// write last eof\n\t\tEOFPacket lastEof = new EOFPacket();\n\t\tlastEof.packetId = ++packetId;\n\t\tbuffer = lastEof.write(buffer, c,true);\n\n\t\t// write buffer\n\t\tc.write(buffer);\n\t}\n\n\tprivate static RowDataPacket getRow(String poolName,\n\t\t\tCacheStatic cacheStatic, String charset) {\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\n\t\trow.add(StringUtil.encode(poolName, charset));\n\t\t// max size\n\t\trow.add(LongUtil.toBytes(cacheStatic.getMaxSize()));\n\t\trow.add(LongUtil.toBytes(cacheStatic.getItemSize()));\n\t\trow.add(LongUtil.toBytes(cacheStatic.getAccessTimes()));\n\t\trow.add(LongUtil.toBytes(cacheStatic.getHitTimes()));\n\t\trow.add(LongUtil.toBytes(cacheStatic.getPutTimes()));\n\t\trow.add(LongUtil.toBytes(cacheStatic.getLastAccesTime()));\n\t\trow.add(LongUtil.toBytes(cacheStatic.getLastPutTime()));\n\t\treturn row;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/ShowHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.parser.ServerParseShow;\nimport io.mycat.server.response.*;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic final class ShowHandler {\n\n\tpublic static void handle(final String originalStmt, ServerConnection c, int offset) {\n\n\t\t// 排除 “ ` ” 符号\n\t\tString stmt = StringUtil.replaceChars(originalStmt, \"`\", null,true);\n\n\t\tint type = ServerParseShow.parse(stmt, offset);\n\t\tswitch (type) {\n\t\tcase ServerParseShow.DATABASES:\n\t\t\tShowDatabases.response(c);\n\t\t\tbreak;\n\t\tcase ServerParseShow.TABLES:\n\t\t\tShowTables.response(c, stmt,type);\n\t\t\tbreak;\n        case ServerParseShow.FULLTABLES:\n            ShowFullTables.response(c, stmt, type);\n            break;\n\t\tcase ServerParseShow.MYCAT_STATUS:\n\t\t\tShowMyCatStatus.response(c);\n\t\t\tbreak;\n\t\tcase ServerParseShow.MYCAT_CLUSTER:\n\t\t\tShowMyCATCluster.response(c);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tc.execute(originalStmt, ServerParse.SHOW);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/StartHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.parser.ServerParseStart;\n\n/**\n * @author mycat\n */\npublic final class StartHandler {\n    private static final byte[] AC_OFF = new byte[] { 7, 0, 0, 1, 0, 0, 0, 0,\n            0, 0, 0 };\n    public static void handle(String stmt, ServerConnection c, int offset) {\n        switch (ServerParseStart.parse(stmt, offset)) {\n        case ServerParseStart.TRANSACTION:\n            if (c.isAutocommit())\n            {\n                c.write(c.writeToBuffer(AC_OFF, c.allocate()));\n            }else\n            {\n                c.getSession2().commit() ;\n            }\n            c.setAutocommit(false);\n            break;\n        default:\n            c.execute(stmt, ServerParse.START);\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/handler/UseHandler.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.handler;\n\nimport java.nio.ByteBuffer;\nimport java.util.Set;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.net.handler.FrontendPrivileges;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic final class UseHandler {\n\n    public static void handle(String sql, ServerConnection c, int offset) {\n        String schema = sql.substring(offset).trim();\n        int length = schema.length();\n        if (length > 0) {\n        \t//许多客户端工具断链重连后会批量发送SQL,如下:\n            //USE `TESTDB`;\\nSELECT SYSDATE(),CURRENT_USER()\n            //modify by jeff.cao 2018/3/31\n            int end = schema.indexOf(\";\");\n            if (end > 0) {\n                schema = schema.substring(0, end - 1);\n            }\n            schema = StringUtil.replaceChars(schema, \"`\", null,true);\n            length = schema.length();\n            if (schema.charAt(0) == '\\'' && schema.charAt(length - 1) == '\\'') {\n                schema = schema.substring(1, length - 1);\n            }\n        }\n        // 检查schema的有效性\n        FrontendPrivileges privileges = c.getPrivileges();\n        if (schema == null || !privileges.schemaExists(schema)) {\n            c.writeErrMessage(ErrorCode.ER_BAD_DB_ERROR, \"Unknown database '\" + schema + \"'\");\n            return;\n        }\n        String user = c.getUser();\n        if (!privileges.userExists(user, c.getHost())) {\n            c.writeErrMessage(ErrorCode.ER_ACCESS_DENIED_ERROR, \"Access denied for user '\" + c.getUser() + \"'\");\n            return;\n        }\n        Set<String> schemas = privileges.getUserSchemas(user);\n        if (schemas == null || schemas.size() == 0 || schemas.contains(schema)) {\n            c.setSchema(schema);\n            ByteBuffer buffer = c.allocate();\n            c.write(c.writeToBuffer(OkPacket.OK, buffer));\n        } else {\n            String msg = \"Access denied for user '\" + c.getUser() + \"' to database '\" + schema + \"'\";\n            c.writeErrMessage(ErrorCode.ER_DBACCESS_DENIED_ERROR, msg);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/interceptor/SQLInterceptor.java",
    "content": "package io.mycat.server.interceptor;\n/**\n * used for interceptor sql before execute ,can modify sql befor execute\n * @author wuzhih\n *\n */\npublic interface SQLInterceptor {\n\n\t/**\n\t * return new sql to handler,ca't modify sql's type \n\t * @param sql\n\t * @param sqlType\n\t * @return new sql\n\t */\n\tString interceptSQL(String sql ,int sqlType);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/interceptor/impl/DefaultSqlInterceptor.java",
    "content": "package io.mycat.server.interceptor.impl;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.server.interceptor.SQLInterceptor;\n\npublic class DefaultSqlInterceptor implements SQLInterceptor {\n\tprivate static final char ESCAPE_CHAR = '\\\\';\n\n\tprivate static final int TARGET_STRING_LENGTH = 2;\n\n\t/**\n\t * mysql driver对'转义与\\',解析前改为foundationdb parser支持的'' add by sky\n\t * \n\t * @param sql\n\t * @update by jason@dayima.com replace regex with general string walking\n\t * avoid sql being destroyed in case of some mismatch\n\t * maybe some performance enchanced\n\t * @return\n\t */\n\tpublic static String processEscape(String sql) {\n\t\tint firstIndex = -1;\n\t\tif ((sql == null) || ((firstIndex = sql.indexOf(ESCAPE_CHAR)) == -1)) {\n\t\t\treturn sql;\n\t\t} else {\n\t\t\tint lastIndex = sql.lastIndexOf(ESCAPE_CHAR, sql.length() - 2) + TARGET_STRING_LENGTH;\n\t\t\tStringBuilder sb = new StringBuilder(sql);\n\t\t\tfor (int i = firstIndex; i < lastIndex; i ++) {\n\t\t\t\tif (sb.charAt(i) == '\\\\') {\n\t\t\t\t\tif (i + 1 < lastIndex\n\t\t\t\t\t\t\t&& sb.charAt(i + 1) == '\\'') {\n\t\t\t\t\t\t\t//replace\n\t\t\t\t\t\t\tsb.setCharAt(i, '\\'');\n\t\t\t\t\t}\n\t\t\t\t\t//roll over\n\t\t\t\t\ti ++;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n\t/**\n\t * escape mysql escape letter sql type ServerParse.UPDATE,ServerParse.INSERT\n\t * etc\n\t */\n\t@Override\n\tpublic String interceptSQL(String sql, int sqlType) {\n\t\tif(\"fdbparser\".equals(MycatServer.getInstance().getConfig().getSystem().getDefaultSqlParser())) {\n\t\t\tsql = processEscape(sql);\n\t\t}\n\t\t\n\t\t// 全局表一致性 sql 改写拦截\n\t\tSystemConfig system = MycatServer.getInstance().getConfig().getSystem();\n\t\tif(system != null && system.getUseGlobleTableCheck() == 1) // 全局表一致性检测是否开启\n\t\t\tsql = GlobalTableUtil.interceptSQL(sql, sqlType);\n\t\t\n\t\t// other interceptors put in here ....\n\t\t\n\t\treturn sql;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/interceptor/impl/GlobalTableUtil.java",
    "content": "package io.mycat.server.interceptor.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport com.alibaba.druid.sql.ast.statement.*;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.SQLLimit;\nimport com.alibaba.druid.sql.ast.SQLName;\nimport com.alibaba.druid.sql.ast.SQLOrderBy;\nimport com.alibaba.druid.sql.ast.SQLOrderingSpecification;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLCharExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\nimport com.alibaba.fastjson.JSON;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.heartbeat.MySQLConsistencyChecker;\nimport io.mycat.backend.mysql.nio.MySQLDataSource;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.sqlengine.SQLQueryResult;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author digdeep@126.com\n * 全局表一致性检查 和 拦截\n */\npublic class GlobalTableUtil{\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(GlobalTableUtil.class);\n\tprivate static Map<String, TableConfig> globalTableMap = new ConcurrentHashMap<>();\n\t/** 全局表 保存修改时间戳 的字段名，用于全局表一致性检查 */\n\tpublic static final String GLOBAL_TABLE_MYCAT_COLUMN = \"_mycat_op_time\";\n\tpublic static final String COUNT_COLUMN = \"record_count\";\n\tpublic static final String MAX_COLUMN = \"max_timestamp\";   \n\tpublic static final String INNER_COLUMN = \"inner_col_exist\";   \n\tprivate static String operationTimestamp = String.valueOf(new Date().getTime());\n\tprivate static volatile int isInnerColumnCheckFinished = 0;\n\tprivate static volatile int isColumnCountCheckFinished = 0;\n\tprivate static final ReentrantLock lock = new ReentrantLock(false);\n\tprivate static List<SQLQueryResult<Map<String, String>>> innerColumnNotExist = new ArrayList<>();\n\tprivate static Map<String, String> tableColumsMap = new ConcurrentHashMap<>();\n\t\n\tpublic static Map<String, TableConfig> getGlobalTableMap() {\n\t\treturn globalTableMap;\n\t}\n\n\tstatic {\n\t\tgetGlobalTable();\t// 初始化 globalTableMap\n\t}\n\t\n\tpublic static String interceptSQL(String sql, int sqlType){\n\t\treturn GlobalTableUtil.consistencyInterceptor(sql, sqlType);\n\t}\n\t\n\tpublic static String consistencyInterceptor(String sql, int sqlType){\n\t\t// 统一使用mycat-server所在机器的时间，防止不同mysqld时间不同步\n\t\toperationTimestamp = String.valueOf(new Date().getTime());\n\t\t\n\t\tLOGGER.debug(\"before intercept: \" +  sql);\n\t\t\n\t\tif(sqlType == ServerParse.INSERT){\n\t\t\tsql =  convertInsertSQL(sql);\n\t\t}\n\t\tif(sqlType == ServerParse.UPDATE){\n\t\t\tsql = convertUpdateSQL(sql);\n\t\t}\n\t\tif(sqlType == ServerParse.DDL){\n\t\t\tLOGGER.info(\" DDL to modify global table.\");\n\t\t\tsql = handleDDLSQL(sql);\t\n\t\t}\n\t\t\n\t\tLOGGER.debug(\"after intercept: \" +  sql);\n\t\t/*\n\t\t   目前  mycat-server不支持 replace 语句，报错如下：\n\t\t ERROR 1064 (HY000):  ReplaceStatement can't be supported,\n\t\t use insert into ...on duplicate key update... instead\n\t\t \n\t\tif(sqlType == ServerParse.REPLACE){\n\t\t\treturn convertReplaceSQL(sql);\n\t\t}\n\t\t*/\n\t\treturn sql;\n\t}\n\t\n\t/*\n\t * Name: 'ALTER TABLE'\n\t\tDescription:\n\t\tSyntax:\n\t\tALTER [IGNORE] TABLE tbl_name\n\t\t    [alter_specification [, alter_specification] ...]\n\t\t    [partition_options]\n\t       如果 DDL 修改了表结构，需要重新获得表的列list\n\t */\n\tprivate static String handleDDLSQL(String sql){\n\t\tMySqlStatementParser parser = new MySqlStatementParser(sql);\t \n\t\tSQLStatement statement = parser.parseStatement();\n\t\t// druid高版本去掉了 MySqlAlterTableStatement，在其父类 SQLAlterTableStatement 直接支持 mysql alter table 语句\n//\t\t\tMySqlAlterTableStatement alter = (MySqlAlterTableStatement)statement;\n\t\tSQLExprTableSource source = getDDLTableSource(statement);\n\t\tif (source == null)\n\t\t\treturn sql;\n\t\tString tableName = StringUtil.removeBackquote(source.toString());\n\t\tif(StringUtils.isNotBlank(tableName))\n\t\t\ttableName = tableName.trim();\n\t\telse\n\t\t\treturn sql;\n\t\t\n\t\tif(!isGlobalTable(tableName))\n\t\t\treturn sql;\n\t\t\n\t\t//增加对全局表create语句的解析，如果是建表语句创建的是全局表，且表中不含\"_mycat_op_time\"列\n\t\t//则为其增加\"_mycat_op_time\"列，方便导入数据。\n\t\tsql = addColumnIfCreate(sql, statement);\n\t\t\n\t\tfinal String tn = tableName;\n\t\tMycatServer.getInstance().getListeningExecutorService().execute(new Runnable() {\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\tTimeUnit.SECONDS.sleep(3);\t// DDL发出之后，等待3秒让DDL分发完成\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t} \n\t\t\t\treGetColumnsForTable(tn); // DDL 语句可能会增删 列，所以需要重新获取 全局表的 列list\n\t\t\t}\n\t\t});\n\t\t\n\t\tMycatServer.getInstance().getListeningExecutorService().execute(new Runnable() {\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\tTimeUnit.MINUTES.sleep(10);\t// DDL发出之后，等待10分钟再次执行，全局表一般很小，DDL耗时不会超过10分钟\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t} \n\t\t\t\treGetColumnsForTable(tn); // DDL 语句可能会增删 列，所以需要重新获取 全局表的 列list\n\t\t\t}\n\t\t});\n\t\treturn sql;\n\t}\n\n\tstatic String addColumnIfCreate(String sql, SQLStatement statement) {\n\t\tif (isCreate(statement) && sql.trim().toUpperCase().startsWith(\"CREATE TABLE \") && !hasGlobalColumn(statement)) {\n\t\t\tSQLColumnDefinition column = new SQLColumnDefinition();\n\t\t\tcolumn.setDataType(new SQLCharacterDataType(\"bigint\"));\n\t\t\tcolumn.setName(new SQLIdentifierExpr(GLOBAL_TABLE_MYCAT_COLUMN));\n\t\t\tcolumn.setComment(new SQLCharExpr(\"全局表保存修改时间戳的字段名\"));\n\t\t\t((SQLCreateTableStatement)statement).getTableElementList().add(column);\n\t\t}\n\t\treturn statement.toString();\n\t}\n\t\n\tprivate static boolean hasGlobalColumn(SQLStatement statement){\n\t\tfor (SQLTableElement tableElement : ((SQLCreateTableStatement)statement).getTableElementList()) {\n\t\t\tSQLName sqlName = null;\n\t\t\tif (tableElement instanceof SQLColumnDefinition) {\n\t\t\t\tsqlName = ((SQLColumnDefinition)tableElement).getName();\n\t\t\t}\n\t\t\tif (sqlName != null) {\n\t\t\t\tString simpleName = sqlName.getSimpleName();\n\t\t\t\tsimpleName = StringUtil.removeBackquote(simpleName);\n\t\t\t\tif (tableElement instanceof SQLColumnDefinition && GLOBAL_TABLE_MYCAT_COLUMN.equalsIgnoreCase(simpleName)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static SQLExprTableSource getDDLTableSource(SQLStatement statement) {\n\t\tSQLExprTableSource source = null;\n\t\tif (statement instanceof SQLAlterTableStatement) {\n\t\t\tsource = ((SQLAlterTableStatement)statement).getTableSource();\n\t\t\t\n\t\t} else if (isCreate(statement)) {\n\t\t\tsource = ((SQLCreateTableStatement)statement).getTableSource();\n\t\t}\n\t\treturn source;\n\t}\n\n\tprivate static boolean isCreate(SQLStatement statement) {\n\t\treturn statement instanceof SQLCreateTableStatement;\n\t}\n\t\n\t/**\n\t * Syntax:\n\t\tINSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]\n\t    [INTO] tbl_name\n\t    [PARTITION (partition_name,...)]\n\t    [(col_name,...)]\n\t    {VALUES | VALUE} ({expr | DEFAULT},...),(...),...\n\t    [ ON DUPLICATE KEY UPDATE\n\t      col_name=expr\n\t        [, col_name=expr] ... ]\n\t\n\t\tOr:\n\t\n\t\tINSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]\n\t    [INTO] tbl_name\n\t    [PARTITION (partition_name,...)]\n\t    SET col_name={expr | DEFAULT}, ...\n\t    [ ON DUPLICATE KEY UPDATE\n\t      col_name=expr\n\t        [, col_name=expr] ... ]\n\t\n\t\tOr:\n\t\n\t\tINSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]\n\t    [INTO] tbl_name\n\t    [PARTITION (partition_name,...)]\n\t    [(col_name,...)]\n\t    SELECT ...\n\t    [ ON DUPLICATE KEY UPDATE\n\t      col_name=expr\n        [, col_name=expr] ... ]\n        mysql> insert user value (33333333,'ddd');\n\t\tmysql> insert into user value (333333,'ddd');\n\t\tmysql> insert user values (3333,'ddd');\n     * insert into user(id,name) valueS(1111,'dig'),\n     * (1111,  'dig'), (1111,'dig') ,(1111,'dig');\n\t * @param sql\n\t * @return\n\t */\n\tprivate static String convertInsertSQL(String sql){\n\t\ttry{\n\t\t\tMySqlStatementParser parser = new MySqlStatementParser(sql);\t \n\t\t\tSQLStatement statement = parser.parseStatement();\n\t\t\tMySqlInsertStatement insert = (MySqlInsertStatement)statement; \n\t        String tableName = StringUtil.removeBackquote(insert.getTableName().getSimpleName());\n\t        if(!isGlobalTable(tableName))\n\t\t\t\treturn sql;\n\t        if(!isInnerColExist(tableName))\n\t        \treturn sql;\n\t        \t\n\t        if(insert.getQuery() != null)\t// insert into tab select \n\t        \treturn sql;\n\t        \n\t        StringBuilder sb = new StringBuilder(200)\t// 指定初始容量可以提高性能\n\t\t\t.append(\"insert into \").append(tableName);\n\t        \n\t        List<SQLExpr> columns = insert.getColumns();\n\t        \n\t        int idx = -1;\t\n\t        int colSize = -1;\n\t        \n\t        if(columns == null || columns.size() <= 0){ // insert 没有带列名：insert into t values(xxx,xxx)\n\t        \tString columnsList = tableColumsMap.get(tableName.toUpperCase());\n\t        \tif(StringUtils.isNotBlank(columnsList)){ //\"id,name,_mycat_op_time\"\n\t        \t\t//newSQL = \"insert into t(id,name,_mycat_op_time)\";\n\t        \t\t// 构建一个虚拟newSQL来寻找 内部列的索引位置\n\t        \t\tString newSQL = \"insert into \" + tableName + \"(\" + columnsList + \")\";\n\t        \t\tMySqlStatementParser newParser = new MySqlStatementParser(newSQL);\t \n\t        \t\tSQLStatement newStatement = newParser.parseStatement();\n\t        \t\tMySqlInsertStatement newInsert = (MySqlInsertStatement)newStatement; \n\t        \t\tList<SQLExpr> newColumns = newInsert.getColumns();\n\t        \t\tfor(int i = 0; i < newColumns.size(); i++) {\n\t\t\t\t\t\tString column = StringUtil.removeBackquote(newInsert.getColumns().get(i).toString());\n\t\t\t\t\t\tif(column.equalsIgnoreCase(GLOBAL_TABLE_MYCAT_COLUMN))\n\t\t\t\t\t\t\tidx = i;\t// 找到 内部列的索引位置\n\t\t\t\t\t}\n\t        \t\tcolSize = newColumns.size();\n\t        \t\tsb.append(\"(\").append(columnsList).append(\")\");\n\t        \t}else{\t// tableName 是全局表，但是 tableColumsMap 没有其对应的列list，这种情况不应该存在\n\t\t        \tLOGGER.warn(\"you'd better do not use 'insert into t values(a,b)' Syntax (without column list) on global table, \"\n    \t\t\t\t+ \"If you do. Then you must make sure inner column '_mycat_op_time' is last column of global table: \" \n    \t\t\t\t+ tableName + \" in all database. Good luck. ^_^\");\n\t\t        \t// 我们假定 内部列位于表中所有列的最后，后面我们在values 子句的最后 给他附加上时间戳\n\t        \t}\n\t        }else{\t// insert 语句带有 列名\n\t        \tsb.append(\"(\");\n\t\t\t\tfor(int i = 0; i < columns.size(); i++) {\n\t\t\t\t\tif(i < columns.size() - 1)\n\t\t\t\t\t\tsb.append(columns.get(i).toString()).append(\",\");\n\t\t\t\t\telse\n\t\t\t\t\t\tsb.append(columns.get(i).toString());\n\t\t\t\t\tString column = StringUtil.removeBackquote(insert.getColumns().get(i).toString());\n\t\t\t\t\tif(column.equalsIgnoreCase(GLOBAL_TABLE_MYCAT_COLUMN))\n\t\t\t\t\t\tidx = i;\n\t\t\t\t}\n\t\t\t\tif(idx <= -1)\n\t\t\t\t\tsb.append(\",\").append(GLOBAL_TABLE_MYCAT_COLUMN);\n\t\t\t\tsb.append(\")\");\n\t\t\t\tcolSize = columns.size();\n\t        }\n\t\t\t\n\t\t\tsb.append(\" values\");\n\t\t\tList<ValuesClause> vcl = insert.getValuesList();\n\t\t\tif(vcl != null && vcl.size() > 1){\t// 批量insert\n\t\t\t\tfor(int j=0; j<vcl.size(); j++){\n\t\t\t\t   if(j != vcl.size() - 1)\n\t\t\t\t\t   appendValues(vcl.get(j).getValues(), sb, idx, colSize).append(\",\");\n\t\t\t\t   else\n\t\t\t\t\t   appendValues(vcl.get(j).getValues(), sb, idx, colSize);\n\t\t\t\t}\n\t\t\t}else{\t// 非批量 insert\n\t\t\t\tList<SQLExpr> valuse = insert.getValues().getValues();\n\t\t\t\tappendValues(valuse, sb, idx, colSize);\n\t\t\t}\n\t\t\t\n\t\t\tList<SQLExpr> dku = insert.getDuplicateKeyUpdate();\n\t\t\tif(dku != null && dku.size() > 0){\n\t\t\t\tsb.append(\" on duplicate key update \");\n\t\t\t\tfor(int i=0; i<dku.size(); i++){\n\t\t\t\t\tSQLExpr exp = dku.get(i);\n\t\t\t\t\tif(exp != null){\n\t\t\t\t\t\tif(i < dku.size() - 1)\n\t\t\t\t\t\t\tsb.append(exp.toString()).append(\",\");\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tsb.append(exp.toString());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn sb.toString();\n\t\t}catch(Exception e){ // 发生异常，则返回原始 sql\n\t\t\tLOGGER.warn(e.getMessage());\n\t\t\treturn sql;\n\t\t}\n\t}\n\t\n\tpublic static void main(String[] args){\n//\t\tString newSQL = \"insert into t(id,name,_mycat_op_time)\";// + columnsList + \")\";\n//\t\tMySqlStatementParser parser = new MySqlStatementParser(newSQL);\t \n//\t\tSQLStatement statement = parser.parseStatement();\n//\t\tMySqlInsertStatement insert = (MySqlInsertStatement)statement; \n//\t\tList<SQLExpr> columns = insert.getColumns();\n//\t\tSystem.out.println(columns.size());\n\t\t\n\t\tString sql = \"alter table t add colomn name varchar(30)\";\n\t\tSystem.out.println(handleDDLSQL(sql));\n\t}\n\t\n\tprivate static boolean isInnerColExist(String tableName){\n\t\tif(innerColumnNotExist.size() > 0){\n\t\t\tfor(SQLQueryResult<Map<String, String>> map : innerColumnNotExist){\n\t\t\t\tif(map != null && tableName.equalsIgnoreCase(map.getTableName())){\n\t\t\t\t\tStringBuilder warnStr = new StringBuilder(map.getDataNode())\n\t\t\t\t\t\t\t.append(\".\").append(tableName).append(\" inner column: \")\n\t\t\t\t\t\t\t.append(GLOBAL_TABLE_MYCAT_COLUMN)\n\t\t\t\t\t\t\t.append(\" is not exist.\");\n\t\t\t\t\tLOGGER.warn(warnStr.toString());\n\t\t\t\t\treturn false;\t// tableName 全局表没有内部列\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\t// tableName 有内部列\n\t}\n\t\n\tprivate static StringBuilder appendValues(List<SQLExpr> valuse, StringBuilder sb, int idx, int colSize){\n\t\tint size = valuse.size();\n\t\tif(size < colSize)\n\t\t\tsize = colSize;\n\t\t\n\t\tsb.append(\"(\");\n\t\tfor(int i = 0; i < size; i++) {\n    \t\tif(i < size - 1){\n    \t\t\tif(i != idx)\n    \t\t\t\tsb.append(valuse.get(i).toString()).append(\",\");\n    \t\t\telse\n    \t\t\t\tsb.append(operationTimestamp).append(\",\");\n    \t\t}else{\n    \t\t\tif(i != idx){\n    \t\t\t\tsb.append(valuse.get(i).toString());\n    \t\t\t}else{\n    \t\t\t\tsb.append(operationTimestamp);\n    \t\t\t}\n    \t\t}\n\t\t}\n\t\tif(idx <= -1)\n    \t   sb.append(\",\").append(operationTimestamp);\n\t\treturn sb.append(\")\");\n\t}\n\t\n\t/**\n\t * UPDATE [LOW_PRIORITY] [IGNORE] table_reference\n    \tSET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ...\n    \t[WHERE where_condition]\n    \t[ORDER BY ...]\n    \t[LIMIT row_count]\n\n\t\tMultiple-table syntax:\n\n\t\tUPDATE [LOW_PRIORITY] [IGNORE] table_references\n    \tSET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ...\n    \t[WHERE where_condition]\n    \t\n    \tupdate user, tuser set user.name='dddd',tuser.pwd='aaa' \n    \twhere user.id=2 and tuser.id=0;\n\t * @param sql update tuser set pwd='aaa', name='digdee' where id=0;\n\t * @return\n\t */\n\tpublic static String convertUpdateSQL(String sql){\n\t\ttry{\n\t\t\tMySqlStatementParser parser = new MySqlStatementParser(sql);\t \n\t\t\tSQLStatement stmt = parser.parseStatement();\n\t\t\tif (!(stmt instanceof MySqlUpdateStatement)){\n\t\t\t\treturn sql;\n\t\t\t}\n\t\t\tMySqlUpdateStatement update = (MySqlUpdateStatement)stmt;\n\t\t\tSQLTableSource ts = update.getTableSource();\n\t\t\tif(ts != null && ts.toString().contains(\",\")){\n\t\t\t\tSystem.out.println(ts.toString());\n\t\t\t\tLOGGER.warn(\"Do not support Multiple-table udpate syntax...\");\n\t\t\t\treturn sql;\n\t\t\t}\n\t\t\t\n\t\t\tString tableName = StringUtil.removeBackquote(update.getTableName().getSimpleName());\n\t        if(!isGlobalTable(tableName))\n\t\t\t\treturn sql;\n\t        if(!isInnerColExist(tableName))\n\t        \treturn sql;\t\t// 没有内部列\n\t        \n\t\t\tStringBuilder sb = new StringBuilder(150);\n\t\t\t\n\t\t\tSQLExpr se = update.getWhere();\n\t\t\t// where中有子查询： update company set name='com' where id in (select id from xxx where ...)\n\t\t\tif(se instanceof SQLInSubQueryExpr){\n\t\t\t\t// return sql;\n\t\t\t\tint idx = sql.toUpperCase().indexOf(\" SET \") + 5;\n\t\t\t\tsb.append(sql.substring(0, idx)).append(GLOBAL_TABLE_MYCAT_COLUMN)\n\t\t\t\t.append(\"=\").append(operationTimestamp)\n\t\t\t\t.append(\",\").append(sql.substring(idx));\n\t\t\t\treturn sb.toString();\n\t\t\t}\n\t\t\tString where = null;\n\t\t\tif(update.getWhere() != null)\n\t\t\t\twhere = update.getWhere().toString();\n\t\t\t\n\t\t\tSQLOrderBy orderBy = update.getOrderBy();\n\t\t\tSQLLimit limit = update.getLimit();\n\t\t\t\n\t\t\tsb.append(\"update \").append(tableName).append(\" set \");\n\t\t\tList<SQLUpdateSetItem> items = update.getItems();\n\t\t\tboolean flag = false;\n\t\t\tfor(int i=0; i<items.size(); i++){\n\t\t\t\tSQLUpdateSetItem item = items.get(i);\n\t\t\t\tString col = item.getColumn().toString();\n\t\t\t\tString val = item.getValue().toString();\n\t\t\t\t\n\t\t\t\tif(StringUtil.removeBackquote(col)\n\t\t\t\t\t\t.equalsIgnoreCase(GLOBAL_TABLE_MYCAT_COLUMN)){\n\t\t\t\t\tflag = true;\n\t\t\t\t\tsb.append(col).append(\"=\");\n\t\t\t\t\tif(i != items.size() - 1)\n\t\t\t\t\t\tsb.append(operationTimestamp).append(\",\");\n\t\t\t\t\telse\n\t\t\t\t\t\tsb.append(operationTimestamp);\n\t\t\t\t}else{\n\t\t\t\t\tsb.append(col).append(\"=\");\n\t\t\t\t\tif(i != items.size() -1 )\n\t\t\t\t\t\tsb.append(val).append(\",\");\n\t\t\t\t\telse\n\t\t\t\t\t\tsb.append(val);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif(!flag){\n\t\t\t\tsb.append(\",\").append(GLOBAL_TABLE_MYCAT_COLUMN)\n\t\t\t\t.append(\"=\").append(operationTimestamp);\n\t\t\t}\n\t\t\t\n\t\t\tsb.append(\" where \").append(where);\n\t\t\t\n\t\t\tif(orderBy != null && orderBy.getItems()!=null \n\t\t\t\t\t\t\t\t&& orderBy.getItems().size() > 0){\n\t\t\t\tsb.append(\" order by \");\n\t\t\t\tfor(int i=0; i<orderBy.getItems().size(); i++){\n\t\t\t\t\tSQLSelectOrderByItem item = orderBy.getItems().get(i);\n\t\t\t\t\tSQLOrderingSpecification os = item.getType();\n\t\t\t\t\tsb.append(item.getExpr().toString());\n\t\t\t\t\tif(i < orderBy.getItems().size() - 1){\n\t\t\t\t\t\tif(os != null)\n\t\t\t\t\t\t\tsb.append(\" \").append(os.toString());\n\t\t\t\t\t\tsb.append(\",\");\n\t\t\t\t\t}else{\n\t\t\t\t\t\tif(os != null)\n\t\t\t\t\t\t\tsb.append(\" \").append(os.toString());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\t\n\t\t\tif(limit != null){\t\t// 分为两种情况： limit 10;   limit 10,10;\n\t\t\t\tsb.append(\" limit \");\n\t\t\t\tif(limit.getOffset() != null)\n\t\t\t\t\tsb.append(limit.getOffset().toString()).append(\",\");\n\t\t\t\tsb.append(limit.getRowCount().toString());\n\t\t\t}\n\t\t\t\n\t\t\treturn sb.toString();\n\t\t}catch(Exception e){\n\t\t\tLOGGER.warn(e.getMessage());\n\t\t\treturn sql;\n\t\t}\n\t}\n\t\n\tprivate static void getGlobalTable(){\n\t\tMycatConfig config = MycatServer.getInstance().getConfig();\n\t\tMap<String, SchemaConfig> schemaMap = config.getSchemas();\n\t\tSchemaConfig schemaMconfig = null;\n\t\tfor(String key : schemaMap.keySet()){\n\t\t\tif(schemaMap.get(key) != null){\n\t\t\t\tschemaMconfig = schemaMap.get(key);\n\t\t\t\tMap<String, TableConfig> tableMap = schemaMconfig.getTables();\n\t\t\t\tif(tableMap != null){\n\t\t\t\t\tfor(String k : tableMap.keySet()){\n\t\t\t\t\t\tTableConfig table = tableMap.get(k);\n\t\t\t\t\t\tif(table != null && table.isGlobalTable()){\n\t\t\t\t\t\t\tglobalTableMap.put(table.getName().toUpperCase(), table);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * 重新获得table 的列list\n\t * @param tableName\n\t */\n\tprivate static void reGetColumnsForTable(String tableName){\n\t\tMycatConfig config = MycatServer.getInstance().getConfig();\n\t\tif(globalTableMap != null \n\t\t\t\t\t\t&& globalTableMap.get(tableName.toUpperCase()) != null){\n\t\t\t\n\t\t\tTableConfig tableConfig = globalTableMap.get(tableName.toUpperCase());\n\t\t\tif(tableConfig == null || isInnerColumnCheckFinished != 1)\t// consistencyCheck 在运行中\n\t\t\t\treturn;\n\t\t\t\n\t\t\tString nodeName = tableConfig.getDataNodes().get(0);\n\t\t\t\n\t\t\tMap<String, PhysicalDBNode> map = config.getDataNodes();\n\t\t\tfor(String k2 : map.keySet()){\n\t\t\t\tPhysicalDBNode dBnode = map.get(k2);\n\t\t\t\tif(nodeName.equals(dBnode.getName())){\n\t\t\t\t\tPhysicalDBPool pool = dBnode.getDbPool();\n\t\t\t\t\tList<PhysicalDatasource> dsList = (List<PhysicalDatasource>)pool.genAllDataSources();\n\t\t\t\t\tfor(PhysicalDatasource ds : dsList){\n\t\t\t\t\t\tif(ds instanceof MySQLDataSource){\n\t\t\t\t\t\t\tMySQLDataSource mds = (MySQLDataSource)dsList.get(0);\n\t\t\t\t\t\t\tMySQLConsistencyChecker checker = \n\t\t\t\t\t\t\t\t\tnew MySQLConsistencyChecker(mds, tableConfig.getName());\n\t\t\t\t\t\t\tchecker.checkInnerColumnExist();\n\t\t\t\t\t\t\treturn; // 运行一次就行了，不需要像consistencyCheck那样每个db都运行一次\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic static void consistencyCheck() {\n\t\tMycatConfig config = MycatServer.getInstance().getConfig();\n\t\tfor(String key : globalTableMap.keySet()){\n\t\t\tTableConfig table = globalTableMap.get(key);\n\t\t\t// <table name=\"travelrecord\" dataNode=\"dn1,dn2,dn3\"\n\t\t\tList<String> dataNodeList = table.getDataNodes();\n\t\t\t\n\t\t\t// 记录本次已经执行的datanode\n\t\t\t// 多个 datanode 对应到同一个 PhysicalDatasource 只执行一次\n\t\t\tMap<String, String> executedMap = new HashMap<>();\n\t\t\tfor(String nodeName : dataNodeList){\t\n\t\t\t\tMap<String, PhysicalDBNode> map = config.getDataNodes();\n\t\t\t\tfor(String k2 : map.keySet()){\n\t\t\t\t\t// <dataNode name=\"dn1\" dataHost=\"localhost1\" database=\"db1\" />\n\t\t\t\t\tPhysicalDBNode dBnode = map.get(k2);\n\t\t\t\t\tif(nodeName.equals(dBnode.getName())){\t// dn1,dn2,dn3\n\t\t\t\t\t\tPhysicalDBPool pool = dBnode.getDbPool();\n\t\t\t\t\t\tCollection<PhysicalDatasource> allDS = pool.genAllDataSources();\n\t\t\t\t\t\tfor(PhysicalDatasource pds : allDS){\n\t\t\t\t\t\t\tif(pds instanceof MySQLDataSource){\n\t\t\t\t\t\t\t\tMySQLDataSource mds = (MySQLDataSource)pds;\n\t\t\t\t\t\t\t\tif(executedMap.get(pds.getName()) == null){\n\t\t\t\t\t\t\t\t\tMySQLConsistencyChecker checker = \n\t\t\t\t\t\t\t\t\t\t\tnew MySQLConsistencyChecker(mds, table.getName());\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tisInnerColumnCheckFinished = 0;\n\t\t\t\t\t\t\t\t\tchecker.checkInnerColumnExist();\n\t\t\t\t\t\t\t\t\twhile(isInnerColumnCheckFinished <= 0){\n\t\t\t\t\t\t\t\t\t\tLOGGER.debug(\"isInnerColumnCheckFinished:\" + isInnerColumnCheckFinished);\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tTimeUnit.SECONDS.sleep(1);\n\t\t\t\t\t\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t\t\t\t\t\t\tLOGGER.warn(e.getMessage());\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tLOGGER.debug(\"isInnerColumnCheckFinished:\" + isInnerColumnCheckFinished);\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t// 一种 check 完成之后，再进行另一种 check\n\t\t\t\t\t\t\t\t\tchecker = new MySQLConsistencyChecker(mds, table.getName());\n\t\t\t\t\t\t\t\t\tisColumnCountCheckFinished = 0;\n\t\t\t\t\t\t\t\t\tchecker.checkRecordCout();\n\t\t\t\t\t\t\t\t\twhile(isColumnCountCheckFinished <= 0){\n\t\t\t\t\t\t\t\t\t\tLOGGER.debug(\"isColumnCountCheckFinished:\" + isColumnCountCheckFinished);\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tTimeUnit.SECONDS.sleep(1);\n\t\t\t\t\t\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t\t\t\t\t\t\tLOGGER.warn(e.getMessage());\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tLOGGER.debug(\"isColumnCountCheckFinished:\" + isColumnCountCheckFinished);\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tchecker = new MySQLConsistencyChecker(mds, table.getName());\n\t\t\t\t\t\t\t\t\tchecker.checkMaxTimeStamp();\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\texecutedMap.put(pds.getName(), nodeName);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * 每次处理 一种 check 的结果，不会交叉同时处理 多种不同 check 的结果\n\t * @param list\n\t * @return\n\t */\n\tpublic static List<SQLQueryResult<Map<String, String>>>\n\t\t\t\t\tfinished(List<SQLQueryResult<Map<String, String>>> list){\n\t\tlock.lock();\n\t\ttry{\n\t\t\t//[{\"dataNode\":\"db3\",\"result\":{\"count(*)\":\"1\"},\"success\":true,\"tableName\":\"COMPANY\"}]\n\t\t\tLOGGER.debug(\"list:::::::::::\" + JSON.toJSONString(list));\n\t\t\tfor(SQLQueryResult<Map<String, String>> map : list){\n\t\t\t\tMap<String, String> row = map.getResult();\n\t\t\t\tif(row != null){\n\t\t\t\t\tif(row.containsKey(GlobalTableUtil.MAX_COLUMN)){\n\t\t\t\t\t\tLOGGER.info(map.getDataNode() + \".\" + map.getTableName() \n\t\t\t\t\t\t\t\t+ \".\" + GlobalTableUtil.MAX_COLUMN\n\t\t\t\t\t\t\t\t+ \": \"+ map.getResult().get(GlobalTableUtil.MAX_COLUMN));\n\t\t\t\t\t}\n\t\t\t\t\tif(row.containsKey(GlobalTableUtil.COUNT_COLUMN)){\n\t\t\t\t\t\tLOGGER.info(map.getDataNode() + \".\" + map.getTableName() \n\t\t\t\t\t\t\t\t+ \".\" + GlobalTableUtil.COUNT_COLUMN\n\t\t\t\t\t\t\t\t+ \": \"+ map.getResult().get(GlobalTableUtil.COUNT_COLUMN));\n\t\t\t\t\t}\n\t\t\t\t\tif(row.containsKey(GlobalTableUtil.INNER_COLUMN)){\n\t\t\t\t\t\tString columnsList = null;\n\t\t\t\t\t\ttry{\n\t\t\t\t\t\t\tif(StringUtils.isNotBlank(row.get(GlobalTableUtil.INNER_COLUMN)))\n\t\t\t\t\t\t\t\tcolumnsList = row.get(GlobalTableUtil.INNER_COLUMN); // id,name,_mycat_op_time\n\t\t\t\t\t\t\tLOGGER.debug(\"columnsList: \" + columnsList);\n\t\t\t\t\t\t}catch(Exception e){\n\t\t\t\t\t\t\tLOGGER.warn(row.get(GlobalTableUtil.INNER_COLUMN) + \", \" + e.getMessage());\n\t\t\t\t\t\t}finally{\n\t\t\t\t\t\t\tif(columnsList == null \n\t\t\t\t\t\t\t\t\t|| columnsList.indexOf(GlobalTableUtil.GLOBAL_TABLE_MYCAT_COLUMN) == -1){\n\t\t\t\t\t\t\t\tLOGGER.warn(map.getDataNode() + \".\" + map.getTableName() \n\t\t\t\t\t\t\t\t\t\t+ \" inner column: \" \n\t\t\t\t\t\t\t\t\t\t+ GlobalTableUtil.GLOBAL_TABLE_MYCAT_COLUMN\n\t\t\t\t\t\t\t\t\t\t+ \" is not exist.\");\n\t\t\t\t\t\t\t\tif(StringUtils.isNotBlank(map.getTableName())){\n\t\t\t\t\t\t\t\t\tfor(SQLQueryResult<Map<String, String>> sqr : innerColumnNotExist){\n\t\t\t\t\t\t\t\t\t\tString name = map.getTableName();\n\t\t\t\t\t\t\t\t\t\tString node = map.getDataNode();\n\t\t\t\t\t\t\t\t\t\tif(name != null && !name.equalsIgnoreCase(sqr.getTableName())\n\t\t\t\t\t\t\t\t\t\t\t\t|| node != null && !node.equalsIgnoreCase(sqr.getDataNode())){\n\t\t\t\t\t\t\t\t\t\t\tinnerColumnNotExist.add(map);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\tLOGGER.debug(\"columnsList: \" + columnsList);\n\t\t\t\t\t\t\t\t// COMPANY -> \"id,name,_mycat_op_time\"，获得了全局表的所有列，并且知道了全局表是否有内部列\n\t\t\t\t\t\t\t\t// 所有列，在 insert into t values(xx,yy) 语法中需要用到\n\t\t\t\t\t\t\t\ttableColumsMap.put(map.getTableName().toUpperCase(), columnsList);\n\t\t\t\t\t\t\t}\n//\t\t\t\t\t\t\tisInnerColumnCheckFinished = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}finally{\n\t\t\tisInnerColumnCheckFinished = 1;\n\t\t\tisColumnCountCheckFinished = 1;\n\t\t\tlock.unlock();\n\t\t}\n\t\treturn list;\n\t}\n\t\n\tprivate static boolean isGlobalTable(String tableName){\n\t\tif(globalTableMap != null && globalTableMap.size() > 0){\n\t\t\treturn globalTableMap.get(tableName.toUpperCase()) != null;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static Map<String, String> getTableColumsMap() {\n\t\treturn tableColumsMap;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/interceptor/impl/StatSqlInterceptor.java",
    "content": "package io.mycat.server.interceptor.impl;\r\n\r\nimport io.mycat.server.interceptor.SQLInterceptor;\r\n\r\npublic class StatSqlInterceptor implements SQLInterceptor {\r\n\r\n\t@Override\r\n\tpublic String interceptSQL(String sql, int sqlType) {\r\n\t\t// TODO Auto-generated method stub\r\n\t\tfinal int atype = sqlType;\r\n        final String sqls = DefaultSqlInterceptor.processEscape(sql);\r\n        return sql;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/server/interceptor/impl/StatisticsSqlInterceptor.java",
    "content": "package io.mycat.server.interceptor.impl;\n\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.server.interceptor.SQLInterceptor;\nimport io.mycat.server.parser.ServerParse;\n\nimport java.io.File;\n\npublic class StatisticsSqlInterceptor implements SQLInterceptor {\n    \nprivate final class StatisticsSqlRunner implements Runnable {\n        \n        private int    sqltype = 0;\n        private String sqls    = \"\";\n        \n        public StatisticsSqlRunner(int sqltype, String sqls) {\n            this.sqltype = sqltype;\n            this.sqls = sqls;\n        }\n        \n        public void run() {\n            try {\n                SystemConfig sysconfig = MycatServer.getInstance().getConfig().getSystem();\n                String sqlInterceptorType = sysconfig.getSqlInterceptorType();\n                String sqlInterceptorFile = sysconfig.getSqlInterceptorFile();\n                \n                String[] sqlInterceptorTypes = sqlInterceptorType.split(\",\");\n                for (String type : sqlInterceptorTypes) {\n                    if (StatisticsSqlInterceptor.parseType(type.toUpperCase()) == sqltype) {\n                        switch (sqltype) {\n                            case ServerParse.SELECT:\n                                StatisticsSqlInterceptor.appendFile(sqlInterceptorFile, \"SELECT:\"\n                                    + sqls + \"\");\n                                break;\n                            case ServerParse.UPDATE:\n                                StatisticsSqlInterceptor.appendFile(sqlInterceptorFile, \"UPDATE:\"\n                                    + sqls);\n                                break;\n                            case ServerParse.INSERT:\n                                StatisticsSqlInterceptor.appendFile(sqlInterceptorFile, \"INSERT:\"\n                                    + sqls);\n                                break;\n                            case ServerParse.DELETE:\n                                StatisticsSqlInterceptor.appendFile(sqlInterceptorFile, \"DELETE:\"\n                                    + sqls);\n                                break;\n                            default:\n                                break;\n                        }\n                    }\n                }\n                \n            } catch (Exception e) {\n                LOGGER.error(\"interceptSQL error:\" + e.getMessage(),e);\n            }\n        }\n    }\n    \n    private static final Logger         LOGGER  = LoggerFactory.getLogger(StatisticsSqlInterceptor.class);\n    \n    private static Map<String, Integer> typeMap = new HashMap<String, Integer>();\n    static {\n        typeMap.put(\"SELECT\", 7);\n        typeMap.put(\"UPDATE\", 11);\n        typeMap.put(\"INSERT\", 4);\n        typeMap.put(\"DELETE\", 3);\n    }\n    \n    public static int parseType(String type) {\n        return typeMap.get(type);\n    }\n    \n    /**\n     * 方法追加文件：使用FileWriter\n     */\n    private static synchronized void appendFile(String fileName, String content) {\n        \n        Calendar calendar = Calendar.getInstance();\n        DateFormat dateFormat = new SimpleDateFormat(\"yyyy-MM-dd\");\n        String dayFile = dateFormat.format(calendar.getTime());\n        FileWriter writer = null;\n        try {\n            String newFileName = fileName;\n            //打开一个写文件器，构造函数中的第二个参数true表示以追加形式写文件\n            String[] title = newFileName.split(\"\\\\.\");\n            if (title.length == 2) {\n                newFileName = title[0] + dayFile + \".\" + title[1];\n            }\n            File file = new File(newFileName);\n            if (!file.exists()) {\n                file.createNewFile();\n            }\n            writer = new FileWriter(file, true);\n            String newContent = content.replaceAll(\"[\\\\t\\\\n\\\\r]\", \"\")\n                + System.getProperty(\"line.separator\");\n            writer.write(newContent);\n            \n            writer.flush();\n        } catch (IOException e) {\n            LOGGER.error(\"appendFile error:\" + e.getMessage(),e);\n        } finally {\n            if(writer != null ){\n                try {\n                    writer.close();\n                } catch (IOException e) {\n                    LOGGER.error(\"close file error:\" + e.getMessage(),e);\n                }\n            }\n        }\n    }\n    \n    /**\n     * interceptSQL ,\n     * \ttype :insert,delete,update,select\n     *  exectime:xxx ms\n     *  log content : select:select 1 from table,exectime:100ms,shared:1\n     * etc\n     */\n    @Override\n    public String interceptSQL(String sql, int sqlType) {\n        LOGGER.debug(\"sql interceptSQL:\");\n        \n        final int sqltype = sqlType;\n        final String sqls = DefaultSqlInterceptor.processEscape(sql);\n        MycatServer.getInstance().getBusinessExecutor()\n            .execute(new StatisticsSqlRunner(sqltype, sqls));\n        return sql;\n    }\n    \n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/parser/ServerParse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.parser;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport io.mycat.route.parser.util.ParseUtil;\n\n/**\n * @author mycat\n */\npublic final class ServerParse {\n\n\tpublic static final int OTHER = -1;\n\tpublic static final int BEGIN = 1;\n\tpublic static final int COMMIT = 2;\n\tpublic static final int DELETE = 3;\n\tpublic static final int INSERT = 4;\n\tpublic static final int REPLACE = 5;\n\tpublic static final int ROLLBACK = 6;\n\tpublic static final int SELECT = 7;\n\tpublic static final int SET = 8;\n\tpublic static final int SHOW = 9;\n\tpublic static final int START = 10;\n\tpublic static final int UPDATE = 11;\n\tpublic static final int KILL = 12;\n\tpublic static final int SAVEPOINT = 13;\n\tpublic static final int USE = 14;\n\tpublic static final int EXPLAIN = 15;\n\tpublic static final int EXPLAIN2 = 151;\n\tpublic static final int KILL_QUERY = 16;\n\tpublic static final int HELP = 17;\n\tpublic static final int MYSQL_CMD_COMMENT = 18;\n\tpublic static final int MYSQL_COMMENT = 19;\n\tpublic static final int CALL = 20;\n\tpublic static final int DESCRIBE = 21;\n\tpublic static final int LOCK = 22;\n\tpublic static final int UNLOCK = 23;\n    public static final int LOAD_DATA_INFILE_SQL = 99;\n    public static final int DDL = 100;\n    public static final int COMMAND = 101;\n    public static final String COM_FIELD_LIST_FLAG=\"select @@command \";\n\n\n\tpublic static final int MIGRATE  = 203;\n    private static final  Pattern pattern = Pattern.compile(\"(load)+\\\\s+(data)+\\\\s+\\\\w*\\\\s*(infile)+\",Pattern.CASE_INSENSITIVE);\n    private static final  Pattern callPattern = Pattern.compile(\"\\\\w*\\\\;\\\\s*\\\\s*(call)+\\\\s+\\\\w*\\\\s*\",Pattern.CASE_INSENSITIVE);\n\n    public static int parse(String stmt) {\n\t\tint length = stmt.length();\n\t\tfor (int i = 0; i < length; ++i) {\n\t\t\tswitch (stmt.charAt(i)) {\n\t\t\tcase ' ':\n\t\t\tcase '\\t':\n\t\t\tcase '\\r':\n\t\t\tcase '\\n':\n\t\t\t\tcontinue;\n\t\t\tcase '/':\n\t\t\t\t// such as /*!40101 SET character_set_client = @saved_cs_client\n\t\t\t\t// */;\n\t\t\t\tif (i == 0 && stmt.charAt(1) == '*' && stmt.charAt(2) == '!' && stmt.charAt(length - 2) == '*'\n\t\t\t\t\t\t&& stmt.charAt(length - 1) == '/') {\n\t\t\t\t\treturn MYSQL_CMD_COMMENT;\n\t\t\t\t}\n\t\t\tcase '#':\n\t\t\t\ti = ParseUtil.comment(stmt, i);\n\t\t\t\tif (i + 1 == length) {\n\t\t\t\t\treturn MYSQL_COMMENT;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\tcase 'A':\n\t\t\tcase 'a':\n\t\t\t\treturn aCheck(stmt, i);\n\t\t\tcase 'B':\n\t\t\tcase 'b':\n\t\t\t\treturn beginCheck(stmt, i);\n\t\t\tcase 'C':\n\t\t\tcase 'c':\n\t\t\t\treturn commitOrCallCheckOrCreate(stmt, i);\n\t\t\tcase 'D':\n\t\t\tcase 'd':\n\t\t\t\treturn deleteOrdCheck(stmt, i);\n\t\t\tcase 'E':\n\t\t\tcase 'e':\n\t\t\t\treturn explainCheck(stmt, i);\n\t\t\tcase 'I':\n\t\t\tcase 'i':\n\t\t\t\treturn insertCheck(stmt, i);\n\t\t\tcase 'M':\n\t\t\tcase 'm':\n\t\t\t\treturn migrateCheck(stmt, i);\n\t\t\tcase 'R':\n\t\t\tcase 'r':\n\t\t\t\treturn rCheck(stmt, i);\n\t\t\tcase 'S':\n\t\t\tcase 's':\n\t\t\t\treturn sCheck(stmt, i);\n\t\t\tcase 'T':\n\t\t\tcase 't':\n\t\t\t\treturn tCheck(stmt, i);\n\t\t\tcase 'U':\n\t\t\tcase 'u':\n\t\t\t\treturn uCheck(stmt, i);\n\t\t\tcase 'K':\n\t\t\tcase 'k':\n\t\t\t\treturn killCheck(stmt, i);\n\t\t\tcase 'H':\n\t\t\tcase 'h':\n\t\t\t\treturn helpCheck(stmt, i);\n\t\t\tcase 'L':\n\t\t\tcase 'l':\n\t\t\t\treturn lCheck(stmt, i);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\n\tstatic int lCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 3) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'O' || c1 == 'o') && (c2 == 'A' || c2 == 'a')\n\t\t\t\t\t&& (c3 == 'D' || c3 == 'd')) {\n\t\t\t\tMatcher matcher = pattern.matcher(stmt);\n\t\t\t\treturn matcher.find() ? LOAD_DATA_INFILE_SQL : OTHER;\n\t\t\t} else if ((c1 == 'O' || c1 == 'o') && (c2 == 'C' || c2 == 'c')\n\t\t\t\t\t&& (c3 == 'K' || c3 == 'k')){\n\t\t\t\treturn LOCK;\n\t\t\t}\n\t\t}\n\n\t\treturn OTHER;\n\t}\n\n\tprivate static int migrateCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 7) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\n\n\t\t\tif ((c1 == 'i' || c1 == 'I')\n\t\t\t\t\t&& (c2 == 'g' || c2 == 'G')\n\t\t\t\t\t&& (c3 == 'r' || c3 == 'R')\n\t\t\t\t\t&& (c4 == 'a' || c4 == 'A')\n\t\t\t\t\t&& (c5 == 't' || c5 == 'T')\n\t\t\t\t\t&& (c6 == 'e' || c6 == 'E'))\n\t\t\t\t\t{\n\t\t\t\treturn MIGRATE;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\t//truncate\n\tprivate static int tCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 7) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tchar c8 = stmt.charAt(++offset);\n\n\t\t\tif ((c1 == 'R' || c1 == 'r')\n\t\t\t\t\t&& (c2 == 'U' || c2 == 'u')\n\t\t\t\t\t&& (c3 == 'N' || c3 == 'n')\n\t\t\t\t\t&& (c4 == 'C' || c4 == 'c')\n\t\t\t\t\t&& (c5 == 'A' || c5 == 'a')\n\t\t\t\t\t&& (c6 == 'T' || c6 == 't')\n\t\t\t\t\t&& (c7 == 'E' || c7 == 'e')\n\t\t\t\t\t&& (c8 == ' ' || c8 == '\\t' || c8 == '\\r' || c8 == '\\n')) {\n\t\t\t\treturn DDL;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\t//alter table/view/...\n\tprivate static int aCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 4) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'L' || c1 == 'l')\n\t\t\t\t\t&& (c2 == 'T' || c2 == 't')\n\t\t\t\t\t&& (c3 == 'E' || c3 == 'e')\n\t\t\t\t\t&& (c4 == 'R' || c4 == 'r')\n\t\t\t\t\t&& (c5 == ' ' || c5 == '\\t' || c5 == '\\r' || c5 == '\\n')) {\n\t\t\t\treturn DDL;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\t//create table/view/...\n\tprivate static int createCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 5) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'R' || c1 == 'r')\n\t\t\t\t\t&& (c2 == 'E' || c2 == 'e')\n\t\t\t\t\t&& (c3 == 'A' || c3 == 'a')\n\t\t\t\t\t&& (c4 == 'T' || c4 == 't')\n\t\t\t\t\t&& (c5 == 'E' || c5 == 'e')\n\t\t\t\t\t&& (c6 == ' ' || c6 == '\\t' || c6 == '\\r' || c6 == '\\n')) {\n\t\t\t\treturn DDL;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\t//drop\n\tprivate static int dropCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 3) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'R' || c1 == 'r')\n\t\t\t\t\t&& (c2 == 'O' || c2 == 'o')\n\t\t\t\t\t&& (c3 == 'P' || c3 == 'p')\n\t\t\t\t\t&& (c4 == ' ' || c4 == '\\t' || c4 == '\\r' || c4 == '\\n')) {\n\t\t\t\treturn DDL;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\t// delete or drop\n    static int deleteOrdCheck(String stmt, int offset){\n    \tint sqlType = OTHER;\n\t\tswitch (stmt.charAt((offset + 1))) {\n\t\tcase 'E':\n\t\tcase 'e':\n\t\t\tsqlType = dCheck(stmt, offset);\n\t\t\tbreak;\n\t\tcase 'R':\n\t\tcase 'r':\n\t\t\tsqlType = dropCheck(stmt, offset);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tsqlType = OTHER;\n\t\t}\n\t\treturn sqlType;\n    }\n\t// HELP' '\n\tstatic int helpCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"ELP \".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e') && (c2 == 'L' || c2 == 'l')\n\t\t\t\t\t&& (c3 == 'P' || c3 == 'p')) {\n\t\t\t\treturn (offset << 8) | HELP;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// EXPLAIN' '\n\tstatic int explainCheck(String stmt, int offset) {\n\n\t\tif (stmt.length() > offset + \"XPLAIN \".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'X' || c1 == 'x') && (c2 == 'P' || c2 == 'p')\n\t\t\t\t\t&& (c3 == 'L' || c3 == 'l') && (c4 == 'A' || c4 == 'a')\n\t\t\t\t\t&& (c5 == 'I' || c5 == 'i') && (c6 == 'N' || c6 == 'n')\n\t\t\t\t\t&& (c7 == ' ' || c7 == '\\t' || c7 == '\\r' || c7 == '\\n')) {\n\t\t\t\treturn (offset << 8) | EXPLAIN;\n\t\t\t}\n\t\t}\n\t\tif(stmt != null && stmt.toLowerCase().startsWith(\"explain2\")){\n\t\t\treturn (offset << 8) | EXPLAIN2;\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// KILL' '\n\tstatic int killCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"ILL \".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'I' || c1 == 'i') && (c2 == 'L' || c2 == 'l')\n\t\t\t\t\t&& (c3 == 'L' || c3 == 'l')\n\t\t\t\t\t&& (c4 == ' ' || c4 == '\\t' || c4 == '\\r' || c4 == '\\n')) {\n\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\tcase ' ':\n\t\t\t\t\tcase '\\t':\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase 'Q':\n\t\t\t\t\tcase 'q':\n\t\t\t\t\t\treturn killQueryCheck(stmt, offset);\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn (offset << 8) | KILL;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// KILL QUERY' '\n\tstatic int killQueryCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"UERY \".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'U' || c1 == 'u') && (c2 == 'E' || c2 == 'e')\n\t\t\t\t\t&& (c3 == 'R' || c3 == 'r') && (c4 == 'Y' || c4 == 'y')\n\t\t\t\t\t&& (c5 == ' ' || c5 == '\\t' || c5 == '\\r' || c5 == '\\n')) {\n\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\tcase ' ':\n\t\t\t\t\tcase '\\t':\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn (offset << 8) | KILL_QUERY;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// BEGIN\n\tstatic int beginCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 4) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e')\n\t\t\t\t\t&& (c2 == 'G' || c2 == 'g')\n\t\t\t\t\t&& (c3 == 'I' || c3 == 'i')\n\t\t\t\t\t&& (c4 == 'N' || c4 == 'n')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn BEGIN;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// COMMIT\n\tstatic int commitCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 5) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'O' || c1 == 'o')\n\t\t\t\t\t&& (c2 == 'M' || c2 == 'm')\n\t\t\t\t\t&& (c3 == 'M' || c3 == 'm')\n\t\t\t\t\t&& (c4 == 'I' || c4 == 'i')\n\t\t\t\t\t&& (c5 == 'T' || c5 == 't')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn COMMIT;\n\t\t\t}\n\t\t}\n\n\t\treturn OTHER;\n\t}\n\n\t// CALL\n\tstatic int callCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 3) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'A' || c1 == 'a') && (c2 == 'L' || c2 == 'l')\n\t\t\t\t\t&& (c3 == 'L' || c3 == 'l')) {\n\t\t\t\treturn CALL;\n\t\t\t}\n\t\t}\n\n\t\treturn OTHER;\n\t}\n\n\tstatic int commitOrCallCheckOrCreate(String stmt, int offset) {\n\t\tint sqlType = OTHER;\n\t\tswitch (stmt.charAt((offset + 1))) {\n\t\tcase 'O':\n\t\tcase 'o':\n\t\t\tsqlType = commitCheck(stmt, offset);\n\t\t\tbreak;\n\t\tcase 'A':\n\t\tcase 'a':\n\t\t\tsqlType = callCheck(stmt, offset);\n\t\t\tbreak;\n\t\tcase 'R':\n\t\tcase 'r':\n\t\t\tsqlType = createCheck(stmt, offset);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tsqlType = OTHER;\n\t\t}\n\t\treturn sqlType;\n\t}\n\n\t// DESCRIBE or desc or DELETE' '\n\tstatic int dCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 4) {\n\t\t\tint res = describeCheck(stmt, offset);\n\t\t\tif (res == DESCRIBE) {\n\t\t\t\treturn res;\n\t\t\t}\n\t\t}\n\t\t// continue check\n\t\tif (stmt.length() > offset + 6) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e') && (c2 == 'L' || c2 == 'l')\n\t\t\t\t\t&& (c3 == 'E' || c3 == 'e') && (c4 == 'T' || c4 == 't')\n\t\t\t\t\t&& (c5 == 'E' || c5 == 'e')\n\t\t\t\t\t&& (c6 == ' ' || c6 == '\\t' || c6 == '\\r' || c6 == '\\n')) {\n\t\t\t\treturn DELETE;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// DESCRIBE' ' 或 desc' '\n\tstatic int describeCheck(String stmt, int offset) {\n\t\t//desc\n\t\tif (stmt.length() > offset + 4) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e') && (c2 == 'S' || c2 == 's')\n\t\t\t\t\t&& (c3 == 'C' || c3 == 'c')\n\t\t\t\t\t&& (c4 == ' ' || c4 == '\\t' || c4 == '\\r' || c4 == '\\n')) {\n\t\t\t\treturn DESCRIBE;\n\t\t\t}\n\t\t\t//describe\n\t\t\tif (stmt.length() > offset + 4) {\n\t\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\t\tchar c8 = stmt.charAt(++offset);\n\t\t\t\tif ((c1 == 'E' || c1 == 'e') && (c2 == 'S' || c2 == 's')\n\t\t\t\t\t\t&& (c3 == 'C' || c3 == 'c') && (c4 == 'R' || c4 == 'r')\n\t\t\t\t\t\t&& (c5 == 'I' || c5 == 'i') && (c6 == 'B' || c6 == 'b')\n\t\t\t\t\t\t&& (c7 == 'E' || c7 == 'e')\n\t\t\t\t\t\t&& (c8 == ' ' || c8 == '\\t' || c8 == '\\r' || c8 == '\\n')) {\n\t\t\t\t\treturn DESCRIBE;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// INSERT' '\n\tstatic int insertCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 6) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'N' || c1 == 'n') && (c2 == 'S' || c2 == 's')\n\t\t\t\t\t&& (c3 == 'E' || c3 == 'e') && (c4 == 'R' || c4 == 'r')\n\t\t\t\t\t&& (c5 == 'T' || c5 == 't')\n\t\t\t\t\t&& (c6 == ' ' || c6 == '\\t' || c6 == '\\r' || c6 == '\\n')) {\n\t\t\t\treturn INSERT;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\tstatic int rCheck(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase 'E':\n\t\t\tcase 'e':\n\t\t\t\treturn replaceCheck(stmt, offset);\n\t\t\tcase 'O':\n\t\t\tcase 'o':\n\t\t\t\treturn rollabckCheck(stmt, offset);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// REPLACE' '\n\tstatic int replaceCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 6) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'P' || c1 == 'p') && (c2 == 'L' || c2 == 'l')\n\t\t\t\t\t&& (c3 == 'A' || c3 == 'a') && (c4 == 'C' || c4 == 'c')\n\t\t\t\t\t&& (c5 == 'E' || c5 == 'e')\n\t\t\t\t\t&& (c6 == ' ' || c6 == '\\t' || c6 == '\\r' || c6 == '\\n')) {\n\t\t\t\treturn REPLACE;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// ROLLBACK\n\tstatic int rollabckCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 6) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'L' || c1 == 'l')\n\t\t\t\t\t&& (c2 == 'L' || c2 == 'l')\n\t\t\t\t\t&& (c3 == 'B' || c3 == 'b')\n\t\t\t\t\t&& (c4 == 'A' || c4 == 'a')\n\t\t\t\t\t&& (c5 == 'C' || c5 == 'c')\n\t\t\t\t\t&& (c6 == 'K' || c6 == 'k')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn ROLLBACK;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\tstatic int sCheck(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase 'A':\n\t\t\tcase 'a':\n\t\t\t\treturn savepointCheck(stmt, offset);\n\t\t\tcase 'E':\n\t\t\tcase 'e':\n\t\t\t\treturn seCheck(stmt, offset);\n\t\t\tcase 'H':\n\t\t\tcase 'h':\n\t\t\t\treturn showCheck(stmt, offset);\n\t\t\tcase 'T':\n\t\t\tcase 't':\n\t\t\t\treturn startCheck(stmt, offset);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SAVEPOINT\n\tstatic int savepointCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 8) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tchar c8 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'V' || c1 == 'v') && (c2 == 'E' || c2 == 'e')\n\t\t\t\t\t&& (c3 == 'P' || c3 == 'p') && (c4 == 'O' || c4 == 'o')\n\t\t\t\t\t&& (c5 == 'I' || c5 == 'i') && (c6 == 'N' || c6 == 'n')\n\t\t\t\t\t&& (c7 == 'T' || c7 == 't')\n\t\t\t\t\t&& (c8 == ' ' || c8 == '\\t' || c8 == '\\r' || c8 == '\\n')) {\n\t\t\t\treturn SAVEPOINT;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\tstatic int seCheck(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase 'L':\n\t\t\tcase 'l':\n\t\t\t\treturn selectCheck(stmt, offset);\n\t\t\tcase 'T':\n\t\t\tcase 't':\n\t\t\t\tif (stmt.length() > ++offset) {\n//支持一下语句\n//  /*!mycat: sql=SELECT * FROM test where id=99 */set @pin=1;\n//                    call p_test(@pin,@pout);\n//                    select @pout;\n                    if(stmt.startsWith(\"/*!mycat:\")||stmt.startsWith(\"/*#mycat:\")||stmt.startsWith(\"/*mycat:\")||stmt.startsWith(\"/* mycat:\"))\n                    {\n                        Matcher matcher = callPattern.matcher(stmt);\n                        if (matcher.find()) {\n\t\t\t\t\t\t\treturn CALL;\n\t\t\t\t\t\t}\n                    }\n\n\t\t\t\t\tchar c = stmt.charAt(offset);\n\t\t\t\t\tif (c == ' ' || c == '\\r' || c == '\\n' || c == '\\t'\n\t\t\t\t\t\t\t|| c == '/' || c == '#') {\n\t\t\t\t\t\treturn (offset << 8) | SET;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn OTHER;\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SELECT' '\n\tstatic int selectCheck(String stmt, int offset) {\n\t\t// SELECT @@command ,对应mysql协议是0x04\n\t\tif(stmt.startsWith(COM_FIELD_LIST_FLAG)) {\n\t\t\treturn COMMAND;\n\t\t}\n\t\tif (stmt.length() > offset + 4) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e')\n\t\t\t\t\t&& (c2 == 'C' || c2 == 'c')\n\t\t\t\t\t&& (c3 == 'T' || c3 == 't')\n\t\t\t\t\t&& (c4 == ' ' || c4 == '\\t' || c4 == '\\r' || c4 == '\\n'\n\t\t\t\t\t\t\t|| c4 == '/' || c4 == '#' || c4 == '*')) {\n\t\t\t\treturn (offset << 8) | SELECT;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SHOW' '\n\tstatic int showCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 3) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'O' || c1 == 'o') && (c2 == 'W' || c2 == 'w')\n\t\t\t\t\t&& (c3 == ' ' || c3 == '\\t' || c3 == '\\r' || c3 == '\\n')) {\n\t\t\t\treturn (offset << 8) | SHOW;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// START' '\n\tstatic int startCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 4) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'A' || c1 == 'a') && (c2 == 'R' || c2 == 'r')\n\t\t\t\t\t&& (c3 == 'T' || c3 == 't')\n\t\t\t\t\t&& (c4 == ' ' || c4 == '\\t' || c4 == '\\r' || c4 == '\\n')) {\n\t\t\t\treturn (offset << 8) | START;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// UPDATE' ' | USE' '\n\tstatic int uCheck(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase 'P':\n\t\t\tcase 'p':\n\t\t\t\tif (stmt.length() > offset + 5) {\n\t\t\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\t\t\tif ((c1 == 'D' || c1 == 'd')\n\t\t\t\t\t\t\t&& (c2 == 'A' || c2 == 'a')\n\t\t\t\t\t\t\t&& (c3 == 'T' || c3 == 't')\n\t\t\t\t\t\t\t&& (c4 == 'E' || c4 == 'e')\n\t\t\t\t\t\t\t&& (c5 == ' ' || c5 == '\\t' || c5 == '\\r' || c5 == '\\n')) {\n\t\t\t\t\t\treturn UPDATE;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'S':\n\t\t\tcase 's':\n\t\t\t\tif (stmt.length() > offset + 2) {\n\t\t\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\t\t\tif ((c1 == 'E' || c1 == 'e')\n\t\t\t\t\t\t\t&& (c2 == ' ' || c2 == '\\t' || c2 == '\\r' || c2 == '\\n')) {\n\t\t\t\t\t\treturn (offset << 8) | USE;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'N':\n\t\t\tcase 'n':\n\t\t\t\tif (stmt.length() > offset + 5) {\n\t\t\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\t\t\tif ((c1 == 'L' || c1 == 'l')\n\t\t\t\t\t\t\t&& (c2 == 'O' || c2 == 'o')\n\t\t\t\t\t\t\t&& (c3 == 'C' || c3 == 'c')\n\t\t\t\t\t\t\t&& (c4 == 'K' || c4 == 'k')\n\t\t\t\t\t\t\t&& (c5 == ' ' || c5 == '\\t' || c5 == '\\r' || c5 == '\\n')) {\n\t\t\t\t\t\treturn UNLOCK;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/parser/ServerParseSelect.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.parser;\n\nimport io.mycat.route.parser.util.CharTypes;\nimport io.mycat.route.parser.util.ParseUtil;\n\n/**\n * @author mycat\n */\npublic final class ServerParseSelect {\n\n\tpublic static final int OTHER = -1;\n\tpublic static final int VERSION_COMMENT = 1;\n\tpublic static final int DATABASE = 2;\n\tpublic static final int USER = 3;\n\tpublic static final int LAST_INSERT_ID = 4;\n\tpublic static final int IDENTITY = 5;\n\tpublic static final int VERSION = 6;\n\tpublic static final int SESSION_INCREMENT = 7;\n\tpublic static final int SESSION_ISOLATION = 8;\n\n    public static final int SELECT_VAR_ALL = 9;\n\n\tpublic static final int SESSION_TX_READ_ONLY = 10;\n\n\tprivate static final char[] _VERSION_COMMENT = \"VERSION_COMMENT\"\n\t\t\t.toCharArray();\n\tprivate static final char[] _IDENTITY = \"IDENTITY\".toCharArray();\n\tprivate static final char[] _LAST_INSERT_ID = \"LAST_INSERT_ID\"\n\t\t\t.toCharArray();\n\tprivate static final char[] _DATABASE = \"DATABASE()\".toCharArray();\n\tprivate static final char[] _CURRENT_USER = \"CURRENT_USER()\".toCharArray();\n\n\tpublic static int parse(String stmt, int offset) {\n\t\tint i = offset;\n\t\tfor (; i < stmt.length(); ++i) {\n\t\t\tswitch (stmt.charAt(i)) {\n\t\t\tcase ' ':\n\t\t\t\tcontinue;\n\t\t\tcase '/':\n\t\t\tcase '#':\n\t\t\t\ti = ParseUtil.comment(stmt, i);\n\t\t\t\tcontinue;\n\t\t\tcase '@':\n\t\t\t\treturn select2Check(stmt, i);\n\t\t\tcase 'D':\n\t\t\tcase 'd':\n\t\t\t\treturn databaseCheck(stmt, i);\n\t\t\tcase 'L':\n\t\t\tcase 'l':\n\t\t\t\treturn lastInsertCheck(stmt, i);\n\t\t\tcase 'U':\n\t\t\tcase 'u':\n\t\t\t\treturn userCheck(stmt, i);\n\t\t\tcase 'C':\n\t\t\tcase 'c':\n\t\t\t\treturn currentUserCheck(stmt, i);\n\t\t\tcase 'V':\n\t\t\tcase 'v':\n\t\t\t\treturn versionCheck(stmt, i);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t/**\n\t * SELECT @@session.auto_increment_increment\n\t * \n\t * @param stmt\n\t * @param offset\n\t * @return\n\t */\n\tprivate static int sessionVarCheck(String stmt, int offset) {\n        String s = stmt.substring(offset).toLowerCase();\n        if (s.startsWith(\"session.auto_increment_increment\")) {\n            if(s.contains(\"@@\"))\n            {\n             return    SELECT_VAR_ALL;\n            }\n\t\t\treturn SESSION_INCREMENT;\n\t\t} else if (s\n\t\t\t\t.startsWith(\"session.tx_isolation\")) {\n\t\t\treturn SESSION_ISOLATION;\n\t\t}\n\t\telse if (s\n\t\t\t\t.startsWith(\"session.tx_read_only\")) {\n\t\t\treturn SESSION_TX_READ_ONLY;\n\t\t}\n\t\telse {\n\t\t\treturn OTHER;\n\t\t}\n\t}\n\n\t// SELECT VERSION\n\tprivate static int versionCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"ERSION\".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e') && (c2 == 'R' || c2 == 'r')\n\t\t\t\t\t&& (c3 == 'S' || c3 == 's') && (c4 == 'I' || c4 == 'i')\n\t\t\t\t\t&& (c5 == 'O' || c5 == 'o') && (c6 == 'N' || c6 == 'n')) {\n\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\tcase ' ':\n\t\t\t\t\tcase '\\t':\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase '(':\n\t\t\t\t\t\treturn versionParenthesisCheck(stmt, offset);\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SELECT VERSION (\n\tprivate static int versionParenthesisCheck(String stmt, int offset) {\n\t\twhile (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase ' ':\n\t\t\tcase '\\t':\n\t\t\tcase '\\r':\n\t\t\tcase '\\n':\n\t\t\t\tcontinue;\n\t\t\tcase ')':\n\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\tcase ' ':\n\t\t\t\t\tcase '\\t':\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn VERSION;\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t/**\n\t * <code>SELECT LAST_INSERT_ID() AS id, </code>\n\t * \n\t * @param offset\n\t *            index of 'i', offset == stmt.length() is possible\n\t * @return index of ','. return stmt.length() is possible. -1 if not alias\n\t */\n\tprivate static int skipAlias(String stmt, int offset) {\n\t\toffset = ParseUtil.move(stmt, offset, 0);\n\t\tif (offset >= stmt.length()) {\n\t\t\treturn offset;\n\t\t}\n\t\tswitch (stmt.charAt(offset)) {\n\t\tcase '\\'':\n\t\t\treturn skipString(stmt, offset);\n\t\tcase '\"':\n\t\t\treturn skipString2(stmt, offset);\n\t\tcase '`':\n\t\t\treturn skipIdentifierEscape(stmt, offset);\n\t\tdefault:\n\t\t\tif (CharTypes.isIdentifierChar(stmt.charAt(offset))) {\n\t\t\t\tfor (; offset < stmt.length()\n\t\t\t\t\t\t&& CharTypes.isIdentifierChar(stmt.charAt(offset)); ++offset) {\n\t\t\t\t}\n\t\t\t\treturn offset;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/**\n\t * <code>`abc`d</code>\n\t * \n\t * @param offset\n\t *            index of first <code>`</code>\n\t * @return index of 'd'. return stmt.length() is possible. -1 if string\n\t *         invalid\n\t */\n\tprivate static int skipIdentifierEscape(String stmt, int offset) {\n\t\tfor (++offset; offset < stmt.length(); ++offset) {\n\t\t\tif (stmt.charAt(offset) == '`'\n\t\t\t\t\t&& (++offset >= stmt.length() || stmt.charAt(offset) != '`')) {\n\t\t\t\t\treturn offset;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/**\n\t * <code>\"abc\"d</code>\n\t * \n\t * @param offset\n\t *            index of first <code>\"</code>\n\t * @return index of 'd'. return stmt.length() is possible. -1 if string\n\t *         invalid\n\t */\n\tprivate static int skipString2(String stmt, int offset) {\n\t\tint state = 0;\n\t\tfor (++offset; offset < stmt.length(); ++offset) {\n\t\t\tchar c = stmt.charAt(offset);\n\t\t\tswitch (state) {\n\t\t\tcase 0:\n\t\t\t\tswitch (c) {\n\t\t\t\tcase '\\\\':\n\t\t\t\t\tstate = 1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\"':\n\t\t\t\t\tstate = 2;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tstate = 0;\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tswitch (c) {\n\t\t\t\tcase '\"':\n\t\t\t\t\tstate = 0;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn offset;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (offset == stmt.length() && state == 2) {\n\t\t\treturn stmt.length();\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/**\n\t * <code>'abc'd</code>\n\t * \n\t * @param offset\n\t *            index of first <code>'</code>\n\t * @return index of 'd'. return stmt.length() is possible. -1 if string\n\t *         invalid\n\t */\n\tprivate static int skipString(String stmt, int offset) {\n\t\tint state = 0;\n\t\tfor (++offset; offset < stmt.length(); ++offset) {\n\t\t\tchar c = stmt.charAt(offset);\n\t\t\tswitch (state) {\n\t\t\tcase 0:\n\t\t\t\tswitch (c) {\n\t\t\t\tcase '\\\\':\n\t\t\t\t\tstate = 1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\'':\n\t\t\t\t\tstate = 2;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tstate = 0;\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tswitch (c) {\n\t\t\t\tcase '\\'':\n\t\t\t\t\tstate = 0;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn offset;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (offset == stmt.length() && state == 2) {\n\t\t\treturn stmt.length();\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/**\n\t * <code>SELECT LAST_INSERT_ID() AS id</code>\n\t * \n\t * @param offset\n\t *            index of first ' ' after LAST_INSERT_ID(), offset ==\n\t *            stmt.length() is possible\n\t * @return index of 'i'. return stmt.length() is possible\n\t */\n\tpublic static int skipAs(String stmt, int offset) {\n\t\toffset = ParseUtil.move(stmt, offset, 0);\n\t\tif (stmt.length() > offset + \"AS\".length()\n\t\t\t\t&& (stmt.charAt(offset) == 'A' || stmt.charAt(offset) == 'a')\n\t\t\t\t&& (stmt.charAt(offset + 1) == 'S' || stmt.charAt(offset + 1) == 's')\n\t\t\t\t&& (stmt.charAt(offset + 2) == ' '\n\t\t\t\t\t\t|| stmt.charAt(offset + 2) == '\\r'\n\t\t\t\t\t\t|| stmt.charAt(offset + 2) == '\\n'\n\t\t\t\t\t\t|| stmt.charAt(offset + 2) == '\\t'\n\t\t\t\t\t\t|| stmt.charAt(offset + 2) == '/' || stmt\n\t\t\t\t\t\t.charAt(offset + 2) == '#')) {\n\t\t\toffset = ParseUtil.move(stmt, offset + 2, 0);\n\t\t}\n\t\treturn offset;\n\t}\n\n\t/**\n\t * @param offset\n\t *            <code>stmt.charAt(offset) == first 'L' OR 'l'</code>\n\t * @return index after LAST_INSERT_ID(), might equals to length. -1 if not\n\t *         LAST_INSERT_ID\n\t */\n\tpublic static int indexAfterLastInsertIdFunc(String stmt, int offset) {\n\t\tif (stmt.length() >= offset + \"LAST_INSERT_ID()\".length()\n\t\t\t\t&& ParseUtil.compare(stmt, offset, _LAST_INSERT_ID)) {\n\t\t\t\toffset = ParseUtil.move(stmt, offset + _LAST_INSERT_ID.length,\n\t\t\t\t\t\t0);\n\t\t\t\tif (offset + 1 < stmt.length() && stmt.charAt(offset) == '(') {\n\t\t\t\t\toffset = ParseUtil.move(stmt, offset + 1, 0);\n\t\t\t\t\tif (offset < stmt.length() && stmt.charAt(offset) == ')') {\n\t\t\t\t\t\treturn ++offset;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/**\n\t * @param offset\n\t *            <code>stmt.charAt(offset) == first '`' OR 'i' OR 'I' OR '\\'' OR '\"'</code>\n\t * @return index after identity or `identity` or \"identity\" or 'identity',\n\t *         might equals to length. -1 if not identity or `identity` or\n\t *         \"identity\" or 'identity'\n\t */\n\tpublic static int indexAfterIdentity(String stmt, int offset) {\n\t\tchar first = stmt.charAt(offset);\n\t\tswitch (first) {\n\t\tcase '`':\n\t\tcase '\\'':\n\t\tcase '\"':\n\t\t\tif (stmt.length() < offset + \"identity\".length() + 2) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (stmt.charAt(offset + \"identity\".length() + 1) != first) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t++offset;\n\t\t\tbreak;\n\t\tcase 'i':\n\t\tcase 'I':\n\t\t\tif (stmt.length() < offset + \"identity\".length()) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn -1;\n\t\t}\n\t\tif (ParseUtil.compare(stmt, offset, _IDENTITY)) {\n\t\t\toffset += _IDENTITY.length;\n\t\t\tswitch (first) {\n\t\t\tcase '`':\n\t\t\tcase '\\'':\n\t\t\tcase '\"':\n\t\t\t\treturn ++offset;\n\t\t\t}\n\t\t\treturn offset;\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/**\n\t * SELECT LAST_INSERT_ID()\n\t */\n\tstatic int lastInsertCheck(String stmt, int offset) {\n\t\toffset = indexAfterLastInsertIdFunc(stmt, offset);\n\t\tif (offset < 0) {\n\t\t\treturn OTHER;\n\t\t}\n\t\toffset = skipAs(stmt, offset);\n\t\toffset = skipAlias(stmt, offset);\n\t\tif (offset < 0) {\n\t\t\treturn OTHER;\n\t\t}\n\t\toffset = ParseUtil.move(stmt, offset, 0);\n\t\tif (offset < stmt.length()) {\n\t\t\treturn OTHER;\n\t\t}\n\t\treturn LAST_INSERT_ID;\n\t}\n\n\t/**\n\t * select @@identity<br/>\n\t * select @@identiTy aS iD\n\t */\n\tstatic int identityCheck(String stmt, int offset) {\n\t\toffset = indexAfterIdentity(stmt, offset);\n\t\tif (offset < 0) {\n\t\t\treturn OTHER;\n\t\t}\n\t\toffset = skipAs(stmt, offset);\n\t\toffset = skipAlias(stmt, offset);\n\t\tif (offset < 0) {\n\t\t\treturn OTHER;\n\t\t}\n\t\toffset = ParseUtil.move(stmt, offset, 0);\n\t\tif (offset < stmt.length()) {\n\t\t\treturn OTHER;\n\t\t}\n\t\treturn IDENTITY;\n\t}\n\n\tstatic int select2Check(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset && stmt.charAt(offset) == '@'\n\t\t\t\t&& stmt.length() > ++offset) {\n\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\tcase 'V':\n\t\t\t\tcase 'v':\n\t\t\t\t\treturn versionCommentCheck(stmt, offset);\n\t\t\t\tcase 'i':\n\t\t\t\tcase 'I':\n\t\t\t\t\treturn identityCheck(stmt, offset);\n\t\t\t\tcase 's':\n\t\t\t\tcase 'S':\n\t\t\t\t\treturn sessionVarCheck(stmt, offset);\n\t\t\t\tdefault:\n\t\t\t\t\treturn OTHER;\n\t\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t/**\n\t * SELECT DATABASE()\n\t */\n\tstatic int databaseCheck(String stmt, int offset) {\n\t\tint length = offset + _DATABASE.length;\n\t\tif (stmt.length() >= length\n\t\t\t\t&& ParseUtil.compare(stmt, offset, _DATABASE)) {\n\t\t\tif (stmt.length() > length && stmt.charAt(length) != ' ') {\n\t\t\t\treturn OTHER;\n\t\t\t} else {\n\t\t\t\treturn DATABASE;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t/**\n\t * SELECT USER()\n\t */\n\tstatic int userCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 5) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'S' || c1 == 's')\n\t\t\t\t\t&& (c2 == 'E' || c2 == 'e')\n\t\t\t\t\t&& (c3 == 'R' || c3 == 'r')\n\t\t\t\t\t&& (c4 == '(')\n\t\t\t\t\t&& (c5 == ')')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn USER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t/**\n\t * SELECT USER()\n\t */\n\tstatic int currentUserCheck(String stmt, int offset) {\n\t\tint length = offset + _CURRENT_USER.length;\n\t\tif (stmt.length() >= length\n\t\t\t\t&& ParseUtil.compare(stmt, offset, _CURRENT_USER)) {\n\t\t\t\tif (stmt.length() > length && stmt.charAt(length) != ' ') {\n\t\t\t\t\treturn OTHER;\n\t\t\t\t}\n\t\t\t\treturn USER;\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t/**\n\t * SELECT @@VERSION_COMMENT\n\t */\n\tstatic int versionCommentCheck(String stmt, int offset) {\n\t\tint length = offset + _VERSION_COMMENT.length;\n\t\tif (stmt.length() >= length\n\t\t\t\t&& ParseUtil.compare(stmt, offset, _VERSION_COMMENT)) {\n\t\t\tif (stmt.length() > length && stmt.charAt(length) != ' ') {\n\t\t\t\treturn OTHER;\n\t\t\t} else {\n\t\t\t\treturn VERSION_COMMENT;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/parser/ServerParseSet.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.parser;\n\nimport io.mycat.route.parser.util.ParseUtil;\n\n/**\n * @author mycat\n */\npublic final class ServerParseSet {\n\n\tpublic static final int OTHER = -1;\n\tpublic static final int AUTOCOMMIT_ON = 1;\n\tpublic static final int AUTOCOMMIT_OFF = 2;\n\tpublic static final int TX_READ_UNCOMMITTED = 3;\n\tpublic static final int TX_READ_COMMITTED = 4;\n\tpublic static final int TX_REPEATED_READ = 5;\n\tpublic static final int TX_SERIALIZABLE = 6;\n\tpublic static final int NAMES = 7;\n\tpublic static final int CHARACTER_SET_CLIENT = 8;\n\tpublic static final int CHARACTER_SET_CONNECTION = 9;\n\tpublic static final int CHARACTER_SET_RESULTS = 10;\n\tpublic static final int XA_FLAG_ON = 11;\n\tpublic static final int XA_FLAG_OFF = 12;\n\n\tpublic static final int TX_READONLY = 13;\n\tpublic static final int TX_READWRITE = 14;\n\tpublic static final int SQL_SELECT_LIMIT = 15;\n\n\tpublic static int parse(String stmt, int offset) {\n\t\tif (stmt.contains(\"@@session.\")){\n\t\t\tstmt = stmt.replaceAll(\"@@session.\",\"\");\n\t\t}\n\t\tint i = offset;\n\t\tfor (; i < stmt.length(); i++) {\n\t\t\tswitch (stmt.charAt(i)) {\n\t\t\tcase ' ':\n\t\t\tcase '\\r':\n\t\t\tcase '\\n':\n\t\t\tcase '\\t':\n\t\t\t\tcontinue;\n\t\t\tcase '/':\n\t\t\tcase '#':\n\t\t\t\ti = ParseUtil.comment(stmt, i);\n\t\t\t\tcontinue;\n\t\t\tcase 'A':\n\t\t\tcase 'a':\n\t\t\t\treturn autocommit(stmt, i);\n\t\t\tcase 'C':\n\t\t\tcase 'c':\n\t\t\t\treturn characterSet(stmt, i, 0);\n\t\t\tcase 'N':\n\t\t\tcase 'n':\n\t\t\t\treturn names(stmt, i);\n\t\t\tcase 'S':\n\t\t\tcase 's':\n\t\t\t\treturn parseS(stmt, i);\n\t\t\tcase 'T':\n\t\t\tcase 't':\n\t\t\t\treturn transaction(stmt, i);\n\t\t\tcase 'X':\n\t\t\tcase 'x':\n\t\t\t\treturn xaFlag(stmt, i);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// set xa=1\n\tprivate static int xaFlag(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 1) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'A' || c1 == 'a')) {\n\t\t\t\twhile (stmt.length() >= ++ offset) {\n\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\tcase ' ':\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase '=':\n\t\t\t\t\t\tint value = autocommitValue(stmt, offset);\n\t\t\t\t\t\tif (value == AUTOCOMMIT_ON) {\n\t\t\t\t\t\t\treturn XA_FLAG_ON;\n\t\t\t\t\t\t} else if (value == AUTOCOMMIT_OFF) {\n\t\t\t\t\t\t\treturn XA_FLAG_OFF;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET AUTOCOMMIT(' '=)\n\tprivate static int autocommit(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 9) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tchar c8 = stmt.charAt(++offset);\n\t\t\tchar c9 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'U' || c1 == 'u') && (c2 == 'T' || c2 == 't')\n\t\t\t\t\t&& (c3 == 'O' || c3 == 'o') && (c4 == 'C' || c4 == 'c')\n\t\t\t\t\t&& (c5 == 'O' || c5 == 'o') && (c6 == 'M' || c6 == 'm')\n\t\t\t\t\t&& (c7 == 'M' || c7 == 'm') && (c8 == 'I' || c8 == 'i')\n\t\t\t\t\t&& (c9 == 'T' || c9 == 't')) {\n\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\tcase ' ':\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase '=':\n\t\t\t\t\t\treturn autocommitValue(stmt, offset);\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\tprivate static int autocommitValue(String stmt, int offset) {\n\t\tfor (;;) {\n\t\t\toffset++;\n\t\t\tif (stmt.length() <= offset) {\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase ' ':\n\t\t\tcase '\\r':\n\t\t\tcase '\\n':\n\t\t\tcase '\\t':\n\t\t\t\tcontinue;\n\t\t\tcase '1':\n\t\t\t\tif (stmt.length() == ++offset\n\t\t\t\t\t\t|| ParseUtil.isEOF(stmt.charAt(offset))) {\n\t\t\t\t\treturn AUTOCOMMIT_ON;\n\t\t\t\t} else {\n\t\t\t\t\treturn OTHER;\n\t\t\t\t}\n\t\t\tcase '0':\n\t\t\t\tif (stmt.length() == ++offset\n\t\t\t\t\t\t|| ParseUtil.isEOF(stmt.charAt(offset))) {\n\t\t\t\t\treturn AUTOCOMMIT_OFF;\n\t\t\t\t} else {\n\t\t\t\t\treturn OTHER;\n\t\t\t\t}\n\t\t\tcase 'O':\n\t\t\tcase 'o':\n\t\t\t\treturn autocommitOn(stmt, offset);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static int autocommitOn(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase 'N':\n\t\t\tcase 'n':\n\t\t\t\tif (stmt.length() == ++offset\n\t\t\t\t\t\t|| ParseUtil.isEOF(stmt.charAt(offset))) {\n\t\t\t\t\treturn AUTOCOMMIT_ON;\n\t\t\t\t} else {\n\t\t\t\t\treturn OTHER;\n\t\t\t\t}\n\t\t\tcase 'F':\n\t\t\tcase 'f':\n\t\t\t\treturn autocommitOff(stmt, offset);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET AUTOCOMMIT = OFF\n\tprivate static int autocommitOff(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase 'F':\n\t\t\tcase 'f':\n\t\t\t\tif (stmt.length() == ++offset\n\t\t\t\t\t\t|| ParseUtil.isEOF(stmt.charAt(offset))) {\n\t\t\t\t\treturn AUTOCOMMIT_OFF;\n\t\t\t\t} else {\n\t\t\t\t\treturn OTHER;\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET NAMES' '\n\tprivate static int names(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 5) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'A' || c1 == 'a') && (c2 == 'M' || c2 == 'm')\n\t\t\t\t\t&& (c3 == 'E' || c3 == 'e') && (c4 == 'S' || c4 == 's')\n\t\t\t\t\t&& stmt.charAt(++offset) == ' ') {\n\t\t\t\treturn (offset << 8) | NAMES;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET CHARACTER_SET_\n\tprivate static int characterSet(String stmt, int offset, int depth) {\n\t\tif (stmt.length() > offset + 14) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tchar c8 = stmt.charAt(++offset);\n\t\t\tchar c9 = stmt.charAt(++offset);\n\t\t\tchar c10 = stmt.charAt(++offset);\n\t\t\tchar c11 = stmt.charAt(++offset);\n\t\t\tchar c12 = stmt.charAt(++offset);\n\t\t\tchar c13 = stmt.charAt(++offset);\n\t\t\tchar c14 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'H' || c1 == 'h') && (c2 == 'A' || c2 == 'a')\n\t\t\t\t\t&& (c3 == 'R' || c3 == 'r') && (c4 == 'A' || c4 == 'a')\n\t\t\t\t\t&& (c5 == 'C' || c5 == 'c') && (c6 == 'T' || c6 == 't')\n\t\t\t\t\t&& (c7 == 'E' || c7 == 'e') && (c8 == 'R' || c8 == 'r')\n\t\t\t\t\t&& (c9 == '_') && (c10 == 'S' || c10 == 's')\n\t\t\t\t\t&& (c11 == 'E' || c11 == 'e') && (c12 == 'T' || c12 == 't')\n\t\t\t\t\t&& (c13 == '_')) {\n\t\t\t\tswitch (c14) {\n\t\t\t\tcase 'R':\n\t\t\t\tcase 'r':\n\t\t\t\t\treturn characterSetResults(stmt, offset);\n\t\t\t\tcase 'C':\n\t\t\t\tcase 'c':\n\t\t\t\t\treturn characterSetC(stmt, offset);\n\t\t\t\tdefault:\n\t\t\t\t\treturn OTHER;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET CHARACTER_SET_RESULTS =\n\tprivate static int characterSetResults(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 6) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e') && (c2 == 'S' || c2 == 's')\n\t\t\t\t\t&& (c3 == 'U' || c3 == 'u') && (c4 == 'L' || c4 == 'l')\n\t\t\t\t\t&& (c5 == 'T' || c5 == 't') && (c6 == 'S' || c6 == 's')) {\n\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\tcase ' ':\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase '=':\n\t\t\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\t\t\tcase ' ':\n\t\t\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\treturn (offset << 8) | CHARACTER_SET_RESULTS;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET CHARACTER_SET_C\n\tprivate static int characterSetC(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 1) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tswitch (c1) {\n\t\t\tcase 'o':\n\t\t\tcase 'O':\n\t\t\t\treturn characterSetConnection(stmt, offset);\n\t\t\tcase 'l':\n\t\t\tcase 'L':\n\t\t\t\treturn characterSetClient(stmt, offset);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET CHARACTER_SET_CONNECTION =\n\tprivate static int characterSetConnection(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 8) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tchar c8 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'N' || c1 == 'n') && (c2 == 'N' || c2 == 'n')\n\t\t\t\t\t&& (c3 == 'E' || c3 == 'e') && (c4 == 'C' || c4 == 'c')\n\t\t\t\t\t&& (c5 == 'T' || c5 == 't') && (c6 == 'I' || c6 == 'i')\n\t\t\t\t\t&& (c7 == 'O' || c7 == 'o') && (c8 == 'N' || c8 == 'n')) {\n\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\tcase ' ':\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase '=':\n\t\t\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\t\t\tcase ' ':\n\t\t\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\treturn (offset << 8) | CHARACTER_SET_CONNECTION;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET CHARACTER_SET_CLIENT =\n\tprivate static int characterSetClient(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 4) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'I' || c1 == 'i') && (c2 == 'E' || c2 == 'e')\n\t\t\t\t\t&& (c3 == 'N' || c3 == 'n') && (c4 == 'T' || c4 == 't')) {\n\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\tcase ' ':\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase '=':\n\t\t\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\t\t\tcase ' ':\n\t\t\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\treturn (offset << 8) | CHARACTER_SET_CLIENT;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\tprivate static int parseS(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 1) {\n\t\t\tchar c1 = stmt.charAt(offset + 1);\n\t\t\tif(c1=='E' || c1=='e'){\n\t\t\t\treturn session(stmt, offset);\n\t\t\t}\n\t\t\tif(c1=='Q' || c1=='q'){\n\t\t\t\treturn sqlSelectLimit(stmt, offset);\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\n\t// SET SQL_SELECT_LIMIT=N|DEFAULT\n\tprivate static int sqlSelectLimit(String stmt, int offset) {\n\t\t// SET SQL_SELECT_LIMIT=N|DEFAULT\n\t\tif (stmt.length() > offset + 15) {\n\t\t\tString var = stmt.substring(offset, offset+16);\n\t\t\tif(var.equalsIgnoreCase(\"SQL_SELECT_LIMIT\")) {\n\t\t\t\treturn SQL_SELECT_LIMIT;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET SESSION' '\n\t// SET SQL_SELECT_LIMIT=N|DEFAULT\n\tprivate static int session(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 7) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e') && (c2 == 'S' || c2 == 's')\n\t\t\t\t\t&& (c3 == 'S' || c3 == 's') && (c4 == 'I' || c4 == 'i')\n\t\t\t\t\t&& (c5 == 'O' || c5 == 'o') && (c6 == 'N' || c6 == 'n')\n\t\t\t\t\t&& stmt.charAt(++offset) == ' ') {\n//\t\t\t\tfor (;;) {\n\t\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\t\tcase ' ':\n\t\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tcase 'T':\n\t\t\t\t\t\tcase 't':\n\t\t\t\t\t\t\treturn transaction(stmt, offset);\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n//\t\t\t\t\treturn OTHER;\n//\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET [SESSION] TRANSACTION ISOLATION LEVEL\n\tprivate static int transaction(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 11) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tchar c8 = stmt.charAt(++offset);\n\t\t\tchar c9 = stmt.charAt(++offset);\n\t\t\tchar c10 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'R' || c1 == 'r') && (c2 == 'A' || c2 == 'a')\n\t\t\t\t\t&& (c3 == 'N' || c3 == 'n') && (c4 == 'S' || c4 == 's')\n\t\t\t\t\t&& (c5 == 'A' || c5 == 'a') && (c6 == 'C' || c6 == 'c')\n\t\t\t\t\t&& (c7 == 'T' || c7 == 't') && (c8 == 'I' || c8 == 'i')\n\t\t\t\t\t&& (c9 == 'O' || c9 == 'o') && (c10 == 'N' || c10 == 'n')\n\t\t\t\t\t&& stmt.charAt(++offset) == ' ') {\n//\t\t\t\tfor (;;) {\n\t\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\t\tcase ' ':\n\t\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tcase 'I':\n\t\t\t\t\t\tcase 'i':\n\t\t\t\t\t\t\treturn isolation(stmt, offset);\n\t\t\t\t\t\tcase 'R':\n\t\t\t\t\t\tcase 'r':\n\t\t\t\t\t\t\treturn transactionRead(stmt, offset);\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n//\t\t\t\t\treturn OTHER;\n//\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET [SESSION] TRANSACTION READ WRITE|ONLY\n\tprivate static int transactionRead(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 4) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e') && (c2 == 'A' || c2 == 'a')\n\t\t\t\t&& (c3 == 'D' || c3 == 'd')) {\n//\t\t\t\tfor (;;) {\n\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\t\tcase ' ':\n\t\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tcase 'W':\n\t\t\t\t\t\tcase 'w':\n\t\t\t\t\t\t\treturn transactionReadWrite(stmt, offset);\n\t\t\t\t\t\tcase 'O':\n\t\t\t\t\t\tcase 'o':\n\t\t\t\t\t\t\treturn transactionReadOnly(stmt, offset);\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t}\n\t\t\t\t}\n//\t\t\t\t\treturn OTHER;\n//\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET [SESSION] TRANSACTION READ WRITE\n\tprivate static int transactionReadWrite(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 4) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\n\t\t\tif ((c1 == 'R' || c1 == 'r')\n\t\t\t\t&& (c2 == 'I' || c2 == 'i')\n\t\t\t\t&& (c3 == 'T' || c3 == 't')\n\t\t\t\t&& (c4 == 'E' || c4 == 'e')\n\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn TX_READWRITE;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\t// SET [SESSION] TRANSACTION READ ONLY\n\tprivate static int transactionReadOnly(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 3) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\n\t\t\tif ((c1 == 'N' || c1 == 'n')\n\t\t\t\t&& (c2 == 'L' || c2 == 'l')\n\t\t\t\t&& (c3 == 'Y' || c3 == 'y')\n\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn TX_READONLY;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET [SESSION] TRANSACTION ISOLATION LEVEL\n\tprivate static int isolation(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 9) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tchar c8 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'S' || c1 == 's') && (c2 == 'O' || c2 == 'o')\n\t\t\t\t\t&& (c3 == 'L' || c3 == 'l') && (c4 == 'A' || c4 == 'a')\n\t\t\t\t\t&& (c5 == 'T' || c5 == 't') && (c6 == 'I' || c6 == 'i')\n\t\t\t\t\t&& (c7 == 'O' || c7 == 'o') && (c8 == 'N' || c8 == 'n')\n\t\t\t\t\t&& stmt.charAt(++offset) == ' ') {\n//\t\t\t\tfor (;;) {\n\t\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\t\tcase ' ':\n\t\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tcase 'L':\n\t\t\t\t\t\tcase 'l':\n\t\t\t\t\t\t\treturn level(stmt, offset);\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n//\t\t\t\t\treturn OTHER;\n//\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET [SESSION] TRANSACTION ISOLATION LEVEL' '\n\tprivate static int level(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 5) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e') && (c2 == 'V' || c2 == 'v')\n\t\t\t\t\t&& (c3 == 'E' || c3 == 'e') && (c4 == 'L' || c4 == 'l')\n\t\t\t\t\t&& stmt.charAt(++offset) == ' ') {\n//\t\t\t\tfor (;;) {\n\t\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\t\tcase ' ':\n\t\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tcase 'R':\n\t\t\t\t\t\tcase 'r':\n\t\t\t\t\t\t\treturn rCheck(stmt, offset);\n\t\t\t\t\t\tcase 'S':\n\t\t\t\t\t\tcase 's':\n\t\t\t\t\t\t\treturn serializable(stmt, offset);\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n//\t\t\t\t\treturn OTHER;\n//\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SET [SESSION] TRANSACTION ISOLATION LEVEL SERIALIZABLE\n\tprivate static int serializable(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 11) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tchar c8 = stmt.charAt(++offset);\n\t\t\tchar c9 = stmt.charAt(++offset);\n\t\t\tchar c10 = stmt.charAt(++offset);\n\t\t\tchar c11 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e')\n\t\t\t\t\t&& (c2 == 'R' || c2 == 'r')\n\t\t\t\t\t&& (c3 == 'I' || c3 == 'i')\n\t\t\t\t\t&& (c4 == 'A' || c4 == 'a')\n\t\t\t\t\t&& (c5 == 'L' || c5 == 'l')\n\t\t\t\t\t&& (c6 == 'I' || c6 == 'i')\n\t\t\t\t\t&& (c7 == 'Z' || c7 == 'z')\n\t\t\t\t\t&& (c8 == 'A' || c8 == 'a')\n\t\t\t\t\t&& (c9 == 'B' || c9 == 'b')\n\t\t\t\t\t&& (c10 == 'L' || c10 == 'l')\n\t\t\t\t\t&& (c11 == 'E' || c11 == 'e')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn TX_SERIALIZABLE;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// READ' '|REPEATABLE\n\tprivate static int rCheck(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase 'E':\n\t\t\tcase 'e':\n\t\t\t\treturn eCheck(stmt, offset);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// READ' '|REPEATABLE\n\tprivate static int eCheck(String stmt, int offset) {\n\t\tif (stmt.length() > ++offset) {\n\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\tcase 'A':\n\t\t\tcase 'a':\n\t\t\t\treturn aCheck(stmt, offset);\n\t\t\tcase 'P':\n\t\t\tcase 'p':\n\t\t\t\treturn pCheck(stmt, offset);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// READ' '\n\tprivate static int aCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 2) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'D' || c1 == 'd') && stmt.charAt(++offset) == ' ') {\n//\t\t\t\tfor (;;) {\n\t\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\t\tcase ' ':\n\t\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tcase 'C':\n\t\t\t\t\t\tcase 'c':\n\t\t\t\t\t\t\treturn committed(stmt, offset);\n\t\t\t\t\t\tcase 'U':\n\t\t\t\t\t\tcase 'u':\n\t\t\t\t\t\t\treturn uncommitted(stmt, offset);\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n//\t\t\t\t\treturn OTHER;\n//\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// COMMITTED\n\tprivate static int committed(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 8) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tchar c8 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'O' || c1 == 'o')\n\t\t\t\t\t&& (c2 == 'M' || c2 == 'm')\n\t\t\t\t\t&& (c3 == 'M' || c3 == 'm')\n\t\t\t\t\t&& (c4 == 'I' || c4 == 'i')\n\t\t\t\t\t&& (c5 == 'T' || c5 == 't')\n\t\t\t\t\t&& (c6 == 'T' || c6 == 't')\n\t\t\t\t\t&& (c7 == 'E' || c7 == 'e')\n\t\t\t\t\t&& (c8 == 'D' || c8 == 'd')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn TX_READ_COMMITTED;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// UNCOMMITTED\n\tprivate static int uncommitted(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 10) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tchar c8 = stmt.charAt(++offset);\n\t\t\tchar c9 = stmt.charAt(++offset);\n\t\t\tchar c10 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'N' || c1 == 'n')\n\t\t\t\t\t&& (c2 == 'C' || c2 == 'c')\n\t\t\t\t\t&& (c3 == 'O' || c3 == 'o')\n\t\t\t\t\t&& (c4 == 'M' || c4 == 'm')\n\t\t\t\t\t&& (c5 == 'M' || c5 == 'm')\n\t\t\t\t\t&& (c6 == 'I' || c6 == 'i')\n\t\t\t\t\t&& (c7 == 'T' || c7 == 't')\n\t\t\t\t\t&& (c8 == 'T' || c8 == 't')\n\t\t\t\t\t&& (c9 == 'E' || c9 == 'e')\n\t\t\t\t\t&& (c10 == 'D' || c10 == 'd')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn TX_READ_UNCOMMITTED;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// REPEATABLE\n\tprivate static int pCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 8) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tchar c7 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e') && (c2 == 'A' || c2 == 'a')\n\t\t\t\t\t&& (c3 == 'T' || c3 == 't') && (c4 == 'A' || c4 == 'a')\n\t\t\t\t\t&& (c5 == 'B' || c5 == 'b') && (c6 == 'L' || c6 == 'l')\n\t\t\t\t\t&& (c7 == 'E' || c7 == 'e') && stmt.charAt(++offset) == ' ') {\n//\t\t\t\tfor (;;) {\n\t\t\t\t\twhile (stmt.length() > ++offset) {\n\t\t\t\t\t\tswitch (stmt.charAt(offset)) {\n\t\t\t\t\t\tcase ' ':\n\t\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tcase 'R':\n\t\t\t\t\t\tcase 'r':\n\t\t\t\t\t\t\treturn prCheck(stmt, offset);\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn OTHER;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n//\t\t\t\t\treturn OTHER;\n//\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// READ\n\tprivate static int prCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + 3) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'E' || c1 == 'e')\n\t\t\t\t\t&& (c2 == 'A' || c2 == 'a')\n\t\t\t\t\t&& (c3 == 'D' || c3 == 'd')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn TX_REPEATED_READ;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/parser/ServerParseShow.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.parser;\n\nimport java.util.regex.Pattern;\n\nimport io.mycat.route.parser.util.ParseUtil;\n\n/**\n * @author mycat\n */\npublic final class ServerParseShow {\n\n\tpublic static final int OTHER = -1;\n\tpublic static final int DATABASES = 1;\n\tpublic static final int DATASOURCES = 2;\n\tpublic static final int MYCAT_STATUS = 3;\n\tpublic static final int MYCAT_CLUSTER = 4;\n\tpublic static final int TABLES = 5;\n    public static final int FULLTABLES =65;\n\tprivate static final Pattern SHOWTablePattern = Pattern.compile(\n\t\t\t\"^\\\\s*(SHOW)(\\\\s+(full|all))?\\\\s+(TABLES)(\\\\s+(FROM)\\\\s+([a-zA-Z_0-9]+))?((\\\\s+(like)\\\\s+'((. *)*)'\\\\s*)|(\\\\s+(where)\\\\s+((. *)*)\\\\s*))?\",\n            Pattern.CASE_INSENSITIVE);\n\n\tpublic static int parse(String stmt, int offset) {\n\t\tint i = offset;\n\t\tfor (; i < stmt.length(); i++) {\n\t\t\tswitch (stmt.charAt(i)) {\n\t\t\tcase ' ':\n                continue;\n                case 'F':\n                case 'f':\n              return fullTableCheck(stmt,i) ;\n\t\t\tcase '/':\n\t\t\tcase '#':\n\t\t\t\ti = ParseUtil.comment(stmt, i);\n\t\t\t\tcontinue;\n\t\t\tcase 'M':\n\t\t\tcase 'm':\n\t\t\t\treturn mycatCheck(stmt, i);\n\t\t\tcase 'D':\n\t\t\tcase 'd':\n\t\t\t\treturn dataCheck(stmt, i);\n\t\t\tcase 'T':\n\t\t\tcase 't':\n\t\t\t\treturn tableCheck(stmt, i);\n\t\t\tdefault:\n\t\t\t\treturn OTHER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SHOW MYCAT_\n\tstatic int mycatCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"ycat_?\".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'Y' || c1 == 'y') && (c2 == 'C' || c2 == 'c')\n\t\t\t\t\t&& (c3 == 'A' || c3 == 'a') && (c4 == 'T' || c4 == 't')\n\t\t\t\t\t&& (c5 == '_')) {\n\t\t\t\tswitch (stmt.charAt(++offset)) {\n\t\t\t\tcase 'S':\n\t\t\t\tcase 's':\n\t\t\t\t\treturn showMyCatStatus(stmt, offset);\n\t\t\t\tcase 'C':\n\t\t\t\tcase 'c':\n\t\t\t\t\treturn showMyCatCluster(stmt, offset);\n\t\t\t\tdefault:\n\t\t\t\t\treturn OTHER;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SHOW MyCat_STATUS\n\tstatic int showMyCatStatus(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"tatus\".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 't' || c1 == 'T')\n\t\t\t\t\t&& (c2 == 'a' || c2 == 'A')\n\t\t\t\t\t&& (c3 == 't' || c3 == 'T')\n\t\t\t\t\t&& (c4 == 'u' || c4 == 'U')\n\t\t\t\t\t&& (c5 == 's' || c5 == 'S')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn MYCAT_STATUS;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SHOW MyCat_CLUSTER\n\tstatic int showMyCatCluster(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"luster\".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'L' || c1 == 'l')\n\t\t\t\t\t&& (c2 == 'U' || c2 == 'u')\n\t\t\t\t\t&& (c3 == 'S' || c3 == 's')\n\t\t\t\t\t&& (c4 == 'T' || c4 == 't')\n\t\t\t\t\t&& (c5 == 'E' || c5 == 'e')\n\t\t\t\t\t&& (c6 == 'R' || c6 == 'r')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn MYCAT_CLUSTER;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SHOW DATA\n\tstatic int dataCheck(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"ata?\".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'A' || c1 == 'a') && (c2 == 'T' || c2 == 't')\n\t\t\t\t\t&& (c3 == 'A' || c3 == 'a')) {\n\t\t\t\tswitch (stmt.charAt(++offset)) {\n\t\t\t\tcase 'B':\n\t\t\t\tcase 'b':\n\t\t\t\t\treturn showDatabases(stmt, offset);\n\t\t\t\tcase 'S':\n\t\t\t\tcase 's':\n\t\t\t\t\treturn showDataSources(stmt, offset);\n\t\t\t\tdefault:\n\t\t\t\t\treturn OTHER;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n    public static int fullTableCheck(String stmt, int offset) {\n\t\tif (SHOWTablePattern.matcher(stmt).matches()) {\n            return FULLTABLES;\n        }\n        return OTHER;\n    }\n\n\t// SHOW TABLE\n\tpublic static int tableCheck(String stmt, int offset) {\n\t\tif (SHOWTablePattern.matcher(stmt).matches()) {\n\t\t\treturn TABLES;\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SHOW DATABASES\n\tstatic int showDatabases(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"ases\".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'A' || c1 == 'a')\n\t\t\t\t\t&& (c2 == 'S' || c2 == 's')\n\t\t\t\t\t&& (c3 == 'E' || c3 == 'e')\n\t\t\t\t\t&& (c4 == 'S' || c4 == 's')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn DATABASES;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t// SHOW DATASOURCES\n\tstatic int showDataSources(String stmt, int offset) {\n\t\tif (stmt.length() > offset + \"ources\".length()) {\n\t\t\tchar c1 = stmt.charAt(++offset);\n\t\t\tchar c2 = stmt.charAt(++offset);\n\t\t\tchar c3 = stmt.charAt(++offset);\n\t\t\tchar c4 = stmt.charAt(++offset);\n\t\t\tchar c5 = stmt.charAt(++offset);\n\t\t\tchar c6 = stmt.charAt(++offset);\n\t\t\tif ((c1 == 'O' || c1 == 'o')\n\t\t\t\t\t&& (c2 == 'U' || c2 == 'u')\n\t\t\t\t\t&& (c3 == 'R' || c3 == 'r')\n\t\t\t\t\t&& (c4 == 'C' || c4 == 'c')\n\t\t\t\t\t&& (c5 == 'E' || c5 == 'e')\n\t\t\t\t\t&& (c6 == 'S' || c6 == 's')\n\t\t\t\t\t&& (stmt.length() == ++offset || ParseUtil.isEOF(stmt\n\t\t\t\t\t\t\t.charAt(offset)))) {\n\t\t\t\treturn DATASOURCES;\n\t\t\t}\n\t\t}\n\t\treturn OTHER;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/parser/ServerParseStart.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.parser;\n\nimport io.mycat.route.parser.util.ParseUtil;\n\n/**\n * @author mycat\n */\npublic final class ServerParseStart {\n\n    public static final int OTHER = -1;\n    public static final int TRANSACTION = 1;\n\n    public static int parse(String stmt, int offset) {\n        int i = offset;\n        for (; i < stmt.length(); i++) {\n            switch (stmt.charAt(i)) {\n            case ' ':\n                continue;\n            case '/':\n            case '#':\n                i = ParseUtil.comment(stmt, i);\n                continue;\n            case 'T':\n            case 't':\n                return transactionCheck(stmt, i);\n            default:\n                return OTHER;\n            }\n        }\n        return OTHER;\n    }\n\n    // START TRANSACTION\n    static int transactionCheck(String stmt, int offset) {\n        if (stmt.length() > offset + \"ransaction\".length()) {\n            char c1 = stmt.charAt(++offset);\n            char c2 = stmt.charAt(++offset);\n            char c3 = stmt.charAt(++offset);\n            char c4 = stmt.charAt(++offset);\n            char c5 = stmt.charAt(++offset);\n            char c6 = stmt.charAt(++offset);\n            char c7 = stmt.charAt(++offset);\n            char c8 = stmt.charAt(++offset);\n            char c9 = stmt.charAt(++offset);\n            char c10 = stmt.charAt(++offset);\n            if ((c1 == 'R' || c1 == 'r') && (c2 == 'A' || c2 == 'a') && (c3 == 'N' || c3 == 'n')\n                    && (c4 == 'S' || c4 == 's') && (c5 == 'A' || c5 == 'a') && (c6 == 'C' || c6 == 'c')\n                    && (c7 == 'T' || c7 == 't') && (c8 == 'I' || c8 == 'i') && (c9 == 'O' || c9 == 'o')\n                    && (c10 == 'N' || c10 == 'n')\n                    && (stmt.length() == ++offset || ParseUtil.isEOF(stmt.charAt(offset)))) {\n                return TRANSACTION;\n            }\n        }\n        return OTHER;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/CharacterSet.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport static io.mycat.server.parser.ServerParseSet.CHARACTER_SET_CLIENT;\nimport static io.mycat.server.parser.ServerParseSet.CHARACTER_SET_CONNECTION;\nimport static io.mycat.server.parser.ServerParseSet.CHARACTER_SET_RESULTS;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.config.ErrorCode;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParseSet;\nimport io.mycat.util.SetIgnoreUtil;\nimport io.mycat.util.SplitUtil;\n\n/**\n * 字符集属性设置\n */\n/**\n * @author mycat\n */\npublic class CharacterSet {\n\n    private static final Logger logger = LoggerFactory.getLogger(CharacterSet.class);\n\n    public static void response(String stmt, ServerConnection c, int rs) {\n        if (-1 == stmt.indexOf(',')) {\n            /* 单个属性 */\n            oneSetResponse(stmt, c, rs);\n        } else {\n            /* 多个属性 ,但是只关注CHARACTER_SET_RESULTS，CHARACTER_SET_CONNECTION */\n            multiSetResponse(stmt, c, rs);\n        }\n    }\n\n    private static void oneSetResponse(String stmt, ServerConnection c, int rs) {\n        if ((rs & 0xff) == CHARACTER_SET_CLIENT) {\n            /* 忽略client属性设置 */\n            c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n        } else {\n            String charset = stmt.substring(rs >>> 8).trim();\n            if (charset.endsWith(\";\")) {\n                /* 结尾为 ; 标识符 */\n                charset = charset.substring(0, charset.length() - 1);\n            }\n\n            if (charset.startsWith(\"'\") || charset.startsWith(\"`\")) {\n                /* 与mysql保持一致，引号里的字符集不做trim操作 */\n                charset = charset.substring(1, charset.length() - 1);\n            }\n\n            // 设置字符集\n            setCharset(charset, c);\n        }\n    }\n\n    private static void multiSetResponse(String stmt, ServerConnection c, int rs) {\n        String charResult = \"null\";\n        String charConnection = \"null\";\n        String[] sqlList = SplitUtil.split(stmt, ',', false);\n\n        // check first\n        switch (rs & 0xff) {\n        case CHARACTER_SET_RESULTS:\n            charResult = sqlList[0].substring(rs >>> 8).trim();\n            break;\n        case CHARACTER_SET_CONNECTION:\n            charConnection = sqlList[0].substring(rs >>> 8).trim();\n            break;\n        }\n\n        // check remaining\n        for (int i = 1; i < sqlList.length; i++) {\n            String sql = new StringBuilder(\"set \").append(sqlList[i]).toString();\n            if ((i + 1 == sqlList.length) && sql.endsWith(\";\")) {\n                /* 去掉末尾的 ‘;’ */\n                sql = sql.substring(0, sql.length() - 1);\n            }\n            int rs2 = ServerParseSet.parse(sql, \"set\".length());\n            switch (rs2 & 0xff) {\n            case CHARACTER_SET_RESULTS:\n                charResult = sql.substring(rs2 >>> 8).trim();\n                break;\n            case CHARACTER_SET_CONNECTION:\n                charConnection = sql.substring(rs2 >>> 8).trim();\n                break;\n            case CHARACTER_SET_CLIENT:\n                break;\n            default:\n            \tboolean ignore = SetIgnoreUtil.isIgnoreStmt( sql );\n            \tif ( !ignore ) {\n\t                StringBuilder s = new StringBuilder();\n\t                logger.warn(s.append(c).append(sql).append(\" is not executed\").toString());\n            \t}\n            }\n        }\n\n        if (charResult.startsWith(\"'\") || charResult.startsWith(\"`\")) {\n            charResult = charResult.substring(1, charResult.length() - 1);\n        }\n        if (charConnection.startsWith(\"'\") || charConnection.startsWith(\"`\")) {\n            charConnection = charConnection.substring(1, charConnection.length() - 1);\n        }\n\n        // 如果其中一个为null，则以另一个为准。\n        if (\"null\".equalsIgnoreCase(charResult)) {\n            setCharset(charConnection, c);\n            return;\n        }\n        if (\"null\".equalsIgnoreCase(charConnection)) {\n            setCharset(charResult, c);\n            return;\n        }\n        if (charConnection.equalsIgnoreCase(charResult)) {\n            setCharset(charConnection, c);\n        } else {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"charset is not consistent:[connection=\").append(charConnection);\n            sb.append(\",results=\").append(charResult).append(']');\n            c.writeErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, sb.toString());\n        }\n    }\n\n    private static void setCharset(String charset, ServerConnection c) {\n        if (\"null\".equalsIgnoreCase(charset)) {\n            /* 忽略字符集为null的属性设置 */\n            c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n        } else if (c.setCharset(charset)) {\n            c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n        } else {\n            try {\n                if (c.setCharsetIndex(Integer.parseInt(charset))) {\n                    c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n                } else {\n                    c.writeErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, \"Unknown charset :\" + charset);\n                }\n            } catch (RuntimeException e) {\n                c.writeErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, \"Unknown charset :\" + charset);\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/ClientHeartbeatResponse.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.server.response;\r\n\r\nimport java.nio.ByteBuffer;\r\n\r\nimport io.mycat.backend.mysql.PacketUtil;\r\nimport io.mycat.config.Fields;\r\nimport io.mycat.net.mysql.EOFPacket;\r\nimport io.mycat.net.mysql.FieldPacket;\r\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\r\nimport io.mycat.net.mysql.RowDataPacket;\r\nimport io.mycat.server.ServerConnection;\r\nimport io.mycat.util.StringUtil;\r\n\r\n/**\r\n * 构建select 1语句返回包：\r\n * 客户端连接池给mycat发送select 1或select 1 from dual心跳语句,mycat拦截，用该类直接返回包\r\n */\r\npublic class ClientHeartbeatResponse {\r\n    private static byte[] RESPONSE_DATA = null;\r\n\r\n    public static void response(ServerConnection c) {\r\n        if (RESPONSE_DATA == null) {\r\n            synchronized (ClientHeartbeatResponse.class) {\r\n                if (RESPONSE_DATA == null) {\r\n                    RESPONSE_DATA = buildPacket(c);\r\n                }\r\n            }\r\n        }\r\n\r\n        ByteBuffer buffer = c.allocate();\r\n        buffer.put(RESPONSE_DATA);\r\n        c.write(buffer);\r\n    }\r\n\r\n    private static byte[] buildPacket(ServerConnection c) {\r\n        byte packetId = 0;\r\n        final int FIELD_COUNT = 1;\r\n        final String FIELD_NAME = \"1\";\r\n        final String FIELD_VALUE = \"1\";\r\n\t\tByteBuffer buffer = c.allocate();\r\n\r\n        // write header\r\n        ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\r\n        header.packetId = ++packetId;\r\n\t\tbuffer = header.write(buffer, c, false);\r\n\r\n        FieldPacket field = PacketUtil.getField(FIELD_NAME, Fields.FIELD_TYPE_VAR_STRING);\r\n        field.packetId = ++packetId;\r\n        buffer = field.write(buffer, c, false);\r\n\r\n        EOFPacket eof = new EOFPacket();\r\n        eof.packetId = ++packetId;\r\n\t\tbuffer = eof.write(buffer, c, false);\r\n\r\n        // write body\r\n\t\tRowDataPacket row = new RowDataPacket(FIELD_COUNT);\r\n        row.add(StringUtil.encode(FIELD_VALUE, c.getCharset()));\r\n\t\trow.packetId = ++packetId;\r\n\t\tbuffer = row.write(buffer, c, false);\r\n\r\n        EOFPacket rowEof = new EOFPacket();\r\n        rowEof.packetId = ++packetId;\r\n        buffer = rowEof.write(buffer, c, false);\r\n\r\n        buffer.flip();\r\n        byte[] data = new byte[buffer.limit()];\r\n        buffer.get(data);\r\n\r\n        // recycle buffer\r\n        c.recycle(buffer);\r\n        return data;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/server/response/Heartbeat.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.HeartbeatPacket;\nimport io.mycat.net.mysql.OkPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.TimeUtil;\n\n/**\n * @author mycat\n */\npublic class Heartbeat {\n\n    private static final Logger HEARTBEAT = LoggerFactory.getLogger(\"heartbeat\");\n\n    public static void response(ServerConnection c, byte[] data) {\n        HeartbeatPacket hp = new HeartbeatPacket();\n        hp.read(data);\n        if (MycatServer.getInstance().isOnline()) {\n            OkPacket ok = new OkPacket();\n            ok.packetId = 1;\n            ok.affectedRows = hp.id;\n            ok.serverStatus = 2;\n            ok.write(c);\n            if (HEARTBEAT.isInfoEnabled()) {\n                HEARTBEAT.info(responseMessage(\"OK\", c, hp.id));\n            }\n        } else {\n            ErrorPacket error = new ErrorPacket();\n            error.packetId = 1;\n            error.errno = ErrorCode.ER_SERVER_SHUTDOWN;\n            error.message = String.valueOf(hp.id).getBytes();\n            error.write(c);\n            if (HEARTBEAT.isInfoEnabled()) {\n                HEARTBEAT.info(responseMessage(\"ERROR\", c, hp.id));\n            }\n        }\n    }\n\n    private static String responseMessage(String action, ServerConnection c, long id) {\n        return new StringBuilder(\"RESPONSE:\").append(action).append(\", id=\").append(id).append(\", host=\")\n                .append(c.getHost()).append(\", port=\").append(c.getPort()).append(\", time=\")\n                .append(TimeUtil.currentTimeMillis()).toString();\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/InformationSchemaProfiling.java",
    "content": "package io.mycat.server.response;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.server.ServerConnection;\n\nimport java.nio.ByteBuffer;\n\n\npublic class InformationSchemaProfiling\n{\n\n    private static final int FIELD_COUNT = 3;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n\n\n\t/**\n\t * response method.\n\t * @param c\n\t */\n\tpublic static void response(ServerConnection c) {\n\n\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"State\" , Fields.FIELD_TYPE_VAR_STRING);\n        fields[i].packetId = ++packetId;\n        fields[i+1] = PacketUtil.getField(\"Duration\" , Fields.FIELD_TYPE_DECIMAL);\n        fields[i+1].packetId = ++packetId;\n\n        fields[i+2] = PacketUtil.getField(\"Percentage\" , Fields.FIELD_TYPE_DECIMAL);\n        fields[i+2].packetId = ++packetId;\n        eof.packetId = ++packetId;\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n         packetId = eof.packetId;\n\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n\t\t\n\t\t\n    }\n\n\n\n\n\n\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/response/InformationSchemaProfilingSqlyog.java",
    "content": "package io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.server.ServerConnection;\n\n\npublic class InformationSchemaProfilingSqlyog\n{\n\n    private static final int FIELD_COUNT = 2;\n\n\n\n\t/**\n\t * response method.\n\t * @param c\n\t */\n\tpublic static void response(ServerConnection c) {\n\n        ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n        FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n        EOFPacket eof = new EOFPacket();\n\n\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"State\" , Fields.FIELD_TYPE_VAR_STRING);\n        fields[i].packetId = ++packetId;\n        fields[i+1] = PacketUtil.getField(\"duration (summed) in sec\" , Fields.FIELD_TYPE_DECIMAL);\n        fields[i+1].packetId = ++packetId;\n\n        eof.packetId = ++packetId;\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n         packetId = eof.packetId;\n\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n\t\t\n\t\t\n    }\n\n\n\n\n\n\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/response/Ping.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.OkPacket;\n\n/**\n * 加入了offline状态推送，用于心跳语句。\n * \n * @author mycat\n */\npublic class Ping {\n\n    private static final ErrorPacket error = PacketUtil.getShutdown();\n\n    public static void response(FrontendConnection c) {\n        if (MycatServer.getInstance().isOnline()) {\n            c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));\n        } else {\n            error.write(c);\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/PreparedStmtResponse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.backend.mysql.PreparedStatement;\nimport io.mycat.net.FrontendConnection;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.PreparedOkPacket;\n\n/**\n * @author mycat\n */\npublic class PreparedStmtResponse {\n    private static final Logger LOGGER = LoggerFactory.getLogger(PreparedStmtResponse.class);\n    public static void response(PreparedStatement pstmt, FrontendConnection c) {\n        if (pstmt.getParams() != null || pstmt.getFields() != null) {\n            LOGGER.info(\"prepared metadata from backend mysql data and is complete\");\n            responseCompleteMetaData(pstmt, c);\n        } else {\n            LOGGER.info(\"prepared metadata comes from self-construction and is incomplete\");\n            responseIncompleteMetaData(pstmt, c);\n        }\n    }\n\n    private static void responseCompleteMetaData(PreparedStatement pstmt, FrontendConnection c) {\n        byte packetId = 0;\n\n        // write preparedOk packet\n        PreparedOkPacket preparedOk = new PreparedOkPacket();\n        preparedOk.packetId = ++packetId;\n        preparedOk.statementId = pstmt.getId();\n        preparedOk.columnsNumber = pstmt.getColumnsNumber();\n        preparedOk.parametersNumber = pstmt.getParametersNumber();\n        ByteBuffer buffer = preparedOk.write(c.allocate(), c, true);\n\n        // write parameter field packet\n        int parametersNumber = pstmt.getParametersNumber();\n        if (parametersNumber > 0) {\n            for (FieldPacket param : pstmt.getParams()) {\n                param.packetId = ++packetId;\n                buffer = param.write(buffer, c, true);\n            }\n            EOFPacket eof = new EOFPacket();\n            eof.packetId = ++packetId;\n            buffer = eof.write(buffer, c, true);\n        }\n\n        // write column field packet\n        int columnsNumber = pstmt.getColumnsNumber();\n        if (columnsNumber > 0) {\n            for (FieldPacket field : pstmt.getFields()) {\n                field.packetId = ++packetId;\n                buffer = field.write(buffer, c, true);\n            }\n            EOFPacket eof = new EOFPacket();\n            eof.packetId = ++packetId;\n            buffer = eof.write(buffer, c, true);\n        }\n\n        // send buffer\n        c.write(buffer);\n    }\n\n    /**\n     * 这种返回结果，元数据不全。用c驱动读取元数据信息会报错，比如下面的用法：\n     *   prepare_meta_result = mysql_stmt_result_metadata(stmt); \n     *   column_count= mysql_num_fields(prepare_meta_result); \n     * @param pstmt\n     * @param c\n     */\n    private static void responseIncompleteMetaData(PreparedStatement pstmt, FrontendConnection c) {\n        byte packetId = 0;\n\n        // write preparedOk packet\n        PreparedOkPacket preparedOk = new PreparedOkPacket();\n        preparedOk.packetId = ++packetId;\n        preparedOk.statementId = pstmt.getId();\n        preparedOk.columnsNumber = pstmt.getColumnsNumber();\n        preparedOk.parametersNumber = pstmt.getParametersNumber();\n        ByteBuffer buffer = preparedOk.write(c.allocate(), c, true);\n\n        // write parameter field packet\n        int parametersNumber = preparedOk.parametersNumber;\n        if (parametersNumber > 0) {\n            for (int i = 0; i < parametersNumber; i++) {\n                FieldPacket field = new FieldPacket();\n                field.packetId = ++packetId;\n                buffer = field.write(buffer, c,true);\n            }\n            EOFPacket eof = new EOFPacket();\n            eof.packetId = ++packetId;\n            buffer = eof.write(buffer, c,true);\n        }\n\n        // write column field packet\n        int columnsNumber = preparedOk.columnsNumber;\n        if (columnsNumber > 0) {\n            String[] columnNames = pstmt.getColumnNames();\n            for (int i = 0; i < columnsNumber; i++) {\n                FieldPacket field = new FieldPacket();\n                field.name= columnNames[i].getBytes();\n                field.packetId = ++packetId;\n                buffer = field.write(buffer, c,true);\n            }\n            EOFPacket eof = new EOFPacket();\n            eof.packetId = ++packetId;\n            buffer = eof.write(buffer, c,true);\n        }\n\n        // send buffer\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/SelectConnnectID.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.RandomUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class SelectConnnectID {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    private static final ErrorPacket error = PacketUtil.getShutdown();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"CONNECTION_ID()\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c) {\n        if (MycatServer.getInstance().isOnline()) {\n            ByteBuffer buffer = c.allocate();\n            buffer = header.write(buffer, c,true);\n            for (FieldPacket field : fields) {\n                buffer = field.write(buffer, c,true);\n            }\n            buffer = eof.write(buffer, c,true);\n            byte packetId = eof.packetId;\n            RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n            row.add(getConnectID(c));\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n            EOFPacket lastEof = new EOFPacket();\n            lastEof.packetId = ++packetId;\n            buffer = lastEof.write(buffer, c,true);\n            c.write(buffer);\n        } else {\n            error.write(c);\n        }\n    }\n\n    private static byte[] getConnectID(ServerConnection c) {\n        StringBuilder sb = new StringBuilder();\n        sb.append(new String(RandomUtil.randomBytes(10000)));\n        return StringUtil.encode(sb.toString(), c.getCharset());\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/SelectDatabase.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class SelectDatabase {\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"DATABASE()\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c) {\n        ByteBuffer buffer = c.allocate();\n        buffer = header.write(buffer, c,true);\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n        buffer = eof.write(buffer, c,true);\n        byte packetId = eof.packetId;\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(StringUtil.encode(c.getSchema(), c.getCharset()));\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/SelectIdentity.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.parser.util.ParseUtil;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.LongUtil;\n\n/**\n * @author mycat\n */\npublic class SelectIdentity {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    static {\n        byte packetId = 0;\n        header.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c, String stmt, int aliasIndex, final String orgName) {\n        String alias = ParseUtil.parseAlias(stmt, aliasIndex);\n        if (alias == null) {\n            alias = orgName;\n        }\n\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        byte packetId = header.packetId;\n        FieldPacket field = PacketUtil.getField(alias, orgName, Fields.FIELD_TYPE_LONGLONG);\n        field.packetId = ++packetId;\n        buffer = field.write(buffer, c,true);\n\n        // write eof\n        EOFPacket eof = new EOFPacket();\n        eof.packetId = ++packetId;\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(LongUtil.toBytes(c.getLastInsertId()));\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/SelectLastInsertId.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.parser.util.ParseUtil;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.LongUtil;\n\n/**\n * @author mycat\n */\npublic class SelectLastInsertId {\n\n    private static final String ORG_NAME = \"LAST_INSERT_ID()\";\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    static {\n        byte packetId = 0;\n        header.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c, String stmt, int aliasIndex) {\n        String alias = ParseUtil.parseAlias(stmt, aliasIndex);\n        if (alias == null) {\n            alias = ORG_NAME;\n        }\n\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        byte packetId = header.packetId;\n        FieldPacket field = PacketUtil.getField(alias, ORG_NAME, Fields.FIELD_TYPE_LONGLONG);\n        field.packetId = ++packetId;\n        buffer = field.write(buffer, c,true);\n\n        // write eof\n        EOFPacket eof = new EOFPacket();\n        eof.packetId = ++packetId;\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(LongUtil.toBytes(c.getLastInsertId()));\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/SelectTxReadOnly.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\nimport java.nio.ByteBuffer;\n\n/**\n * @author mycat\n */\npublic class SelectTxReadOnly {\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    private static byte[] longbt= LongUtil.toBytes(0)     ;\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"@@session.tx_read_only\", Fields.FIELD_TYPE_LONG);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n\n    }\n\n    public static void response(ServerConnection c) {\n        ByteBuffer buffer = c.allocate();\n        buffer = header.write(buffer, c,true);\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n        buffer = eof.write(buffer, c,true);\n        byte packetId = eof.packetId;\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(longbt);\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n        c.write(buffer);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/response/SelectUser.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class SelectUser {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    private static final ErrorPacket error = PacketUtil.getShutdown();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"USER()\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c) {\n        if (MycatServer.getInstance().isOnline()) {\n            ByteBuffer buffer = c.allocate();\n            buffer = header.write(buffer, c,true);\n            for (FieldPacket field : fields) {\n                buffer = field.write(buffer, c,true);\n            }\n            buffer = eof.write(buffer, c,true);\n            byte packetId = eof.packetId;\n            RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n            row.add(getUser(c));\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n            EOFPacket lastEof = new EOFPacket();\n            lastEof.packetId = ++packetId;\n            buffer = lastEof.write(buffer, c,true);\n            c.write(buffer);\n        } else {\n            error.write(c);\n        }\n    }\n\n    private static byte[] getUser(ServerConnection c) {\n        StringBuilder sb = new StringBuilder();\n        sb.append(c.getUser()).append('@').append(c.getHost());\n        return StringUtil.encode(sb.toString(), c.getCharset());\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/SelectVariables.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport com.google.common.base.Splitter;\n\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.NonBlockingSession;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.LongUtil;\nimport io.mycat.util.StringUtil;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * @author mycat\n */\npublic final class SelectVariables\n{\n    private static final Logger LOGGER = LoggerFactory.getLogger(SelectVariables.class);\n\n\n    public static void execute(ServerConnection c, String sql) {\n\n     String subSql=   sql.substring(sql.indexOf(\"SELECT\")+6);\n    List<String>  splitVar=   Splitter.on(\",\").omitEmptyStrings().trimResults().splitToList(subSql) ;\n        splitVar=convert(splitVar);\n        int FIELD_COUNT = splitVar.size();\n        ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n        FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        for (int i1 = 0, splitVarSize = splitVar.size(); i1 < splitVarSize; i1++)\n        {\n            String s = splitVar.get(i1);\n            fields[i] = PacketUtil.getField(s, Fields.FIELD_TYPE_VAR_STRING);\n            fields[i++].packetId = ++packetId;\n        }\n\n\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n\n        EOFPacket eof = new EOFPacket();\n        eof.packetId = ++packetId;\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        //byte packetId = eof.packetId;\n\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        for (int i1 = 0, splitVarSize = splitVar.size(); i1 < splitVarSize; i1++)\n        {\n            String s = splitVar.get(i1);\n            String value=  variables.get(s) ==null?\"\":variables.get(s) ;\n            row.add(value.getBytes());\n\n        }\n\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n\n\n\n        // write lastEof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // write buffer\n        c.write(buffer);\n    }\n\n    private static List<String> convert(List<String> in)\n    {\n        List<String> out=new ArrayList<>();\n        for (String s : in)\n        {\n          int asIndex=s.toUpperCase().indexOf(\" AS \");\n            if(asIndex!=-1)\n            {\n                out.add(s.substring(asIndex+4)) ;\n            }\n        }\n         if(out.isEmpty())\n         {\n             return in;\n         }  else\n         {\n             return out;\n         }\n\n\n    }\n\n\n\n\n    private static final Map<String, String> variables = new HashMap<String, String>();\n    static {\n        variables.put(\"@@character_set_client\", \"utf8\");\n        variables.put(\"@@character_set_connection\", \"utf8\");\n        variables.put(\"@@character_set_results\", \"utf8\");\n        variables.put(\"@@character_set_server\", \"utf8\");\n        variables.put(\"@@init_connect\", \"\");\n        variables.put(\"@@interactive_timeout\", \"172800\");\n        variables.put(\"@@license\", \"GPL\");\n        variables.put(\"@@lower_case_table_names\", \"1\");\n        variables.put(\"@@max_allowed_packet\", \"16777216\");\n        variables.put(\"@@net_buffer_length\", \"16384\");\n        variables.put(\"@@net_write_timeout\", \"60\");\n        variables.put(\"@@query_cache_size\", \"0\");\n        variables.put(\"@@query_cache_type\", \"OFF\");\n        variables.put(\"@@sql_mode\", \"STRICT_TRANS_TABLES\");\n        variables.put(\"@@system_time_zone\", \"CST\");\n        variables.put(\"@@time_zone\", \"SYSTEM\");\n        variables.put(\"@@tx_isolation\", \"REPEATABLE-READ\");\n        variables.put(\"@@wait_timeout\", \"172800\");\n        variables.put(\"@@session.auto_increment_increment\", \"1\");\n\n        variables.put(\"character_set_client\", \"utf8\");\n        variables.put(\"character_set_connection\", \"utf8\");\n        variables.put(\"character_set_results\", \"utf8\");\n        variables.put(\"character_set_server\", \"utf8\");\n        variables.put(\"init_connect\", \"\");\n        variables.put(\"interactive_timeout\", \"172800\");\n        variables.put(\"license\", \"GPL\");\n        variables.put(\"lower_case_table_names\", \"1\");\n        variables.put(\"max_allowed_packet\", \"16777216\");\n        variables.put(\"net_buffer_length\", \"16384\");\n        variables.put(\"net_write_timeout\", \"60\");\n        variables.put(\"query_cache_size\", \"0\");\n        variables.put(\"query_cache_type\", \"OFF\");\n        variables.put(\"sql_mode\", \"STRICT_TRANS_TABLES\");\n        variables.put(\"system_time_zone\", \"CST\");\n        variables.put(\"time_zone\", \"SYSTEM\");\n        variables.put(\"tx_isolation\", \"REPEATABLE-READ\");\n        variables.put(\"wait_timeout\", \"172800\");\n        variables.put(\"auto_increment_increment\", \"1\");\n    }\n    \n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/SelectVersion.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.Versions;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\n\n/**\n * @author mycat\n */\npublic class SelectVersion {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"VERSION()\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c) {\n        ByteBuffer buffer = c.allocate();\n        buffer = header.write(buffer, c,true);\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n        buffer = eof.write(buffer, c,true);\n        byte packetId = eof.packetId;\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(Versions.SERVER_VERSION);\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/SelectVersionComment.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\n\n/**\n * @author mycat\n */\npublic class SelectVersionComment {\n\n    private static final byte[] VERSION_COMMENT = \"MyCat Server (OpenCloudDB)\".getBytes();\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"@@VERSION_COMMENT\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(VERSION_COMMENT);\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/SessionIncrement.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.LongUtil;\n\n/**\n * @author mycat\n */\npublic class SessionIncrement {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"@@session.auto_increment_increment\", Fields.FIELD_TYPE_LONG);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c) {\n        ByteBuffer buffer = c.allocate();\n        buffer = header.write(buffer, c,true);\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n        buffer = eof.write(buffer, c,true);\n        byte packetId = eof.packetId;\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(LongUtil.toBytes(1));\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/SessionIsolation.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class SessionIsolation {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"@@session.tx_isolation\", Fields.FIELD_TYPE_STRING);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c) {\n        ByteBuffer buffer = c.allocate();\n        buffer = header.write(buffer, c,true);\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n        buffer = eof.write(buffer, c,true);\n        byte packetId = eof.packetId;\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(StringUtil.encode(\"REPEATABLE-READ\",c.getCharset()));\n        row.packetId = ++packetId;\n        buffer = row.write(buffer, c,true);\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/ShowCobarCluster.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Alarms;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatCluster;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.MycatNode;\nimport io.mycat.config.model.MycatNodeConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.IntegerUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class ShowCobarCluster {\n\n    private static final Logger alarm = LoggerFactory.getLogger(\"alarm\");\n\n    private static final int FIELD_COUNT = 2;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"HOST\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"WEIGHT\", Fields.FIELD_TYPE_LONG);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write field\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        for (RowDataPacket row : getRows(c)) {\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n        }\n\n        // last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n    }\n\n    private static List<RowDataPacket> getRows(ServerConnection c) {\n        List<RowDataPacket> rows = new LinkedList<RowDataPacket>();\n        MycatConfig config = MycatServer.getInstance().getConfig();\n        MycatCluster cluster = config.getCluster();\n        Map<String, SchemaConfig> schemas = config.getSchemas();\n        SchemaConfig schema = (c.getSchema() == null) ? null : schemas.get(c.getSchema());\n\n        // 如果没有指定schema或者schema为null，则使用全部集群。\n        if (schema == null) {\n            Map<String, MycatNode> nodes = cluster.getNodes();\n            for (MycatNode n : nodes.values()) {\n                if (n != null && n.isOnline()) {\n                    rows.add(getRow(n, c.getCharset()));\n                }\n            }\n        } else {\n\n        \t Map<String, MycatNode> nodes = cluster.getNodes();\n             for (MycatNode n : nodes.values()) {\n                 if (n != null && n.isOnline()) {\n                     rows.add(getRow(n, c.getCharset()));\n                 }\n             }\n        }\n\n        if (rows.size() == 0) {\n            alarm.error(Alarms.CLUSTER_EMPTY + c.toString());\n        }\n\n        return rows;\n    }\n\n    private static RowDataPacket getRow(MycatNode node, String charset) {\n        MycatNodeConfig conf = node.getConfig();\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(StringUtil.encode(conf.getHost(), charset));\n        row.add(IntegerUtil.toBytes(conf.getWeight()));\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/ShowCobarStatus.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\n\n/**\n * 加入了offline状态推送，用于心跳语句。\n * \n * @author mycat\n * @author mycat\n */\npublic class ShowCobarStatus {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    private static final RowDataPacket status = new RowDataPacket(FIELD_COUNT);\n    private static final EOFPacket lastEof = new EOFPacket();\n    private static final ErrorPacket error = PacketUtil.getShutdown();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"STATUS\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n        status.add(\"ON\".getBytes());\n        status.packetId = ++packetId;\n        lastEof.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c) {\n        if (MycatServer.getInstance().isOnline()) {\n            ByteBuffer buffer = c.allocate();\n            buffer = header.write(buffer, c,true);\n            for (FieldPacket field : fields) {\n                buffer = field.write(buffer, c,true);\n            }\n            buffer = eof.write(buffer, c,true);\n            buffer = status.write(buffer, c,true);\n            buffer = lastEof.write(buffer, c,true);\n            c.write(buffer);\n        } else {\n            error.write(c);\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/ShowDatabases.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeSet;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.UserConfig;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class ShowDatabases {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"DATABASE\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        MycatConfig conf = MycatServer.getInstance().getConfig();\n        Map<String, UserConfig> users = conf.getUsers();\n        UserConfig user = users == null ? null : users.get(c.getUser());\n        if (user != null) {\n            TreeSet<String> schemaSet = new TreeSet<String>();\n            Set<String> schemaList = user.getSchemas();\n            if (schemaList == null || schemaList.size() == 0) {\n                schemaSet.addAll(conf.getSchemas().keySet());\n            } else {\n                for (String schema : schemaList) {\n                    schemaSet.add(schema);\n                }\n            }\n            for (String name : schemaSet) {\n                RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n                row.add(StringUtil.encode(name, c.getCharset()));\n                row.packetId = ++packetId;\n                buffer = row.write(buffer, c,true);\n            }\n        }\n\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/ShowFullTables.java",
    "content": "package io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport com.google.common.base.Strings;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.UserConfig;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.util.SchemaUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * show tables impl\n * @author yanglixue\n *\n */\npublic class ShowFullTables {\n\n    private static final int FIELD_COUNT = 2;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n\n    private static final String SCHEMA_KEY = \"schemaName\";\n    //private static final String LIKE_KEY = \"like\";\n    //private static final   Pattern pattern = Pattern.compile(\"^\\\\s*(SHOW)\\\\s++(FULL)*\\\\s*(TABLES)(\\\\s+(FROM)\\\\s+([a-zA-Z_0-9]+))?(\\\\s+(LIKE\\\\s+'(.*)'))?\\\\s*\",Pattern.CASE_INSENSITIVE);\n\n\t/**\n\t * response method.\n\t * @param c\n\t */\n\tpublic static void response(ServerConnection c,String stmt,int type) {\n        String showSchema = SchemaUtil.parseShowTableSchema(stmt);\n        if (showSchema == null) {\n            showSchema = c.getSchema();\n        }\n\n        SchemaConfig schema = MycatServer.getInstance().getConfig().getSchemas().get(showSchema);\n        if(schema != null) {\n        \t//不分库的schema，show tables从后端 mysql中查\n            String node = schema.getDataNode();\n            if(!Strings.isNullOrEmpty(node)) {\n            \tc.execute(stmt, ServerParse.SHOW);\n                return;\n\t\t\t} else {\n\t\t\t\treponseLogicTable(c, stmt);\n\t\t\t\treturn;\n            }\n        } else {\n            c.writeErrMessage(ErrorCode.ER_NO_DB_ERROR, \"Cannot find the corresponding logic schema of Mycat\");\n            return;\n        }\n\t}\n\n\tprivate static void reponseLogicTable(ServerConnection c, String stmt) {\n\t\t// 分库的schema，直接从SchemaConfig中获取所有表名\n        Map<String,String> parm = buildFields(c,stmt);\n        Set<String> tableSet = getTableSet(c, parm);\n\n\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"Tables in \" + parm.get(SCHEMA_KEY), Fields.FIELD_TYPE_VAR_STRING);\n        fields[i].packetId = ++packetId;\n        fields[i+1] = PacketUtil.getField(\"Table_type  \" , Fields.FIELD_TYPE_VAR_STRING);\n        fields[i+1].packetId = ++packetId;\n        eof.packetId = ++packetId;\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n         packetId = eof.packetId;\n\n        for (String name : tableSet) {\n            RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n            row.add(StringUtil.encode(name, c.getCharset()));\n            row.add(StringUtil.encode(\"BASE TABLE\", c.getCharset()));\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n        }\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n\t}\n\n\tpublic static Set<String> getTableSet(ServerConnection c, String stmt) {\n\t\tMap<String, String> parm = buildFields(c, stmt);\n\t\treturn getTableSet(c, parm);\n\n\t}\n\n\tprivate static Set<String> getTableSet(ServerConnection c, Map<String, String> parm) {\n        TreeSet<String> tableSet = new TreeSet<String>();\n        MycatConfig conf = MycatServer.getInstance().getConfig();\n\n        Map<String, UserConfig> users = conf.getUsers();\n        UserConfig user = users == null ? null : users.get(c.getUser());\n        if (user != null) {\n\n\n            Map<String, SchemaConfig> schemas = conf.getSchemas();\n            for (String name:schemas.keySet()){\n                if (null !=parm.get(SCHEMA_KEY) && parm.get(SCHEMA_KEY).toUpperCase().equals(name.toUpperCase())  ){\n\n                    if(null==parm.get(\"LIKE_KEY\")){\n                        tableSet.addAll(schemas.get(name).getTables().keySet());\n                    }else{\n                        String p = \"^\" + parm.get(\"LIKE_KEY\").replaceAll(\"%\", \".*\");\n                        Pattern pattern = Pattern.compile(p,Pattern.CASE_INSENSITIVE);\n                        Matcher ma ;\n\n                        for (String tname : schemas.get(name).getTables().keySet()){\n                            ma=pattern.matcher(tname);\n                            if(ma.matches()){\n                                tableSet.add(tname);\n                            }\n                        }\n\n                    }\n\n                }\n            };\n\n\n\n        }\n        return tableSet;\n    }\n\n    /**\n\t * build fields\n\t * @param c\n\t * @param stmt\n\t */\n\tprivate static Map<String,String> buildFields(ServerConnection c,String stmt) {\n\t\tMap<String,String> map = new HashMap<String, String>();\n        String fields [] =SchemaUtil.parseShowTable(stmt);\n        if (null !=fields[3] && (!\"\".equals(fields[3])) && (!\"null\".equals(fields[3]))){\n            map.put(SCHEMA_KEY, fields[3]);\n        }\n        if ((fields[5] ==null || !\"table_type\".equals(fields[5])) && null !=fields[8] && (!\"\".equals(fields[8])) && (!\"null\".equals(fields[8]))){\n            map.put(\"LIKE_KEY\", fields[8]);\n        }\n\t\tif(null==map.get(SCHEMA_KEY)){\n\t\t\tmap.put(SCHEMA_KEY, c.getSchema());\n\t\t}\n        return  map;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/response/ShowMyCATCluster.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Alarms;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatCluster;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.MycatNode;\nimport io.mycat.config.model.MycatNodeConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.IntegerUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class ShowMyCATCluster {\n\n    private static final Logger alarm = LoggerFactory.getLogger(\"alarm\");\n\n    private static final int FIELD_COUNT = 2;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"HOST\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"WEIGHT\", Fields.FIELD_TYPE_LONG);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c) {\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write field\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n        byte packetId = eof.packetId;\n        for (RowDataPacket row : getRows(c)) {\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n        }\n\n        // last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n    }\n\n    private static List<RowDataPacket> getRows(ServerConnection c) {\n        List<RowDataPacket> rows = new LinkedList<RowDataPacket>();\n        MycatConfig config = MycatServer.getInstance().getConfig();\n        MycatCluster cluster = config.getCluster();\n        Map<String, SchemaConfig> schemas = config.getSchemas();\n        SchemaConfig schema = (c.getSchema() == null) ? null : schemas.get(c.getSchema());\n\n        // 如果没有指定schema或者schema为null，则使用全部集群。\n        if (schema == null) {\n            Map<String, MycatNode> nodes = cluster.getNodes();\n            for (MycatNode n : nodes.values()) {\n                if (n != null && n.isOnline()) {\n                    rows.add(getRow(n, c.getCharset()));\n                }\n            }\n        } else {\n\n        \t Map<String, MycatNode> nodes = cluster.getNodes();\n             for (MycatNode n : nodes.values()) {\n                 if (n != null && n.isOnline()) {\n                     rows.add(getRow(n, c.getCharset()));\n                 }\n             }\n        }\n\n        if (rows.size() == 0) {\n            alarm.error(Alarms.CLUSTER_EMPTY + c.toString());\n        }\n\n        return rows;\n    }\n\n    private static RowDataPacket getRow(MycatNode node, String charset) {\n        MycatNodeConfig conf = node.getConfig();\n        RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n        row.add(StringUtil.encode(conf.getHost(), charset));\n        row.add(IntegerUtil.toBytes(conf.getWeight()));\n        return row;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/ShowMyCatStatus.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.Fields;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\n\n/**\n * 加入了offline状态推送，用于心跳语句。\n * \n * @author mycat\n * @author mycat\n */\npublic class ShowMyCatStatus {\n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    private static final RowDataPacket status = new RowDataPacket(FIELD_COUNT);\n    private static final EOFPacket lastEof = new EOFPacket();\n    private static final ErrorPacket error = PacketUtil.getShutdown();\n    static {\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"STATUS\", Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n        status.add(\"ON\".getBytes());\n        status.packetId = ++packetId;\n        lastEof.packetId = ++packetId;\n    }\n\n    public static void response(ServerConnection c) {\n        if (MycatServer.getInstance().isOnline()) {\n            ByteBuffer buffer = c.allocate();\n            buffer = header.write(buffer, c,true);\n            for (FieldPacket field : fields) {\n                buffer = field.write(buffer, c,true);\n            }\n            buffer = eof.write(buffer, c,true);\n            buffer = status.write(buffer, c,true);\n            buffer = lastEof.write(buffer, c,true);\n            c.write(buffer);\n        } else {\n            error.write(c);\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/server/response/ShowTables.java",
    "content": "package io.mycat.server.response;\n\nimport java.nio.ByteBuffer;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport com.google.common.base.Strings;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.PacketUtil;\nimport io.mycat.config.ErrorCode;\nimport io.mycat.config.Fields;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.UserConfig;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.util.SchemaUtil;\nimport io.mycat.util.StringUtil;\n\n/**\n * show tables impl\n * @author yanglixue\n *\n */\npublic class ShowTables { \n\n    private static final int FIELD_COUNT = 1;\n    private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);\n    private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];\n    private static final EOFPacket eof = new EOFPacket();\n    \n    private static final String SCHEMA_KEY = \"schemaName\";\n    private static final String LIKE_KEY = \"like\";\n    private static final   Pattern pattern = Pattern.compile(\"^\\\\s*(SHOW)\\\\s+(TABLES)(\\\\s+(FROM)\\\\s+([a-zA-Z_0-9]+))?(\\\\s+(LIKE\\\\s+'(.*)'))?\\\\s*\",Pattern.CASE_INSENSITIVE);\n\t\n\t/**\n\t * response method.\n\t * @param c\n\t */\n\tpublic static void response(ServerConnection c,String stmt,int type) {\n        String showSchemal= SchemaUtil.parseShowTableSchema(stmt) ;\n        String cSchema =showSchemal==null? c.getSchema():showSchemal;\n        SchemaConfig schema = MycatServer.getInstance().getConfig().getSchemas().get(cSchema);\n        if(schema != null) {\n        \t//不分库的schema，show tables从后端 mysql中查\n            String node = schema.getDataNode();\n            if(!Strings.isNullOrEmpty(node)) {\n            \tc.execute(stmt, ServerParse.SHOW);\n                return;\n\t\t\t} else {\n\t\t\t\treponseLogicTable(c, stmt);\n\t\t\t\treturn;\n            }\n        } else {\n             c.writeErrMessage(ErrorCode.ER_NO_DB_ERROR,\"No database selected\");\n             return;\n        }\n\n\t}\n\n\tprivate static void reponseLogicTable(ServerConnection c, String stmt) {\n\t\t// 分库的schema，直接从SchemaConfig中获取所有表名\n        Map<String,String> parm = buildFields(c,stmt);\n        java.util.Set<String> tableSet = getTableSet(c, parm);\n\n\n        int i = 0;\n        byte packetId = 0;\n        header.packetId = ++packetId;\n        fields[i] = PacketUtil.getField(\"Tables in \" + parm.get(SCHEMA_KEY), Fields.FIELD_TYPE_VAR_STRING);\n        fields[i++].packetId = ++packetId;\n        eof.packetId = ++packetId;\n        ByteBuffer buffer = c.allocate();\n\n        // write header\n        buffer = header.write(buffer, c,true);\n\n        // write fields\n        for (FieldPacket field : fields) {\n            buffer = field.write(buffer, c,true);\n        }\n\n        // write eof\n        buffer = eof.write(buffer, c,true);\n\n        // write rows\n         packetId = eof.packetId;\n\n        for (String name : tableSet) {\n            RowDataPacket row = new RowDataPacket(FIELD_COUNT);\n            row.add(StringUtil.encode(name.toLowerCase(), c.getCharset()));\n            row.packetId = ++packetId;\n            buffer = row.write(buffer, c,true);\n        }\n        // write last eof\n        EOFPacket lastEof = new EOFPacket();\n        lastEof.packetId = ++packetId;\n        buffer = lastEof.write(buffer, c,true);\n\n        // post write\n        c.write(buffer);\n\t}\n\n\tpublic static Set<String> getTableSet(ServerConnection c, String stmt) {\n\t\tMap<String, String> parm = buildFields(c, stmt);\n\t\treturn getTableSet(c, parm);\n\n\t}\n\n\tprivate static Set<String> getTableSet(ServerConnection c, Map<String, String> parm) {\n        TreeSet<String> tableSet = new TreeSet<String>();\n        MycatConfig conf = MycatServer.getInstance().getConfig();\n\n        Map<String, UserConfig> users = conf.getUsers();\n        UserConfig user = users == null ? null : users.get(c.getUser());\n        if (user != null) {\n\n\n            Map<String, SchemaConfig> schemas = conf.getSchemas();\n            for (String name:schemas.keySet()){\n                if (null !=parm.get(SCHEMA_KEY) && parm.get(SCHEMA_KEY).toUpperCase().equals(name.toUpperCase())  ){\n\n                    if(null==parm.get(\"LIKE_KEY\")){\n                        tableSet.addAll(schemas.get(name).getTables().keySet());\n                    }else{\n                        String p = \"^\" + parm.get(\"LIKE_KEY\").replaceAll(\"%\", \".*\");\n                        Pattern pattern = Pattern.compile(p,Pattern.CASE_INSENSITIVE);\n                        Matcher ma ;\n\n                        for (String tname : schemas.get(name).getTables().keySet()){\n                            ma=pattern.matcher(tname);\n                            if(ma.matches()){\n                                tableSet.add(tname);\n                            }\n                        }\n\n                    }\n\n                }\n            };\n\n\n\n        }\n        return tableSet;\n    }\n\n    /**\n\t * build fields\n\t * @param c\n\t * @param stmt\n\t */\n\tprivate static Map<String,String> buildFields(ServerConnection c,String stmt) {\n\t \n\t\tMap<String,String> map = new HashMap<String, String>();\n\n\t\tMatcher ma = pattern.matcher(stmt);\n\n\t\tif(ma.find()){\n\t\t\t  String schemaName=ma.group(5);\n\t\t\t  if (null !=schemaName && (!\"\".equals(schemaName)) && (!\"null\".equals(schemaName))){\n\t\t\t\t  map.put(SCHEMA_KEY, schemaName);\n\t\t\t  }\n\t\t\t  \n\t\t\t String like = ma.group(8);\n\t\t\t if (null !=like && (!\"\".equals(like)) && (!\"null\".equals(like))){\n\t\t\t\t  map.put(\"LIKE_KEY\", like);\n\t\t\t  }\n\t\t\t}\n\n\n\t\tif(null==map.get(SCHEMA_KEY)){\n\t\t\tmap.put(SCHEMA_KEY, c.getSchema());\n\t\t}\n\t\t \n\t\t\n\n         \n        return  map;\n        \n\t}\n\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/sqlcmd/CommitCommand.java",
    "content": "package io.mycat.server.sqlcmd;\n\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.server.NonBlockingSession;\n\npublic class CommitCommand implements SQLCtrlCommand {\n\n\t@Override\n\tpublic void sendCommand(NonBlockingSession session, BackendConnection con) {\n\t\tcon.commit();\n\t}\n\n\t@Override\n\tpublic void errorResponse(NonBlockingSession session, byte[] err,\n\t\t\tint total, int failed) {\n\t\tErrorPacket errPkg = new ErrorPacket();\n\t\terrPkg.read(err);\n\t\tString errInfo = \"total \" + total + \" failed \" + failed + \" detail:\"\n\t\t\t\t+ new String(errPkg.message);\n\t\tsession.getSource().setTxInterrupt(errInfo);\n\t\terrPkg.write(session.getSource());\n\t}\n\n\t@Override\n\tpublic void okResponse(NonBlockingSession session, byte[] ok) {\n\t\tsession.getSource().write(ok);\n\t}\n\n\t@Override\n\tpublic boolean releaseConOnErr() {\n\t\t// need rollback when err\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean relaseConOnOK() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isAutoClearSessionCons() {\n\t\t// need rollback when err\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/sqlcmd/SQLCmdConstant.java",
    "content": "package io.mycat.server.sqlcmd;\n\npublic class SQLCmdConstant {\n    public static final CommitCommand COMMIT_CMD = new CommitCommand();\n    \n    private SQLCmdConstant() {\n    }\n    \n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/sqlcmd/SQLCtrlCommand.java",
    "content": "package io.mycat.server.sqlcmd;\n\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.server.NonBlockingSession;\n\n/**\n * sql command like set xxxx ,only return OK /Err Pacakage,can't return restult\n * set\n * \n * @author wuzhih\n * \n */\npublic interface SQLCtrlCommand {\n\n\tboolean isAutoClearSessionCons();\n\tboolean releaseConOnErr();\n\t\n\tboolean relaseConOnOK();\n\t\n\tvoid sendCommand(NonBlockingSession session, BackendConnection con);\n\n\t/**\n\t * 收到错误数据包的响应处理\n\t */\n\tvoid errorResponse(NonBlockingSession session,byte[] err,int total,int failed);\n\n\t/**\n\t * 收到OK数据包的响应处理\n\t */\n\tvoid okResponse(NonBlockingSession session, byte[] ok);\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/server/util/SchemaUtil.java",
    "content": "package io.mycat.server.util;\n\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLShowColumnsStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\nimport io.mycat.route.parser.druid.MycatStatementParser;\nimport io.mycat.server.parser.ServerParse;\n\n/**\n * Created by magicdoom on 2016/1/26.\n */\npublic class SchemaUtil {\n    public static SchemaInfo parseSchema(String sql) {\n        try{\n            MycatStatementParser parser = new MycatStatementParser(sql);\n            return parseTables(parser.parseStatement(), new MycatSchemaStatVisitor());\n        }catch (Exception ignore){\n            return null;\n        }\n    }\n\n    public static String detectDefaultDb(String sql, int type) {\n        String db = null;\n        Map<String, SchemaConfig> schemaConfigMap = MycatServer.getInstance().getConfig()\n                .getSchemas();\n        if (ServerParse.SELECT == type) {\n            SchemaUtil.SchemaInfo schemaInfo = SchemaUtil.parseSchema(sql);\n            if ((schemaInfo == null || schemaInfo.table == null) && !schemaConfigMap.isEmpty()) {\n                db = schemaConfigMap.entrySet().iterator().next().getKey();\n            }\n\n            if (schemaInfo != null && schemaInfo.schema != null) {\n\n                if (schemaConfigMap.containsKey(schemaInfo.schema)) {\n                    db = schemaInfo.schema;\n\n                    /**\n                     * 对 MySQL 自带的元数据库 information_schema 进行返回\n                     */\n                } else if (\"information_schema\".equalsIgnoreCase(schemaInfo.schema)) {\n                    db = \"information_schema\";\n                }\n            }\n        } else if (ServerParse.INSERT == type || ServerParse.UPDATE == type || ServerParse.DELETE == type || ServerParse.DDL == type) {\n            SchemaUtil.SchemaInfo schemaInfo = SchemaUtil.parseSchema(sql);\n            if (schemaInfo != null && schemaInfo.schema != null && schemaConfigMap.containsKey(schemaInfo.schema)) {\n                db = schemaInfo.schema;\n            }\n        } else if(ServerParse.COMMAND == type) {\n            int index = sql.indexOf(ServerParse.COM_FIELD_LIST_FLAG);\n            String table = sql.substring(index + 17);\n            db = getSchema(schemaConfigMap, table);\n        } else if ((ServerParse.SHOW == type || ServerParse.USE == type || ServerParse.EXPLAIN == type || ServerParse.SET == type\n                || ServerParse.HELP == type || ServerParse.DESCRIBE == type)\n                && !schemaConfigMap.isEmpty()) {\n            try {\n                //当前存在多个schema的时候使用原来的逻辑会出现bug，改为根据mycat schema配置中的对应关系获取\n                SQLStatementParser parser = new MySqlStatementParser(sql);\n\t\t\t\tSQLShowColumnsStatement stmt = (SQLShowColumnsStatement) parser.parseStatement();\n                db = getSchema(schemaConfigMap, stmt.getTable().getSimpleName());\n            } catch (Exception e) {\n                //e.printStackTrace();\n                //兼容mysql gui  不填默认database\n                db = schemaConfigMap.entrySet().iterator().next().getKey();\n            }\n        }\n        return db;\n    }\n    /** 根据mycat schema配置中的对应关系获取对应的db */\n    public static String getSchema(Map<String, SchemaConfig> schemaConfigMap, String table) {\n        String db = null;\n        for (Map.Entry<String,SchemaConfig> entry : schemaConfigMap.entrySet()) {\n            if(entry.getValue().getTables().containsKey(table.toUpperCase())) {\n                db = entry.getKey();\n                break;\n            }\n        }\n        return db;\n    }\n    public static String parseShowTableSchema(String sql) {\n        // Matcher ma = SHOW_FULL_TABLE_PATTERN.matcher(sql);\n        // if (ma.matches() && ma.groupCount() >= 5) {\n        //     return ma.group(5);\n        // }\n        String fields[] =parseShowTable(sql);\n\t\treturn fields[3];\n    }\n    /**\n     * 解析show full table from|in schema-name where table_type|tables_in_xxx like 'xxx'\n     * \n     * @param sql\n     * @return fields' array. \n     *          <pre>\n     *          [0] is matched, 1 is matcher , other is not matcher\n     *          [1] is full, 代表当前是show full tabe ，如果为空则表示show table\n     *          [2] from or in\n     *          [3] schema-name\n     *          [4] where\n     *          [5] is 'table_type' or 'tables_in_xxxx'\n     *          [6] is 'like' or '=' or ... other operator\n     *          [7] \n     *          [8] is where match condition str, etc. table-name or table_type like 'BASE TABLE'\n     *          </pre>\n     */\n    public static String[] parseShowTable(String sql) {\n        Matcher matcher = SHOW_FULL_TABLE_PATTERN.matcher(sql);\n        // matcher.matches();\n        String[] fields = new String[9];\n        if (matcher.find()) {// show tables\n            fields[0] = \"1\";\n            // group(1) is 'full'\n            fields[1] = matcher.group(1);\n            if(fields[1] != null) {\n                fields[1] = fields[1].trim();\n            }\n            // group(2) is 'from or in'\n            fields[2] = matcher.group(2);\n            if (fields[2] == null || fields[2].length() <= 0) {\n                fields[3] = null;\n                if(matcher.group(3) !=null && \"LIKE\".equals(matcher.group(3))) {\n                    fields[4] = null;\n                } else {\n                    fields[4] = \"WHERE\";\n                }\n            } else {\n                fields[2] = fields[2].trim();\n                // group(3) is schema-name\n                fields[3] = matcher.group(3);\n                //group(4) is 'where'\n                fields[4] = matcher.group(4);\n                if(fields[4] != null) {\n                    fields[4] = fields[4].trim();\n                }\n            }\n            //group(5) is 'table_type' or 'tables_in_xxxx'\n            fields[5] = matcher.group(5);\n            //group(6) is 'like' or '=' or ... other operator\n            if(fields[4] == null) {\n                fields[6] = \"LIKE\";\n            } else {\n                fields[6] = matcher.group(6);\n                if(fields[6] !=null) {\n                    fields[6] = fields[6].trim();\n                }\n            }\n            //skip\n            fields[7] = matcher.group(7);\n            //\n            //group(8) is where match condition str, etc. table-name or table_type like 'BASE TABLE'\n            fields[8] = matcher.group(8);\n        }\n        return fields;\n    }\n\n\tprivate static SchemaInfo parseTables(SQLStatement stmt, MycatSchemaStatVisitor schemaStatVisitor) {\n\n        stmt.accept(schemaStatVisitor);\n\t\tString key = schemaStatVisitor.getCurrentTable();\n        if (key != null && key.contains(\"`\")) {\n            key = key.replaceAll(\"`\", \"\");\n        }\n\n        if (key != null) {\n            SchemaInfo schemaInfo = new SchemaInfo();\n            int pos = key.indexOf(\".\");\n            if (pos > 0) {\n                schemaInfo.schema = key.substring(0, pos);\n                schemaInfo.table = key.substring(pos + 1);\n            } else {\n                schemaInfo.table = key;\n            }\n            return schemaInfo;\n        }\n\n        return null;\n    }\n\n\n    public static class SchemaInfo {\n        public String table;\n        public String schema;\n\n        @Override\n        public String toString() {\n            final StringBuffer sb = new StringBuffer(\"SchemaInfo{\");\n            sb.append(\"table='\").append(table).append('\\'');\n            sb.append(\", schema='\").append(schema).append('\\'');\n            sb.append('}');\n            return sb.toString();\n        }\n    }\n    //sample：SHOW FULL TABLES FROM information_schema WHERE Tables_in_information_schema LIKE 'KEY_COLUMN_USAGE'\n    //注意sql中like后面会有单引号\n\tprivate static Pattern SHOW_FULL_TABLE_PATTERN = Pattern.compile(\n\t\t\t\"^\\\\s*show\\\\s+(full\\\\s+)?tables \\\\s*(in |from )?\\\\s*(\\\\w+)*\\\\s*(where )?\\\\s*(table_type|tables_in_\\\\w+)*\\\\s*(\\\\= |like )?\\\\s*('([\\\\w%\\\\s]+)')*\\\\s*(;)*\\\\s*\",\n\t\t\tPattern.CASE_INSENSITIVE);\n\n    public static void main(String[] args) {\n        String sql = \"SELECT name, type FROM `mysql`.`proc` as xxxx WHERE Db='base'\";\n        //   System.out.println(parseSchema(sql));\n        sql = \"insert into aaa.test(id) values(1)\";\n        // System.out.println(parseSchema(sql));\n        sql = \"update updatebase.test set xx=1 \";\n        //System.out.println(parseSchema(sql));\n        sql = \"CREATE TABLE IF not EXISTS  `test` (\\n\" + \"  `id` bigint(20) NOT NULL AUTO_INCREMENT,\\n\"\n                + \"  `sid` bigint(20) DEFAULT NULL,\\n\" + \"  `name` varchar(45) DEFAULT NULL,\\n\"\n                + \"  `value` varchar(45) DEFAULT NULL,\\n\"\n                + \"  `_slot` int(11) DEFAULT NULL COMMENT '自动迁移算法slot,禁止修改',\\n\" + \"  PRIMARY KEY (`id`)\\n\"\n                + \") ENGINE=InnoDB AUTO_INCREMENT=805781256930734081 DEFAULT CHARSET=utf8\";\n        System.out.println(parseSchema(sql));\n        String pat3 = \"SHOW FULL TABLES FROM information_schema WHERE Tables_in_information_schema LIKE 'KEY_COLUMN_USAGE'\";\n        Matcher ma = SHOW_FULL_TABLE_PATTERN.matcher(pat3);\n        if (ma.matches()) {\n            System.out.println(ma.groupCount());\n            System.out.println(ma.group(5));\n        }\n\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/AllJobFinishedListener.java",
    "content": "package io.mycat.sqlengine;\n/**\n * called when all jobs in EngineCxt finished\n * @author wuzhih\n *\n */\npublic interface AllJobFinishedListener {\n\n\tvoid onAllJobFinished(EngineCtx ctx);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/BatchSQLJob.java",
    "content": "package io.mycat.sqlengine;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentLinkedQueue;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.model.SystemConfig;\n\npublic class BatchSQLJob {\n\n\tprivate ConcurrentHashMap<Integer, SQLJob> runningJobs = new ConcurrentHashMap<Integer, SQLJob>();\n\tprivate ConcurrentLinkedQueue<SQLJob> waitingJobs = new ConcurrentLinkedQueue<SQLJob>();\n\tprivate volatile boolean noMoreJobInput = false;\n\t/*\n\t * \n\t * parallExecute: 是否可以并行执行\n\t * */\n\tpublic void addJob(SQLJob newJob, boolean parallExecute) {\n        SystemConfig system = MycatServer.getInstance().getConfig().getSystem();\n        parallExecute = parallExecute && (system.getParallExecute() == 1);\n        if (parallExecute) {\n\t\t\trunJob(newJob);\n\t\t} else {\n\t\t\twaitingJobs.offer(newJob);\n\t\t\tif (runningJobs.isEmpty()) {\n\t\t\t\tSQLJob job = waitingJobs.poll();\n\t\t\t\tif (job != null) {\n\t\t\t\t\trunJob(job);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t//设置批量任务已经不会在添加任务了。\n\tpublic void setNoMoreJobInput(boolean noMoreJobInput) {\n\t\tthis.noMoreJobInput = noMoreJobInput;\n\t}\n\t//执行任务\n\tprivate void runJob(SQLJob newJob) {\n\t\t// EngineCtx.LOGGER.info(\"run job \" + newJob);\n\t\trunningJobs.put(newJob.getId(), newJob);\n\t\tMycatServer.getInstance().getBusinessExecutor().execute(newJob);\n\t}\n\t//单个的任务执行完毕。 等待任务列表中有任务，  继续执行下一个任务。\t\n\t//返回： 是否所有的任务执行完毕。\t\n\tpublic boolean jobFinished(SQLJob sqlJob) {\n\t\tif (EngineCtx.LOGGER.isDebugEnabled()) {\n\t\t\tEngineCtx.LOGGER.info(\"job finished \" + sqlJob);\n\t\t}\n\t\trunningJobs.remove(sqlJob.getId());\n\t\tSQLJob job = waitingJobs.poll();\n\t\tif (job != null) {\n\t\t\trunJob(job);\n\t\t\treturn false;\n\t\t} else {\n\t\t\tif (noMoreJobInput) {\n\t\t\t\treturn runningJobs.isEmpty() && waitingJobs.isEmpty();\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/EngineCtx.java",
    "content": "package io.mycat.sqlengine;\n\nimport java.nio.ByteBuffer;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.ResultSetHeaderPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.server.NonBlockingSession;\nimport io.mycat.server.ServerConnection;\n//\n//任务的进行的调用，\n//向mysqlClient 写数据。\npublic class EngineCtx {\n\tpublic static final Logger LOGGER = LoggerFactory.getLogger(EngineCtx.class);\n\tprivate final BatchSQLJob bachJob; \n\tprivate AtomicInteger jobId = new AtomicInteger(0);\n\tAtomicInteger packetId = new AtomicInteger(0);\n\tprivate final NonBlockingSession session;\n\tprivate AtomicBoolean finished = new AtomicBoolean(false);\n\tprivate AllJobFinishedListener allJobFinishedListener;\n\tprivate AtomicBoolean headerWrited = new AtomicBoolean();\n\tprivate final ReentrantLock writeLock = new ReentrantLock();\n\tprivate volatile boolean hasError = false;\n\t// private volatile RouteResultset rrs;\n\tprivate volatile boolean isStreamOutputResult = true; //是否流式输出。\n\tpublic EngineCtx(NonBlockingSession session) {\n\t\tthis.bachJob = new BatchSQLJob();\n\t\tthis.session = session;\n\t}\n\t\n\tpublic boolean getIsStreamOutputResult(){\n\t\treturn isStreamOutputResult;\n\t}\n\tpublic void setIsStreamOutputResult(boolean isStreamOutputResult){\n\t\tthis. isStreamOutputResult = isStreamOutputResult;\n\t}\n\tpublic byte incPackageId() {\n\t\treturn (byte) packetId.incrementAndGet();\n\t}\n\t/*\n\t * 将sql 发送到所有的dataNodes分片\n\t * 顺序执行\n\t * */\n\tpublic void executeNativeSQLSequnceJob(String[] dataNodes, String sql,\n\t\t\tSQLJobHandler jobHandler) {\n\t\tfor (String dataNode : dataNodes) {\n\t\t\tSQLJob job = new SQLJob(jobId.incrementAndGet(), sql, dataNode,\n\t\t\t\t\tjobHandler, this);\n\t\t\tbachJob.addJob(job, false);\n\n\t\t}\n\t}\n\n\tpublic ReentrantLock getWriteLock() {\n\t\treturn writeLock;\n\t}\n\t/*\n\t * 所有任务完成的回调\n\t * */\n\tpublic void setAllJobFinishedListener(\n\t\t\tAllJobFinishedListener allJobFinishedListener) {\n\t\tthis.allJobFinishedListener = allJobFinishedListener;\n\t}\n\t/*\n\t * 将sql 发送到所有的dataNodes分片\n\t * 可以并行执行\n\t * */\n\tpublic void executeNativeSQLParallJob(String[] dataNodes, String sql,\n\t\t\tSQLJobHandler jobHandler) {\n\t\tfor (String dataNode : dataNodes) {\n\t\t\tSQLJob job = new SQLJob(jobId.incrementAndGet(), sql, dataNode,\n\t\t\t\t\tjobHandler, this);\n\t\t\tbachJob.addJob(job, true);\n\n\t\t}\n\t}\n\n\t/**\n\t * set no more jobs created\n\t */\n\tpublic void endJobInput() {\n\t\tbachJob.setNoMoreJobInput(true);\n\t}\n\t//a 表和 b表的字段的信息合并在一块。\n\t//向mysql client 输出。\n\tpublic void writeHeader(List<byte[]> afields, List<byte[]> bfields) {\n\t\tif (headerWrited.compareAndSet(false, true)) {\n\t\t\ttry {\n\t\t\t\twriteLock.lock();\n\t\t\t\t// write new header\n\t\t\t\tResultSetHeaderPacket headerPkg = new ResultSetHeaderPacket();\n\t\t\t\theaderPkg.fieldCount = afields.size() +bfields.size()-1;\n\t\t\t\theaderPkg.packetId = incPackageId();\n\t\t\t\tLOGGER.debug(\"packge id \" + headerPkg.packetId);\n\t\t\t\tServerConnection sc = session.getSource();\n\t\t\t\tByteBuffer buf = headerPkg.write(sc.allocate(), sc, true);\n\t\t\t\t// wirte a fields\n\t\t\t\tfor (byte[] field : afields) {\n\t\t\t\t\tfield[3] = incPackageId();\n\t\t\t\t\tbuf = sc.writeToBuffer(field, buf);\n\t\t\t\t}\n\t\t\t\t// write b field\n\t\t\t\tfor (int i=1;i<bfields.size();i++) {\n\t\t\t\t  byte[] bfield = bfields.get(i);\n\t\t\t\t  bfield[3] = incPackageId();\n\t\t\t\t  buf = sc.writeToBuffer(bfield, buf);\n\t\t\t\t}\n\t\t\t\t// write field eof\n\t\t\t\tEOFPacket eofPckg = new EOFPacket();\n\t\t\t\teofPckg.packetId = incPackageId();\n\t\t\t\tbuf = eofPckg.write(buf, sc, true);\n\t\t\t\tsc.write(buf);\n\t\t\t\t//LOGGER.info(\"header outputed ,packgId:\" + eofPckg.packetId);\n\t\t\t} finally {\n\t\t\t\twriteLock.unlock();\n\t\t\t}\n\t\t}\n\n\t}\n\t\n\tpublic void writeHeader(List<byte[]> afields) {\n\t\tif (headerWrited.compareAndSet(false, true)) {\n\t\t\ttry {\n\t\t\t\twriteLock.lock();\n\t\t\t\t// write new header\n\t\t\t\tResultSetHeaderPacket headerPkg = new ResultSetHeaderPacket();\n\t\t\t\theaderPkg.fieldCount = afields.size();// -1;\n\t\t\t\theaderPkg.packetId = incPackageId();\n\t\t\t\tLOGGER.debug(\"packge id \" + headerPkg.packetId);\n\t\t\t\tServerConnection sc = session.getSource();\n\t\t\t\tByteBuffer buf = headerPkg.write(sc.allocate(), sc, true);\n\t\t\t\t// wirte a fields\n\t\t\t\tfor (byte[] field : afields) {\n\t\t\t\t\tfield[3] = incPackageId();\n\t\t\t\t\tbuf = sc.writeToBuffer(field, buf);\n\t\t\t\t}\n\n\t\t\t\t// write field eof\n\t\t\t\tEOFPacket eofPckg = new EOFPacket();\n\t\t\t\teofPckg.packetId = incPackageId();\n\t\t\t\tbuf = eofPckg.write(buf, sc, true);\n\t\t\t\tsc.write(buf);\n\t\t\t\t//LOGGER.info(\"header outputed ,packgId:\" + eofPckg.packetId);\n\t\t\t} finally {\n\t\t\t\twriteLock.unlock();\n\t\t\t}\n\t\t}\n\n\t}\n\t\n\tpublic void writeRow(RowDataPacket rowDataPkg) {\n\t\tServerConnection sc = session.getSource();\n\t\ttry {\n\t\t\twriteLock.lock();\n\t\t\trowDataPkg.packetId = incPackageId();\n\t\t\t// 输出完整的 记录到客户端\n\t\t\tByteBuffer buf = rowDataPkg.write(sc.allocate(), sc, true);\n\t\t\tsc.write(buf);\n\t\t\t//LOGGER.info(\"write  row ,packgId:\" + rowDataPkg.packetId);\n\t\t} finally {\n\t\t\twriteLock.unlock();\n\t\t}\n\t}\n\n\tpublic void writeEof() {\n\t\tServerConnection sc = session.getSource();\n\t\tEOFPacket eofPckg = new EOFPacket();\n\t\teofPckg.packetId = incPackageId();\n\t\tByteBuffer buf = eofPckg.write(sc.allocate(), sc, false);\n\t\tsc.write(buf);\n\t\tLOGGER.info(\"write  eof ,packgId:\" + eofPckg.packetId);\n\t}\n\t\n\n\tpublic NonBlockingSession getSession() {\n\t\treturn session;\n\t}\n\t//单个sqlJob任务完成之后调用的。\n\t//全部任务完成之后 回调allJobFinishedListener 这个函数。\n\tpublic void onJobFinished(SQLJob sqlJob) {\n\t\t\n\t\tboolean allFinished = bachJob.jobFinished(sqlJob);\n\t\tif (allFinished && finished.compareAndSet(false, true)) {\n\t\t\tif(!hasError){\n\t\t\t\tLOGGER.info(\"all job finished  for front connection: \"\n\t\t\t\t\t\t+ session.getSource());\n\t\t\t\tallJobFinishedListener.onAllJobFinished(this);\n\t\t\t}else{\n\t\t\t\tLOGGER.info(\"all job finished with error for front connection: \"\n\t\t\t\t\t\t+ session.getSource());\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic boolean isHasError() {\n\t\treturn hasError;\n\t}\n\n\tpublic void setHasError(boolean hasError) {\n\t\tthis.hasError = hasError;\n\t}\n\t\n\t// public void setRouteResultset(RouteResultset rrs){\n\t// \tthis.rrs = rrs;\n\t// }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/MultiRowSQLQueryResultHandler.java",
    "content": "package io.mycat.sqlengine;\n\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 当SQLJob的结果有多行时，利用该处理器进行处理\n * @author digdeep@126.com\n */\npublic class MultiRowSQLQueryResultHandler extends OneRawSQLQueryResultHandler{\n\tprivate static final Logger LOGGER = LoggerFactory\n\t\t\t\t.getLogger(MultiRowSQLQueryResultHandler.class);\n\t// 获得结果之后，利用该对象进行回调进行通知和处理结果\n\tprivate final SQLQueryResultListener<SQLQueryResult<List<Map<String, String>>>> callback;\n\t\n\tprivate List<Map<String, String>> resultRows = new LinkedList<>();\t// 保存结果行\n\t\n\tpublic MultiRowSQLQueryResultHandler(String[] fetchCols,\n\t\t\tSQLQueryResultListener<SQLQueryResult<List<Map<String, String>>>> callback) {\n\t\tsuper(fetchCols, null);\n\t\tthis.callback = callback;\n\t}\n\n\t@Override\n\tpublic boolean onRowData(String dataNode, byte[] rowData) {\n\t\tsuper.onRowData(dataNode, rowData);\n\t\tresultRows.add(getResult());\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void finished(String dataNode, boolean failed, String errorMsg) {\n\t\tSQLQueryResult<List<Map<String, String>>> queryResult = \n\t\t\t\tnew SQLQueryResult<List<Map<String, String>>>(this.resultRows, !failed);\n\t\tqueryResult.setErrMsg(errorMsg);\n\t\tif(callback != null)\n\t\t\tthis.callback.onResult(queryResult); // callback 是构造函数传进来，在得到结果是进行回调\n\t\telse\n\t\t\tLOGGER.warn(\" callback is null \");\n\t}\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/OneRawSQLQueryResultHandler.java",
    "content": "package io.mycat.sqlengine;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.RowDataPacket;\n\npublic class OneRawSQLQueryResultHandler implements SQLJobHandler {\n\n\tprivate Map<String, Integer> fetchColPosMap;\n\tprivate final SQLQueryResultListener<SQLQueryResult<Map<String, String>>> callback;\n\tprivate final String[] fetchCols;\n\tprivate int fieldCount = 0;\n\tprivate Map<String, String> result = new HashMap<String, String>();\n\tpublic OneRawSQLQueryResultHandler(String[] fetchCols,\n\t\t\tSQLQueryResultListener<SQLQueryResult<Map<String, String>>> callBack) {\n\n\t\tthis.fetchCols = fetchCols;\n\t\tthis.callback = callBack;\n\t}\n\n\tprivate String mark;\n\tpublic void onHeader(String dataNode, byte[] header, List<byte[]> fields) {\n\t\tfieldCount = fields.size();\n\t\tfetchColPosMap = new HashMap<String, Integer>();\n\t\tfor (String watchFd : fetchCols) {\n\t\t\tfor (int i = 0; i < fieldCount; i++) {\n\t\t\t\tbyte[] field = fields.get(i);\n\t\t\t\tFieldPacket fieldPkg = new FieldPacket();\n\t\t\t\tfieldPkg.read(field);\n\t\t\t\tString fieldName = new String(fieldPkg.name);\n\t\t\t\tif (watchFd.equalsIgnoreCase(fieldName)) {\n\t\t\t\t\tfetchColPosMap.put(fieldName, i);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic boolean onRowData(String dataNode, byte[] rowData) {\n\t\tRowDataPacket rowDataPkg = new RowDataPacket(fieldCount);\n\t\trowDataPkg.read(rowData);\n\t\tString variableName = \"\";\n\t\tString variableValue = \"\";\n\t\t//fieldcount为2可能是select x也可能是show create table命令\n\t\tif(fieldCount==2 && (fetchColPosMap.get(\"Variable_name\")!=null || fetchColPosMap.get(\"Value\")!=null)){\n\t\t\tInteger ind = fetchColPosMap.get(\"Variable_name\");\n\t\t\tif (ind != null) {\n\t\t\t\tbyte[] columnData = rowDataPkg.fieldValues.get(ind);\n                String columnVal = columnData!=null?new String(columnData):null;\n                variableName = columnVal;\n            }\n\t\t\tind = fetchColPosMap.get(\"Value\");\n\t\t\tif (ind != null) {\n\t\t\t\tbyte[] columnData = rowDataPkg.fieldValues.get(ind);\n                String columnVal = columnData!=null?new String(columnData):null;\n                variableValue = columnVal;\n            }\n            result.put(variableName, variableValue);\n\t\t}else{\n\t\t\tfor (String fetchCol : fetchCols) {\n\t\t\t\tInteger ind = fetchColPosMap.get(fetchCol);\n\t\t\t\tif (ind != null) {\n\t\t\t\t\tbyte[] columnData = rowDataPkg.fieldValues.get(ind);\n\t                String columnVal = columnData!=null?new String(columnData):null;\n\t                result.put(fetchCol, columnVal);\n\t\t\t\t} else {\n\t\t\t\t\tLOGGER.warn(\"cant't find column in sql query result \" + fetchCol);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void finished(String dataNode, boolean failed, String errorMsg) {\n\t\tSQLQueryResult<Map<String, String>> queryRestl=new SQLQueryResult<Map<String, String>>(this.result,!failed, dataNode,errorMsg);\n\t     this.callback.onResult(queryRestl);\n\n\t}\n\n\tpublic String getMark() {\n\t\treturn mark;\n\t}\n\n\tpublic void setMark(String mark) {\n\t\tthis.mark = mark;\n\t}\n\t\n\t// 子类 MultiRowSQLQueryResultHandler 需要使用\n\tprotected Map<String, String> getResult() {\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/SQLJob.java",
    "content": "package io.mycat.sqlengine;\n\nimport java.util.List;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.BackendConnection;\nimport io.mycat.backend.datasource.PhysicalDBNode;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.mysql.nio.handler.ResponseHandler;\nimport io.mycat.config.MycatConfig;\nimport io.mycat.net.mysql.ErrorPacket;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.server.parser.ServerParse;\n\n/**\n * asyn execute in EngineCtx or standalone (EngineCtx=null)\n * \n * @author wuzhih\n * \n */\npublic class SQLJob implements ResponseHandler, Runnable {\n\t\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(SQLJob.class);\n\t\n\tprivate final String sql;\n\tprivate final String dataNodeOrDatabase;\n\tprivate BackendConnection connection;\n\tprivate final SQLJobHandler jobHandler;\n\tprivate final EngineCtx ctx;\n\tprivate final PhysicalDatasource ds;\n\tprivate final int id;\n\tprivate volatile boolean finished;\n\n\tpublic SQLJob(int id, String sql, String dataNode,\n\t\t\tSQLJobHandler jobHandler, EngineCtx ctx) {\n\t\tsuper();\n\t\tthis.id = id;\n\t\tthis.sql = sql;\n\t\tthis.dataNodeOrDatabase = dataNode;\n\t\tthis.jobHandler = jobHandler;\n\t\tthis.ctx = ctx;\n\t\tthis.ds = null;\n\t}\n\n\tpublic SQLJob(String sql, String databaseName, SQLJobHandler jobHandler,\n\t\t\tPhysicalDatasource ds) {\n\t\tsuper();\n\t\tthis.id = 0;\n\t\tthis.sql = sql;\n\t\tthis.dataNodeOrDatabase = databaseName;\n\t\tthis.jobHandler = jobHandler;\n\t\tthis.ctx = null;\n\t\tthis.ds = ds;\n\n\t}\n\n\tpublic void run() {\n\t\ttry {\n\t\t\tif (ds == null) {\n\t\t\t\tRouteResultsetNode node = new RouteResultsetNode(\n\t\t\t\t\t\tdataNodeOrDatabase, ServerParse.SELECT, sql);\n\t\t\t\t// create new connection\n\t\t\t\tMycatConfig conf = MycatServer.getInstance().getConfig();\n\t\t\t\tPhysicalDBNode dn = conf.getDataNodes().get(node.getName());\n\t\t\t\tdn.getConnection(dn.getDatabase(), true, node, this, node);\n\t\t\t} else {\n\t\t\t\tds.getConnection(dataNodeOrDatabase, true, this, null);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.info(\"can't get connection for sql ,error:\" ,e);\n\t\t\tdoFinished(true,e.getMessage());\n\t\t}\n\t}\n\n\tpublic void teminate(String reason) {\n\t\tLOGGER.info(\"terminate this job reason:\" + reason + \" con:\"\n\t\t\t\t+ connection + \" sql \" + this.sql);\n\t\tif (connection != null) {\n\t\t\tconnection.close(reason);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void connectionAcquired(final BackendConnection conn) {\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"con query sql:\" + sql + \" to con:\" + conn);\n\t\t}\n\t\tconn.setResponseHandler(this);\n\t\ttry {\n\t\t\tif(ctx != null) {\n\t\t\t\tServerConnection sc = ctx.getSession().getSource();\n\t\t\t\t//conn.setCharsetIndex(sc.getCharsetIndex());\t\t\t\t\n\t\t\t\tconn.query(sql ,sc.getCharsetIndex());\n\t\t\t}else {\n\t\t\t\tconn.query(sql );\n\t\t\t}\n\t\t\t\n\t\t\tconnection = conn;\n\t\t} catch (Exception e) {// (UnsupportedEncodingException e) {\n\t\t\tdoFinished(true,e.getMessage());\n\t\t}\n\n\t}\n\n\tpublic boolean isFinished() {\n\t\treturn finished;\n\t}\n\n\tprivate void doFinished(boolean failed,String errorMsg) {\n\t\tfinished = true;\n\t\tjobHandler.finished(dataNodeOrDatabase, failed,errorMsg );\n\t\tif (ctx != null) {\n\t\t\tif(failed){\n\t\t\t\tctx.setHasError(true);\n\t\t\t}\n\t\t\tctx.onJobFinished(this);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void connectionError(Throwable e, BackendConnection conn) {\n\t\tLOGGER.info(\"can't get connection for sql :\" + sql);\n\t\tdoFinished(true,e.getMessage());\n\t}\n\n\t@Override\n\tpublic void errorResponse(byte[] err, BackendConnection conn) {\n\t\tErrorPacket errPg = new ErrorPacket();\n\t\terrPg.read(err);\n\t\t\n\t\tString errMsg = \"error response errno:\" + errPg.errno + \", \" + new String(errPg.message)\n\t\t\t\t+ \" from of sql :\" + sql + \" at con:\" + conn;\n\t\t\n\t\t// @see https://dev.mysql.com/doc/refman/5.6/en/error-messages-server.html\n\t\t// ER_SPECIFIC_ACCESS_DENIED_ERROR\n\t\tif ( errPg.errno == 1227  ) {\n\t\t\tLOGGER.warn( errMsg );\t\n\t\t\t\n\t\t}  else {\n\t\t\tLOGGER.info( errMsg );\n\t\t}\n\t\t\n\t\t\n\t\t\n\t\tdoFinished(true,errMsg);\n\t\tconn.release();\n\t}\n\n\t@Override\n\tpublic void okResponse(byte[] ok, BackendConnection conn) {\n//\t\tconn.syncAndExcute();\n\t\t//modify by zwy  这边 涉及到use database的返回，不能直接释放连接 需要继续处理包\n\t\tboolean executeResponse = conn.syncAndExcute();\t\t\n\t\tif(executeResponse){\n\t\t\tdoFinished(false,null);\n\t\t\tconn.release();\n\t\t} else {\n\t\t\tLOGGER.debug(\"syn response {}\" ,conn);\n\t\t}\n\t\t\n\t}\n\n\t@Override\n\tpublic void fieldEofResponse(byte[] header, List<byte[]> fields,\n\t\t\tbyte[] eof, BackendConnection conn) {\n\t\tjobHandler.onHeader(dataNodeOrDatabase, header, fields);\n\n\t}\n\n\t@Override\n\tpublic void rowResponse(byte[] row, BackendConnection conn) {\n\t\tboolean finsihed = jobHandler.onRowData(dataNodeOrDatabase, row);\n\t\tif (finsihed) {\n\t\t\tdoFinished(false,null);\n\t\t\tconn.close(\"not needed by user proc\");\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void rowEofResponse(byte[] eof, BackendConnection conn) {\n\t\tdoFinished(false,null);\n\t\tconn.release();\n\t}\n\n\t@Override\n\tpublic void writeQueueAvailable() {\n\n\t}\n\n\t@Override\n\tpublic void connectionClose(BackendConnection conn, String reason) {\n\t\tdoFinished(true,reason);\n\t}\n\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"SQLJob [ id=\" + id + \",dataNodeOrDatabase=\"\n\t\t\t\t+ dataNodeOrDatabase + \",sql=\" + sql + \",  jobHandler=\"\n\t\t\t\t+ jobHandler + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/SQLJobHandler.java",
    "content": "package io.mycat.sqlengine;\n\nimport java.util.List;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\npublic interface SQLJobHandler {\n\tpublic static final Logger LOGGER = LoggerFactory.getLogger(SQLJobHandler.class);\n\n\tpublic void onHeader(String dataNode, byte[] header, List<byte[]> fields);\n\n\tpublic boolean onRowData(String dataNode, byte[] rowData);\n\n\tpublic void finished(String dataNode, boolean failed, String errorMsg);\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/SQLQueryResult.java",
    "content": "package io.mycat.sqlengine;\n\npublic class SQLQueryResult<T> {\n\tprivate final T result;\n\tprivate final boolean success;\n\t\n\tprivate final String dataNode;\t// dataNode or database name\n\tprivate String tableName;\n\tprivate String errMsg;\n\n\tpublic SQLQueryResult(T result, boolean success) {\n\t\tsuper();\n\t\tthis.result = result;\n\t\tthis.success = success;\n\t\tthis.dataNode = null;\n\t}\n\t\n\tpublic SQLQueryResult(T result, boolean success, String dataNode,String errMsg) {\n\t\tsuper();\n\t\tthis.result = result;\n\t\tthis.success = success;\n\t\tthis.dataNode= dataNode;\n\t\tthis.errMsg=errMsg;\n\t}\n\n\tpublic String getErrMsg() {\n\t\treturn errMsg;\n\t}\n\n\tpublic void setErrMsg(String errMsg) {\n\t\tthis.errMsg = errMsg;\n\t}\n\n\tpublic T getResult() {\n\t\treturn result;\n\t}\n\tpublic boolean isSuccess() {\n\t\treturn success;\n\t}\n\tpublic String getDataNode() {\n\t\treturn dataNode;\n\t}\n\tpublic String getTableName() {\n\t\treturn tableName;\n\t}\n\tpublic void setTableName(String tableName) {\n\t\tthis.tableName = tableName;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/SQLQueryResultListener.java",
    "content": "package io.mycat.sqlengine;\n\n\npublic interface SQLQueryResultListener<T> {\n\n\tpublic void onResult(T result);\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/AbstractDataNodeMerge.java",
    "content": "package io.mycat.sqlengine.mpp;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.nio.handler.MultiNodeQueryHandler;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.server.NonBlockingSession;\nimport io.mycat.util.StringUtil;\nimport org.apache.log4j.Logger;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * Created by zagnix on 2016/7/6.\n */\npublic abstract class AbstractDataNodeMerge implements Runnable{\n\n\n    private static Logger LOGGER = Logger.getLogger(AbstractDataNodeMerge.class);\n    /**\n     *row 有多少col\n     */\n    protected int fieldCount;\n\n    /**\n     * 本次select的路由缓存集\n     */\n    protected final RouteResultset rrs;\n    /**\n     * 夸分片处理handler\n     */\n    protected MultiNodeQueryHandler multiQueryHandler = null;\n    /**\n     * 分片结束包\n     */\n    public PackWraper END_FLAG_PACK = new PackWraper();\n\n\n    /**\n     * 是否执行流式结果集输出\n     */\n\n    protected boolean isStreamOutputResult = false;\n\n    /**\n     * rowData缓存队列\n     */\n    protected BlockingQueue<PackWraper> packs = new LinkedBlockingQueue<PackWraper>();\n\n    /**\n     * 标志业务线程是否启动了？\n     */\n    protected final AtomicBoolean running = new AtomicBoolean(false);\n\n    public AbstractDataNodeMerge(MultiNodeQueryHandler handler,RouteResultset rrs){\n        this.rrs = rrs;\n        this.multiQueryHandler = handler;\n    }\n\n    public boolean isStreamOutputResult() {\n        return isStreamOutputResult;\n    }\n\n    public void setStreamOutputResult(boolean streamOutputResult) {\n        isStreamOutputResult = streamOutputResult;\n    }\n\n    /**\n     * Add a row pack, and may be wake up a business thread to work if not running.\n     * @param pack row pack\n     * @return true wake up a business thread, otherwise false\n     *\n     * @author Uncle-pan\n     * @since 2016-03-23\n     */\n    protected final boolean addPack(final PackWraper pack){\n        packs.add(pack);\n        if(running.get()){\n            return false;\n        }\n        final MycatServer server = MycatServer.getInstance();\n        server.getBusinessExecutor().execute(this);\n        return true;\n    }\n\n    /**\n     * 处理新进来每个row数据，通过PackWraper进行封装，\n     * 投递到队列中进行后续处理即可。\n     * process new record (mysql binary data),if data can output to client\n     * ,return true\n     *\n     * @param dataNode\n     *            DN's name (data from this dataNode)\n     * @param rowData\n     *            raw data\n     */\n    public boolean onNewRecord(String dataNode, byte[] rowData) {\n        final PackWraper data = new PackWraper();\n        data.dataNode = dataNode;\n        data.rowData = rowData;\n        addPack(data);\n\n        return false;\n    }\n\n\n    /**\n     * 将Map对应的col字段集，返回row中对应的index数组\n     * @param columns\n     * @param toIndexMap\n     * @return\n     */\n    protected static int[] toColumnIndex(String[] columns, Map<String, ColMeta> toIndexMap) {\n        int[] result = new int[columns.length];\n        ColMeta curColMeta;\n        for (int i = 0; i < columns.length; i++) {\n            curColMeta = toIndexMap.get(StringUtil.removeBackquote(columns[i]).toUpperCase());\n            if (curColMeta == null) {\n                throw new IllegalArgumentException(\n                        \"all columns in group by clause should be in the selected column list.!\"\n                                + columns[i]);\n            }\n            result[i] = curColMeta.colIndex;\n        }\n        return result;\n    }\n\n    @Override\n    public abstract void run();\n\n    public abstract void onRowMetaData(Map<String, ColMeta> columToIndx, int fieldCount) throws IOException;\n\n    public void outputMergeResult(NonBlockingSession session, byte[] eof) {\n        addPack(END_FLAG_PACK);\n    }\n\n    public RouteResultset getRrs() {\n        return this.rrs;\n    }\n\n    /**\n     * 做最后的结果集输出\n     * @return (最多i*(offset+size)行数据)\n     */\n    public abstract List<RowDataPacket> getResults(byte[] eof);\n    public abstract void clear();\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/ColMeta.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.sqlengine.mpp;\n\nimport java.io.Serializable;\n\npublic class ColMeta implements Serializable{\n\tpublic static final int COL_TYPE_DECIMAL = 0;\n\tpublic static final int COL_TYPE_INT = 1;\n\tpublic static final int COL_TYPE_SHORT = 2;\n\tpublic static final int COL_TYPE_LONG = 3;\n\tpublic static final int COL_TYPE_FLOAT = 4;\n\tpublic static final int COL_TYPE_DOUBLE = 5;\n\tpublic static final int COL_TYPE_NULL = 6;\n\tpublic static final int COL_TYPE_TIMSTAMP = 7;\n\tpublic static final int COL_TYPE_LONGLONG = 8;\n\tpublic static final int COL_TYPE_INT24 = 9;\n\tpublic static final int COL_TYPE_DATE = 0x0a;\n\tpublic static final int COL_TYPE_DATETIME=0X0C;\n\tpublic static final int COL_TYPE_TIME = 0x0b;\n\tpublic static final int COL_TYPE_YEAR = 0x0d;\n\tpublic static final int COL_TYPE_NEWDATE = 0x0e;\n\tpublic static final int COL_TYPE_VACHAR = 0x0f;\n\tpublic static final int COL_TYPE_BIT = 0x10;\n\tpublic static final int COL_TYPE_NEWDECIMAL = 0xf6;\n\tpublic static final int COL_TYPE_ENUM = 0xf7;\n\tpublic static final int COL_TYPE_SET = 0xf8;\n\tpublic static final int COL_TYPE_TINY_BLOB = 0xf9;\n\tpublic static final int COL_TYPE_TINY_TYPE_MEDIUM_BLOB = 0xfa;\n\tpublic static final int COL_TYPE_TINY_TYPE_LONG_BLOB = 0xfb;\n\tpublic static final int COL_TYPE_BLOB = 0xfc;\n\tpublic static final int COL_TYPE_VAR_STRING = 0xfd;\n\tpublic static final int COL_TYPE_STRING = 0xfe;\n\tpublic static final int COL_TYPE_GEOMETRY = 0xff;\n\tpublic  int colIndex;\n\tpublic final int colType;\n\t\n\tpublic int decimals;\n\n    public  int avgSumIndex;\n    public  int avgCountIndex;\n\n    public ColMeta(int colIndex, int colType) {\n\t\tsuper();\n\t\tthis.colIndex = colIndex;\n\t\tthis.colType = colType;\n\t}\n    public ColMeta(int avgSumIndex,int avgCountIndex,  int colType) {\n        super();\n        this.avgSumIndex = avgSumIndex;\n        this.avgCountIndex=avgCountIndex;\n        this.colType = colType;\n    }\n\tpublic int getColIndex() {\n\t\treturn colIndex;\n\t}\n\n\tpublic void setColIndex(int colIndex) {\n\t\tthis.colIndex = colIndex;\n\t}\n\tpublic int getColType() {\n\t\treturn colType;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ColMeta [colIndex=\" + colIndex + \", colType=\" + colType + \"]\";\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/ColumnRoutePair.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.sqlengine.mpp;\n\n/**\n * column ->node index\n * \n * @author wuzhih\n * \n */\npublic class ColumnRoutePair {\n\tpublic final String colValue;\n\tpublic final RangeValue rangeValue;\n\tpublic Integer nodeId;\n\n\tpublic int slot=-2;\n\n\tpublic int getSlot() {\n\t\treturn slot;\n\t}\n\n\tpublic void setSlot(int slot) {\n\t\tthis.slot = slot;\n\t}\n\n\tpublic ColumnRoutePair(String colValue) {\n\t\tsuper();\n\t\tthis.colValue = colValue;\n\t\tthis.rangeValue = null;\n\t}\n\n\tpublic ColumnRoutePair(RangeValue rangeValue) {\n\t\tsuper();\n\t\tthis.rangeValue = rangeValue;\n\t\tthis.colValue = null;\n\t}\n\n\tpublic Integer getNodeId() {\n\t\treturn nodeId;\n\t}\n\n\tpublic void setNodeId(Integer nodeId) {\n\t\tthis.nodeId = nodeId;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result\n\t\t\t\t+ ((colValue == null) ? 0 : colValue.hashCode());\n\t\tresult = prime * result\n\t\t\t\t+ ((rangeValue == null) ? 0 : rangeValue.hashCode());\n\t\tresult = prime * result + ((nodeId == null) ? 0 : nodeId.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tColumnRoutePair other = (ColumnRoutePair) obj;\n\t\tif (colValue == null) {\n\t\t\tif (other.colValue != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (!colValue.equals(other.colValue)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (rangeValue == null) {\n\t\t\tif (other.rangeValue != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (!rangeValue.equals(other.rangeValue)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (nodeId == null) {\n\t\t\tif (other.nodeId != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (!nodeId.equals(other.nodeId)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ColumnRoutePair [colValue=\" + colValue + \", nodeId=\" + nodeId\n\t\t\t\t+ \"]\";\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/DataMergeService.java",
    "content": "package io.mycat.sqlengine.mpp;\n\n/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\n\nimport com.alibaba.druid.sql.SQLUtils;\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.nio.handler.MultiNodeQueryHandler;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteResultsetNode;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.sqlengine.mpp.tmp.RowDataSorter;\nimport io.mycat.util.StringUtil;\n\nimport org.apache.log4j.Logger;\n\n\n\nimport java.nio.ByteBuffer;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Data merge service handle data Min,Max,AVG group 、order by 、limit\n * \n * @author wuzhih /modify by coder_czp/2015/11/2\n * \n * Fixbug: mycat sql timeout and hang problem.\n * @author Uncle-pan\n * @since 2016-03-23\n * \n */\npublic class DataMergeService extends AbstractDataNodeMerge {\n\n\tprivate RowDataSorter sorter;\n\tprivate RowDataPacketGrouper grouper;\n\tprivate Map<String, LinkedList<RowDataPacket>> result = new HashMap<String, LinkedList<RowDataPacket>>();\n\tprivate static Logger LOGGER = Logger.getLogger(DataMergeService.class);\n\tprivate ConcurrentHashMap<String, Boolean> canDiscard = new ConcurrentHashMap<String, Boolean>();\n\tpublic DataMergeService(MultiNodeQueryHandler handler, RouteResultset rrs) {\n\t\tsuper(handler,rrs);\n\n\t\tfor (RouteResultsetNode node : rrs.getNodes()) {\n\t\t\tresult.put(node.getName(), new LinkedList<RowDataPacket>());\n\t\t}\n\t}\n\n\n\t/**\n\t * @param columToIndx\n\t * @param fieldCount\n     */\n\tpublic void onRowMetaData(Map<String, ColMeta> columToIndx, int fieldCount) {\n\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"field metadata keys:\" + columToIndx.keySet());\n\t\t\tLOGGER.debug(\"field metadata values:\" + columToIndx.values());\n\t\t}\n\n\n\t\tint[] groupColumnIndexs = null;\n\t\tthis.fieldCount = fieldCount;\n\n\t\tif (rrs.getGroupByCols() != null) {\n\t\t\n\t\t\tgroupColumnIndexs = toColumnIndex(rrs.getGroupByCols(), columToIndx);\n\t\t}\n\n\t\tif (rrs.getHavingCols() != null) {\n\t\t\tColMeta colMeta = columToIndx.get(rrs.getHavingCols().getLeft()\n\t\t\t\t\t.toUpperCase());\n\t\t\tif (colMeta != null) {\n\t\t\t\trrs.getHavingCols().setColMeta(colMeta);\n\t\t\t}\n\t\t}\n\n\t\tif (rrs.isHasAggrColumn()) {\n\t\t\tList<MergeCol> mergCols = new LinkedList<MergeCol>();\n\t\t\tMap<String, Integer> mergeColsMap = rrs.getMergeCols();\n\n\n\n\t\t\tif (mergeColsMap != null) {\n\t\t\t\tfor (Map.Entry<String, Integer> mergEntry : mergeColsMap\n\t\t\t\t\t\t.entrySet()) {\n\t\t\t\t\tString colName = mergEntry.getKey().toUpperCase();\n\t\t\t\t\tint type = mergEntry.getValue();\n\t\t\t\t\tif (MergeCol.MERGE_AVG == type) {\n\t\t\t\t\t\n\t\t\t\t\t\tColMeta sumColMeta = columToIndx.get(colName + \"SUM\");\n\t\t\t\t\t\tColMeta countColMeta = columToIndx.get(colName\n\t\t\t\t\t\t\t\t+ \"COUNT\");\n\t\t\t\t\t\tif (sumColMeta != null && countColMeta != null) {\n\t\t\t\t\t\t\tColMeta colMeta = new ColMeta(sumColMeta.colIndex,\n\t\t\t\t\t\t\t\t\tcountColMeta.colIndex,\n\t\t\t\t\t\t\t\t\tsumColMeta.getColType());\n\t\t\t\t\t\t\tcolMeta.decimals = sumColMeta.decimals; // 保存精度\n\t\t\t\t\t\t\tmergCols.add(new MergeCol(colMeta, mergEntry\n\t\t\t\t\t\t\t\t\t.getValue()));\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t\n\t\t\t\t\t\tColMeta colMeta = columToIndx.get(SQLUtils.normalize(colName));\n\t\t\t\t\t\tmergCols.add(new MergeCol(colMeta, mergEntry.getValue()));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// add no alias merg column\n\t\t\tfor (Map.Entry<String, ColMeta> fieldEntry : columToIndx.entrySet()) {\n\t\t\t\tString colName = fieldEntry.getKey();\n\t\t\t\tint result = MergeCol.tryParseAggCol(colName);\n\t\t\t\tif (result != MergeCol.MERGE_UNSUPPORT\n\t\t\t\t\t\t&& result != MergeCol.MERGE_NOMERGE) {\n\t\t\t\t\tmergCols.add(new MergeCol(fieldEntry.getValue(), result));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\n\t\t\tgrouper = new RowDataPacketGrouper(groupColumnIndexs,\n\t\t\t\t\tmergCols.toArray(new MergeCol[mergCols.size()]),\n\t\t\t\t\trrs.getHavingCols());\n\t\t}\n\n\t\tif (rrs.getOrderByCols() != null) {\n\t\t\tLinkedHashMap<String, Integer> orders = rrs.getOrderByCols();\n\t\t\tOrderCol[] orderCols = new OrderCol[orders.size()];\n\t\t\tint i = 0;\n\t\t\tfor (Map.Entry<String, Integer> entry : orders.entrySet()) {\n\t\t\t\tString key = StringUtil.removeBackquote(entry.getKey()\n\t\t\t\t\t\t.toUpperCase());\n\t\t\t\tColMeta colMeta = columToIndx.get(key);\n\t\t\t\tif (colMeta == null) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"all columns in order by clause should be in the selected column list!\"\n\t\t\t\t\t\t\t\t\t+ entry.getKey());\n\t\t\t\t}\n\t\t\t\torderCols[i++] = new OrderCol(colMeta, entry.getValue());\n\t\t\t}\n\n\t\t\tRowDataSorter tmp = new RowDataSorter(orderCols);\n\t\t\ttmp.setLimit(rrs.getLimitStart(), rrs.getLimitSize());\n\t\t\tsorter = tmp;\n\t\t}\n\n\t\tif (MycatServer.getInstance().\n\t\t\t\tgetConfig().getSystem().\n\t\t\t\tgetUseStreamOutput() == 1\n\t\t\t\t&& grouper == null\n\t\t\t\t&& sorter == null) {\n\t\t\tsetStreamOutputResult(true);\n\t\t}else {\n\t\t\tsetStreamOutputResult(false);\n\t\t}\n\t}\n\n\n\t/**\n\t * release resources\n\t */\n\tpublic void clear() {\n\t\tresult.clear();\n\t\tgrouper = null;\n\t\tsorter = null;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\t// sort-or-group: no need for us to using multi-threads, because\n\t\t//both sorter and group are synchronized!!\n\t\t// @author Uncle-pan\n\t\t// @since 2016-03-23\n\t\tif(!running.compareAndSet(false, true)){\n\t\t\treturn;\n\t\t}\n\t\t// eof handler has been placed to \"if (pack == END_FLAG_PACK){}\" in for-statement\n\t\t// @author Uncle-pan\n\t\t// @since 2016-03-23\n\t\tboolean nulpack = false;\n\t\ttry{\n\t\t\t// loop-on-packs\n\t\t\tfor (; ; ) {\n\t\t\t\tfinal PackWraper pack = packs.poll();\n\t\t\t\t// async: handling row pack queue, this business thread should exit when no pack\n\t\t\t\t// @author Uncle-pan\n\t\t\t\t// @since 2016-03-23\n\t\t\t\tif(pack == null){\n\t\t\t\t\tnulpack = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t// eof: handling eof pack and exit\n\t\t\t\tif (pack == END_FLAG_PACK) {\n\n\n\n\t\t\t\t\tfinal int warningCount = 0;\n\t\t\t\t\tfinal EOFPacket eofp   = new EOFPacket();\n\t\t\t\t\tfinal ByteBuffer eof   = ByteBuffer.allocate(9);\n\t\t\t\t\tBufferUtil.writeUB3(eof, eofp.calcPacketSize());\n\t\t\t\t\teof.put(eofp.packetId);\n\t\t\t\t\teof.put(eofp.fieldCount);\n\t\t\t\t\tBufferUtil.writeUB2(eof, warningCount);\n\t\t\t\t\tBufferUtil.writeUB2(eof, eofp.status);\n\t\t\t\t\tfinal ServerConnection source = multiQueryHandler.getSession().getSource();\n\t\t\t\t\tfinal byte[] array = eof.array();\n\t\t\t\t\tmultiQueryHandler.outputMergeResult(source, array, getResults(array));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\n\t\t\t\t// merge: sort-or-group, or simple add\n\t\t\t\tfinal RowDataPacket row = new RowDataPacket(fieldCount);\n\t\t\t\trow.read(pack.rowData);\n\n\t\t\t\tif (grouper != null) {\n\t\t\t\t\tgrouper.addRow(row);\n\t\t\t\t} else if (sorter != null) {\n\t\t\t\t\tif (!sorter.addRow(row)) {\n\t\t\t\t\t\tcanDiscard.put(pack.dataNode,true);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tresult.get(pack.dataNode).add(row);\n\t\t\t\t}\n\t\t\t}// rof\n\t\t}catch(final Exception e){\n\t\t\tmultiQueryHandler.handleDataProcessException(e);\n\t\t}finally{\n\t\t\trunning.set(false);\n\t\t}\n\t\t// try to check packs, it's possible that adding a pack after polling a null pack\n\t\t//and before this time pointer!!\n\t\t// @author Uncle-pan\n\t\t// @since 2016-03-23\n\t\tif(nulpack && !packs.isEmpty()){\n\t\t\tthis.run();\n\t\t}\n\t}\n\t\n\n\n\t/**\n\t * return merged data\n\t * @return (最多i*(offset+size)行数据)\n\t */\n\tpublic List<RowDataPacket> getResults(byte[] eof) {\n\t\n\t\tList<RowDataPacket> tmpResult = null;\n\n\t\tif (this.grouper != null) {\n\t\t\ttmpResult = grouper.getResult();\n\t\t\tgrouper = null;\n\t\t}\n\n\t\t\n\t\tif (sorter != null) {\n\t\t\t\n\t\t\tif (tmpResult != null) {\n\t\t\t\tIterator<RowDataPacket> itor = tmpResult.iterator();\n\t\t\t\twhile (itor.hasNext()) {\n\t\t\t\t\tsorter.addRow(itor.next());\n\t\t\t\t\titor.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t\ttmpResult = sorter.getSortedResult();\n\t\t\tsorter = null;\n\t\t}\n\n\n\t\t\n\t\t//no grouper and sorter\n\t\tif(tmpResult == null){\n\t\t\ttmpResult = new LinkedList<>();\n      /**\n       * 每次移除dataNode,防止一个dataNode重复发送多次结果集\n       */\n\t\t\tfor (RouteResultsetNode node : rrs.getNodes()) {\n\t\t\t\tLinkedList<RowDataPacket> remove = result.remove(node.getName());\n\t\t\t\tif (remove != null){\n\t\t\t\t\ttmpResult.addAll(remove);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"prepare mpp merge result for \" + rrs.getStatement());\n\t\t}\n\t\treturn tmpResult;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/DataNodeMergeManager.java",
    "content": "package io.mycat.sqlengine.mpp;\n\nimport io.mycat.MycatServer;\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.backend.mysql.nio.handler.MultiNodeQueryHandler;\nimport io.mycat.memory.MyCatMemory;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.MemoryManager;\nimport io.mycat.memory.unsafe.row.BufferHolder;\nimport io.mycat.memory.unsafe.row.StructType;\nimport io.mycat.memory.unsafe.row.UnsafeRow;\nimport io.mycat.memory.unsafe.row.UnsafeRowWriter;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\nimport io.mycat.memory.unsafe.utils.sort.PrefixComparator;\nimport io.mycat.memory.unsafe.utils.sort.PrefixComparators;\nimport io.mycat.memory.unsafe.utils.sort.RowPrefixComputer;\nimport io.mycat.memory.unsafe.utils.sort.UnsafeExternalRowSorter;\nimport io.mycat.net.mysql.EOFPacket;\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.server.ServerConnection;\nimport io.mycat.util.StringUtil;\nimport org.apache.log4j.Logger;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n\n/**\n * Created by zagnix on 2016/6/21.\n */\npublic class DataNodeMergeManager extends AbstractDataNodeMerge {\n\n    private static Logger LOGGER = Logger.getLogger(DataNodeMergeManager.class);\n\n    /**\n     * key为datanode的分片节点名字\n     * value为对应的排序器\n     * 目前，没有使用！\n     */\n    private ConcurrentHashMap<String, UnsafeExternalRowSorter> unsafeRows =\n            new ConcurrentHashMap<String,UnsafeExternalRowSorter>();\n    /**\n     * 全局sorter，排序器\n     */\n    private UnsafeExternalRowSorter globalSorter = null;\n    /**\n     * UnsafeRowGrouper\n     */\n    private UnsafeRowGrouper unsafeRowGrouper = null;\n\n    /**\n     * 全局merge，排序器\n     */\n    private UnsafeExternalRowSorter globalMergeResult = null;\n\n    /**\n     * sorter需要的上下文环境\n     */\n    private final MyCatMemory myCatMemory;\n    private final MemoryManager memoryManager;\n    private final MycatPropertyConf conf;\n    /**\n     * Limit N，M\n     */\n    private final  int limitStart;\n    private final  int limitSize;\n    \n    private int[] mergeColsIndex;\n    private boolean hasEndFlag = false;\n    \n\n    private AtomicBoolean isMiddleResultDone;\n    public DataNodeMergeManager(MultiNodeQueryHandler handler, RouteResultset rrs,AtomicBoolean isMiddleResultDone) {\n        super(handler,rrs);\n        this.isMiddleResultDone = isMiddleResultDone;\n        this.myCatMemory = MycatServer.getInstance().getMyCatMemory();\n        this.memoryManager = myCatMemory.getResultMergeMemoryManager();\n        this.conf = myCatMemory.getConf();\n        this.limitStart = rrs.getLimitStart();\n        this.limitSize = rrs.getLimitSize();\n    }\n\n\n    public void onRowMetaData(Map<String, ColMeta> columToIndx, int fieldCount) throws IOException {\n\n        if (LOGGER.isDebugEnabled()) {\n            LOGGER.debug(\"field metadata keys:\" + columToIndx != null ? columToIndx.keySet() : \"null\");\n            LOGGER.debug(\"field metadata values:\" + columToIndx != null ? columToIndx.values() : \"null\");\n        }\n\n        OrderCol[] orderCols = null;\n        StructType schema = null;\n        UnsafeExternalRowSorter.PrefixComputer prefixComputer = null;\n        PrefixComparator prefixComparator = null;\n\n      \n        DataNodeMemoryManager dataNodeMemoryManager = null;\n        UnsafeExternalRowSorter sorter = null;\n\n        int[] groupColumnIndexs = null;\n        this.fieldCount = fieldCount;\n\n        if (rrs.getGroupByCols() != null) {\n            groupColumnIndexs = toColumnIndex(rrs.getGroupByCols(), columToIndx);\n            if (LOGGER.isDebugEnabled()) {\n                for (int i = 0; i <rrs.getGroupByCols().length ; i++) {\n                    LOGGER.debug(\"groupColumnIndexs:\" + rrs.getGroupByCols()[i]);\n                }\n            }\n        }\n\n\n        if (rrs.getHavingCols() != null) {\n            ColMeta colMeta = columToIndx.get(rrs.getHavingCols().getLeft()\n                    .toUpperCase());\n\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"getHavingCols:\" + rrs.getHavingCols().toString());\n            }\n\t\t\t\n\t    /**\n             * mycat 中将 sql： select avg(xxx) from t\n             * 重写 为 select sum(xxx) AS AVG[0~9]SUM,count(xxx) AS AVG[0~9]COUNT from t\n             *  或者 select avg(xxx)  AS xxx from t\n             *  select sum(xxx) AS xxxSUM,count(xxx) AS xxxCOUNT from t\n             */\n            if (colMeta == null) {\n                for (String key : columToIndx.keySet()) {\n                    if (key.toUpperCase().endsWith(\"SUM\")) {\n                        colMeta = columToIndx.get(key);\n                        break;\n                    }\n                }\n            }\n\t\t\t\n            if (colMeta != null) {\n                rrs.getHavingCols().setColMeta(colMeta);\n            }\n        }\n\n        if (rrs.isHasAggrColumn()) {\n            List<MergeCol> mergCols = new LinkedList<MergeCol>();\n            Map<String, Integer> mergeColsMap = rrs.getMergeCols();\n\n            if (mergeColsMap != null) {\n            \n\t\t\t\tif (LOGGER.isDebugEnabled() && rrs.getMergeCols() != null) {\n\t                LOGGER.debug(\"isHasAggrColumn:\" + rrs.getMergeCols().toString());\n\t            }\n                for (Map.Entry<String, Integer> mergEntry : mergeColsMap\n                        .entrySet()) {\n                    String colName = mergEntry.getKey().toUpperCase();\n                    int type = mergEntry.getValue();\n                    if (MergeCol.MERGE_AVG == type) {\n                        ColMeta sumColMeta = columToIndx.get(colName + \"SUM\");\n                        ColMeta countColMeta = columToIndx.get(colName\n                                + \"COUNT\");\n                        if (sumColMeta != null && countColMeta != null) {\n                            ColMeta colMeta = new ColMeta(sumColMeta.colIndex,\n                                    countColMeta.colIndex,\n                                    sumColMeta.getColType());\n                            mergCols.add(new MergeCol(colMeta, mergEntry\n                                    .getValue()));\n                        }\n                    } else {\n                        ColMeta colMeta = columToIndx.get(colName);\n                        mergCols.add(new MergeCol(colMeta, mergEntry.getValue()));\n                    }\n                }\n            }\n\n            // add no alias merg column\n            for (Map.Entry<String, ColMeta> fieldEntry : columToIndx.entrySet()) {\n                String colName = fieldEntry.getKey();\n                int result = MergeCol.tryParseAggCol(colName);\n                if (result != MergeCol.MERGE_UNSUPPORT\n                        && result != MergeCol.MERGE_NOMERGE) {\n                    mergCols.add(new MergeCol(fieldEntry.getValue(), result));\n                }\n            }\n\n            /**\n             * Group操作\n             */\n            MergeCol[] mergColsArrays = mergCols.toArray(new MergeCol[mergCols.size()]);\n            unsafeRowGrouper = new UnsafeRowGrouper(columToIndx,rrs.getGroupByCols(),\n            \t\tmergColsArrays,\n                    rrs.getHavingCols());\n            \n            if(mergColsArrays!=null&&mergColsArrays.length>0){\n    \t\t\tmergeColsIndex = new int[mergColsArrays.length];\n    \t\t\tfor(int i = 0;i<mergColsArrays.length;i++){\n    \t\t\t\tmergeColsIndex[i] = mergColsArrays[i].colMeta.colIndex;\n    \t\t\t}\n    \t\t\tArrays.sort(mergeColsIndex);\n    \t\t}\n        }\n\n\n        if (rrs.getOrderByCols() != null) {\n            LinkedHashMap<String, Integer> orders = rrs.getOrderByCols();\n            orderCols = new OrderCol[orders.size()];\n            int i = 0;\n            for (Map.Entry<String, Integer> entry : orders.entrySet()) {\n                String key = StringUtil.removeBackquote(entry.getKey()\n                        .toUpperCase());\n                ColMeta colMeta = columToIndx.get(key);\n                if (colMeta == null) {\n                    throw new IllegalArgumentException(\n                            \"all columns in order by clause should be in the selected column list!\"\n                                    + entry.getKey());\n                }\n                orderCols[i++] = new OrderCol(colMeta, entry.getValue());\n            }\n\n            /**\n             * 构造全局排序器\n             */\n            schema = new StructType(columToIndx,fieldCount);\n            schema.setOrderCols(orderCols);\n\n            prefixComputer = new RowPrefixComputer(schema);\n\n//            if(orderCols.length>0\n//                    && orderCols[0].getOrderType()\n//                    == OrderCol.COL_ORDER_TYPE_ASC){\n//                prefixComparator = PrefixComparators.LONG;\n//            }else {\n//                prefixComparator = PrefixComparators.LONG_DESC;\n//            }\n            \n            prefixComparator = getPrefixComparator(orderCols);\n\n            dataNodeMemoryManager =\n                    new DataNodeMemoryManager(memoryManager,Thread.currentThread().getId());\n\n            /**\n             * 默认排序，只是将数据连续存储到内存中即可。\n             */\n            globalSorter = new UnsafeExternalRowSorter(\n                    dataNodeMemoryManager,\n                    myCatMemory,\n                    schema,\n                    prefixComparator, prefixComputer,\n                    conf.getSizeAsBytes(\"mycat.buffer.pageSize\",\"32k\"),\n                    false/**是否使用基数排序*/,\n                    true/**排序*/);\n        }\n\n\n        if(conf.getBoolean(\"mycat.stream.output.result\",false)\n                && globalSorter == null\n                && unsafeRowGrouper == null){\n                setStreamOutputResult(true);\n        }else {\n\n            /**\n             * 1.schema \n             */\n\n             schema = new StructType(columToIndx,fieldCount);\n             schema.setOrderCols(orderCols);\n\n            /**\n             * 2 .PrefixComputer\n             */\n             prefixComputer = new RowPrefixComputer(schema);\n\n            /**\n             * 3 .PrefixComparator 默认是ASC，可以选择DESC\n             */\n\n            prefixComparator = PrefixComparators.LONG;\n\n\n            dataNodeMemoryManager = new DataNodeMemoryManager(memoryManager,\n                            Thread.currentThread().getId());\n\n            globalMergeResult = new UnsafeExternalRowSorter(\n                    dataNodeMemoryManager,\n                    myCatMemory,\n                    schema,\n                    prefixComparator,\n                    prefixComputer,\n                    conf.getSizeAsBytes(\"mycat.buffer.pageSize\", \"32k\"),\n                    false,/**是否使用基数排序*/\n                    false/**不排序*/);\n        }\n    }\n    \n    private PrefixComparator getPrefixComparator(OrderCol[] orderCols) {\n\t\tPrefixComparator prefixComparator = null;\n\t\tOrderCol firstOrderCol = orderCols[0];\n\t\tint orderType = firstOrderCol.getOrderType();\n\t\tint colType = firstOrderCol.colMeta.colType;\n\t\t\n\t\tswitch (colType) {\n\t\t\tcase ColMeta.COL_TYPE_INT:\n\t\t\tcase ColMeta.COL_TYPE_LONG:\n\t\t\tcase ColMeta.COL_TYPE_INT24:\n\t\t\tcase ColMeta.COL_TYPE_SHORT:\n\t\t\tcase ColMeta.COL_TYPE_LONGLONG:\n\t\t\t\tprefixComparator = (orderType == OrderCol.COL_ORDER_TYPE_ASC ? PrefixComparators.LONG : PrefixComparators.LONG_DESC);\n\t\t\t\tbreak;\n\t\t\tcase ColMeta.COL_TYPE_FLOAT:\n\t\t\tcase ColMeta.COL_TYPE_DOUBLE:\n\t\t\tcase ColMeta.COL_TYPE_DECIMAL:\n\t\t\tcase ColMeta.COL_TYPE_NEWDECIMAL:\n\t\t\t\tprefixComparator = (orderType == OrderCol.COL_ORDER_TYPE_ASC ? PrefixComparators.DOUBLE : PrefixComparators.DOUBLE_DESC);\n\t\t\t\tbreak;\n\t\t\tcase ColMeta.COL_TYPE_DATE:\n\t\t\tcase ColMeta.COL_TYPE_TIMSTAMP:\n\t\t\tcase ColMeta.COL_TYPE_TIME:\n\t\t\tcase ColMeta.COL_TYPE_YEAR:\n\t\t\tcase ColMeta.COL_TYPE_DATETIME:\n\t\t\tcase ColMeta.COL_TYPE_NEWDATE:\n\t\t\tcase ColMeta.COL_TYPE_BIT:\n\t\t\tcase ColMeta.COL_TYPE_VAR_STRING:\n\t\t\tcase ColMeta.COL_TYPE_STRING:\n\t\t\tcase ColMeta.COL_TYPE_ENUM:\n\t\t\tcase ColMeta.COL_TYPE_SET:\n\t\t\t\tprefixComparator = (orderType == OrderCol.COL_ORDER_TYPE_ASC ? PrefixComparators.BINARY : PrefixComparators.BINARY_DESC);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tprefixComparator = (orderType == OrderCol.COL_ORDER_TYPE_ASC ? PrefixComparators.LONG : PrefixComparators.LONG_DESC);\n\t\t\t\tbreak;\n\t\t}\n\t\t\n\t\treturn prefixComparator;\n\t}\n\n    @Override\n    public List<RowDataPacket> getResults(byte[] eof) {\n        return null;\n    }\n\n    private UnsafeRow unsafeRow = null;\n    private BufferHolder bufferHolder = null;\n    private UnsafeRowWriter unsafeRowWriter = null;\n    private  int Index = 0;\n\n    private volatile int clearStatus = ClearStatusEnum.INIT;\n\n    @Override\n    public void run() {\n\n        if (!running.compareAndSet(false, true)) {\n            return;\n        }\n\n        boolean nulpack = false;\n\n        try {\n            for (; ; ) {\n                if(clearStatus == ClearStatusEnum.PREPARE_CLEAR\n                        || clearStatus == ClearStatusEnum.CLEARED) {\n                    break;\n                }\n                final PackWraper pack = packs.poll();\n\n                if (pack == null) {\n                    nulpack = true;\n                    break;\n                }\n\n                if (pack == END_FLAG_PACK) {\n                \t\n                \thasEndFlag = true;\n                \t\n                \tif(packs.peek()!=null){\n                \t\tpacks.add(pack);\n                \t\tcontinue;\n                \t}\n                \t\n                     /**\n                     * 最后一个节点datenode发送了row eof packet说明了整个\n                     * 分片数据全部接收完成，进而将结果集全部发给你Mycat 客户端\n                     */\n                    final int warningCount = 0;\n                    final EOFPacket eofp = new EOFPacket();\n                    final ByteBuffer eof = ByteBuffer.allocate(9);\n                    BufferUtil.writeUB3(eof, eofp.calcPacketSize());\n                    eof.put(eofp.packetId);\n                    eof.put(eofp.fieldCount);\n                    BufferUtil.writeUB2(eof,warningCount);\n                    BufferUtil.writeUB2(eof,eofp.status);\n                    final ServerConnection source = multiQueryHandler.getSession().getSource();\n                    final byte[] array = eof.array();\n\n\n                    Iterator<UnsafeRow> iters = null;\n\n\n                    if (unsafeRowGrouper != null){\n                        /**\n                         * group by里面需要排序情况\n                         */\n                        if (globalSorter != null){\n                            iters = unsafeRowGrouper.getResult(globalSorter);\n                        }else {\n                            iters = unsafeRowGrouper.getResult(globalMergeResult);\n                        }\n\n                    }else if(globalSorter != null){\n\n                        iters = globalSorter.sort();\n\n                    }else if (!isStreamOutputResult){\n\n                        iters = globalMergeResult.sort();\n\n                    }\n\n                    if(iters != null){\n                        multiQueryHandler.outputMergeResult(source,array,iters,isMiddleResultDone);\n                     }    \n                    break;\n                }\n\n                unsafeRow = new UnsafeRow(fieldCount);\n                bufferHolder = new BufferHolder(unsafeRow,0);\n                unsafeRowWriter = new UnsafeRowWriter(bufferHolder,fieldCount);\n                bufferHolder.reset();\n\n                /**\n                 *构造一行row，将对应的col填充.\n                 */\n                MySQLMessage mm = new MySQLMessage(pack.rowData);\n                \n                unsafeRowWriter.grow((mm.getRowLength(fieldCount) + 1 ) / 2);\n                mm.readUB3();\n                mm.read();\n                \n                int nullnum = 0;\n                for (int i = 0; i < fieldCount; i++) {\n                    byte[] colValue = mm.readBytesWithLength();\n                    if (colValue != null)\n                    \tunsafeRowWriter.write(i,colValue);\n                    else\n                    {\n            \t \t\tif(mergeColsIndex!=null&&mergeColsIndex.length>0){\n            \t \t\t\t\n            \t \t\t\tif(Arrays.binarySearch(mergeColsIndex, i)<0){\n            \t \t\t\t\tnullnum++;\n        \t             \t}\n            \t \t\t}\n            \t \t\tunsafeRow.setNullAt(i);\n                    }\n                }\n                \n                if(mergeColsIndex!=null&&mergeColsIndex.length>0){\n                \tif(nullnum == (fieldCount - mergeColsIndex.length)){\n                \t\tif(!hasEndFlag){\n                \t\t\tpacks.add(pack);\n                        \tcontinue;\n                \t\t}\n                    }\n                }\n\n                unsafeRow.setTotalSize(bufferHolder.totalSize());\n\n                if(unsafeRowGrouper != null){\n                    unsafeRowGrouper.addRow(unsafeRow);\n                }else if (globalSorter != null){\n                    globalSorter.insertRow(unsafeRow);\n                }else {\n                    globalMergeResult.insertRow(unsafeRow);\n                }\n\n                unsafeRow = null;\n                bufferHolder = null;\n                unsafeRowWriter = null;\n            }\n\n        } catch (final Exception e) {\n        \te.printStackTrace();\n            multiQueryHandler.handleDataProcessException(e);\n        } finally {\n            synchronized (this) {\n                running.set(false);\n                if(clearStatus == ClearStatusEnum.PREPARE_CLEAR){\n                    clear();\n                    return ;\n                }\n            }\n            if (nulpack && !packs.isEmpty()) {\n                this.run();\n            }\n        }\n    }\n    /**\n     * 释放DataNodeMergeManager所申请的资源\n     */\n    public void clear() {\n\n        if(clearStatus == ClearStatusEnum.INIT) {\n            synchronized (this){\n                if(clearStatus == ClearStatusEnum.INIT && running.get() == true ) {\n                    clearStatus = ClearStatusEnum.PREPARE_CLEAR;\n                }\n            }\n        }\n\n        boolean flag = false;\n        synchronized (this) {\n            if(clearStatus == ClearStatusEnum.CLEARED || running.get() == true){\n                return;\n            }\n            clearStatus = ClearStatusEnum.CLEARED;\n            flag = true;\n\n        }\n        if(!flag){\n            return ;\n        }\n        unsafeRows.clear();\n        if (unsafeRowGrouper != null) {\n            unsafeRowGrouper.free();\n            unsafeRowGrouper = null;\n        }\n        if(globalSorter != null){\n            globalSorter.cleanupResources();\n            globalSorter = null;\n        }\n\n        if (globalMergeResult != null){\n            globalMergeResult.cleanupResources();\n            globalMergeResult = null;\n        }\n    }\n}\nclass ClearStatusEnum {\n    public static int INIT = -1;\n    public static int PREPARE_CLEAR = 1;\n    public static int CLEARED  = 2;\n}\n\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/HavingCols.java",
    "content": "package io.mycat.sqlengine.mpp;\n\nimport java.io.Serializable;\n\n/**\n * Created by v1.lion on 2015/6/10.\n */\npublic class HavingCols implements Serializable {\n\tString left;\n\tString right;\n\tString operator;\n\tpublic ColMeta colMeta;\n\n\tpublic HavingCols(String left, String right, String operator) {\n\t\tthis.left = left;\n\t\tthis.right = right;\n\t\tthis.operator = operator;\n\t}\n\n\tpublic String getLeft() {\n\t\treturn left;\n\t}\n\n\tpublic void setLeft(String left) {\n\t\tthis.left = left;\n\t}\n\n\tpublic String getRight() {\n\t\treturn right;\n\t}\n\n\tpublic void setRight(String right) {\n\t\tthis.right = right;\n\t}\n\n\tpublic String getOperator() {\n\t\treturn operator;\n\t}\n\n\tpublic void setOperator(String operator) {\n\t\tthis.operator = operator;\n\t}\n\n\tpublic ColMeta getColMeta() {\n\t\treturn colMeta;\n\t}\n\n\tpublic void setColMeta(ColMeta colMeta) {\n\t\tthis.colMeta = colMeta;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/LoadData.java",
    "content": "package io.mycat.sqlengine.mpp;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * Created by magicdoom on 2015/3/30.\n */\npublic class LoadData implements Serializable\n{\n    public static final String loadDataHint=\"/*loaddata*/\";\n    private boolean isLocal;\n    private List<String> data;\n    private String fileName;\n    private  String charset;\n    private  String lineTerminatedBy;\n    private String fieldTerminatedBy;\n    private  String enclose;\n    private  String escape;\n\n    public String getEscape()\n    {\n        return escape;\n    }\n\n    public void setEscape(String escape)\n    {\n        this.escape = escape;\n    }\n\n    public boolean isLocal()\n    {\n        return isLocal;\n    }\n\n    public void setLocal(boolean isLocal)\n    {\n        this.isLocal = isLocal;\n    }\n\n    public List<String> getData()\n    {\n        return data;\n    }\n\n    public void setData(List<String> data)\n    {\n        this.data = data;\n    }\n\n    public String getFileName()\n    {\n        return fileName;\n    }\n\n    public void setFileName(String fileName)\n    {\n        this.fileName = fileName;\n    }\n\n    public String getCharset()\n    {\n        return charset;\n    }\n\n    public void setCharset(String charset)\n    {\n        this.charset = charset;\n    }\n\n    public String getLineTerminatedBy()\n    {\n        return lineTerminatedBy;\n    }\n\n    public void setLineTerminatedBy(String lineTerminatedBy)\n    {\n        this.lineTerminatedBy = lineTerminatedBy;\n    }\n\n    public String getFieldTerminatedBy()\n    {\n        return fieldTerminatedBy;\n    }\n\n    public void setFieldTerminatedBy(String fieldTerminatedBy)\n    {\n        this.fieldTerminatedBy = fieldTerminatedBy;\n    }\n\n    public String getEnclose()\n    {\n        return enclose;\n    }\n\n    public void setEnclose(String enclose)\n    {\n        this.enclose = enclose;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/MergeCol.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.sqlengine.mpp;\n\npublic class MergeCol {\n\tpublic static final int MERGE_COUNT = 1;\n\tpublic static final int MERGE_SUM = 2;\n\tpublic static final int MERGE_MIN = 3;\n\tpublic static final int MERGE_MAX = 4;\n    public static final int MERGE_AVG= 5;\n\tpublic static final int MERGE_UNSUPPORT = -1;\n\tpublic static final int MERGE_NOMERGE = -2;\n\tpublic final int mergeType;\n\tpublic final ColMeta colMeta;\n\n\tpublic MergeCol(ColMeta colMeta, int mergeType) {\n\t\tsuper();\n\t\tthis.colMeta = colMeta;\n\t\tthis.mergeType = mergeType;\n\t}\n\n\tpublic static int getMergeType(String mergeType) {\n\t\tString upper=mergeType.toUpperCase();\n\t\tif (upper.startsWith(\"COUNT\")) {\n\t\t\treturn MERGE_COUNT;\n\t\t} else if (upper.startsWith(\"SUM\")) {\n\t\t\treturn MERGE_SUM;\n\t\t} else if (upper.startsWith(\"MIN\")) {\n\t\t\treturn MERGE_MIN;\n\t\t} else if (upper.startsWith(\"MAX\")) {\n\t\t\treturn MERGE_MAX;\n\t\t}\n        else if (upper.startsWith(\"AVG\")) {\n            return MERGE_AVG;\n        }\n        else {\n\t\t\treturn MERGE_UNSUPPORT;\n\t\t}\n\t}\n\n\tpublic static int tryParseAggCol(String column) {\n\t\t// MIN(*),MAX(*),COUNT(*),SUM\n\t\tif (column.length() < 6) {\n\t\t\treturn -1;\n\t\t}\n\t\tcolumn = column.toUpperCase();\n\n\t\tif (column.startsWith(\"COUNT(\")) {\n\t\t\treturn MERGE_COUNT;\n\t\t} else if (column.startsWith(\"SUM(\")) {\n\t\t\treturn MERGE_SUM;\n\t\t} else if (column.startsWith(\"MIN(\")) {\n\t\t\treturn MERGE_MIN;\n\t\t} else if (column.startsWith(\"MAX(\")) {\n\t\t\treturn MERGE_MAX;\n\t\t} else if (column.startsWith(\"AVG(\")) {\n\t\t\treturn MERGE_AVG;\n\t\t} else {\n\t\t\treturn MERGE_NOMERGE;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/OrderCol.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.sqlengine.mpp;\n\npublic class OrderCol {\n\tpublic final int orderType;\n\tpublic final ColMeta colMeta;\n\n\tpublic static final int COL_ORDER_TYPE_ASC = 0; // ASC\n\tpublic static final int COL_ORDER_TYPE_DESC = 1; // DESC\n\tpublic OrderCol(ColMeta colMeta, int orderType) {\n\t\tsuper();\n\t\tthis.colMeta = colMeta;\n\t\tthis.orderType = orderType;\n\t}\n\n\tpublic int getOrderType() {\n\t\treturn orderType;\n\t}\n\n\tpublic ColMeta getColMeta() {\n\t\treturn colMeta;\n\t}\n\t\n\t\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/PackWraper.java",
    "content": "package io.mycat.sqlengine.mpp;\n\n\n/**\n * Created by zagnix on 2016/7/6.\n */\n\n/**\n * 一行数据是从哪个节点来的。\n * 通过dataNode查找对应的sorter，\n * 将数据放到对应的datanode的sorter，\n * 进行排序.\n */\npublic final  class PackWraper {\n    public byte[] rowData;\n    public String dataNode;\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/RangRowDataPacketSorter.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.sqlengine.mpp;\n\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.sqlengine.mpp.tmp.RowDataSorter;\n\n\npublic class RangRowDataPacketSorter extends RowDataSorter {\n    public RangRowDataPacketSorter(OrderCol[] orderCols) {\n        super(orderCols);\n    }\n\n    public boolean ascDesc(int byColumnIndex) {\n        if (this.orderCols[byColumnIndex].orderType == OrderCol.COL_ORDER_TYPE_ASC) {// 升序\n            return true;\n        }\n        return false;\n    }\n\n    public int compareRowData(RowDataPacket l, RowDataPacket r, int byColumnIndex) {\n        byte[] left = l.fieldValues.get(this.orderCols[byColumnIndex].colMeta.colIndex);\n        byte[] right = r.fieldValues.get(this.orderCols[byColumnIndex].colMeta.colIndex);\n\n        return compareObject(left, right, this.orderCols[byColumnIndex]);\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/RangeValue.java",
    "content": "package io.mycat.sqlengine.mpp;\n\npublic class RangeValue {\n\t/*\n\t * 左值不包含 右值包含\n\t */\n\tpublic static final Integer NE = 0;\n\tpublic static final Integer EE = 1;\n\tpublic static final Integer EN = 2;\n\tpublic static final Integer NN = 3;\n\t\n\tpublic Object beginValue;\n\tpublic Object endValue;\n\tpublic Integer rangeType;\n\t\n\tpublic RangeValue(Object beginValue, Object endValue, Integer rangeType) {\n\t\tsuper();\n\t\tthis.beginValue = beginValue;\n\t\tthis.endValue = endValue;\n\t\tthis.rangeType = rangeType;\n\t}\n\t\n\t@Override\n\tpublic int hashCode(){\n\t\tint hash = 0;\n\t\thash = beginValue.hashCode();\n\t\thash = hash*31+endValue.hashCode();\n\t\thash = hash*31+rangeType;\n\t\treturn hash;\n\t}\n\t\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tRangeValue other = (RangeValue) obj;\n\t\t\n\t\tif( beginValue == null ){\n\t\t\tif(other.beginValue != null){\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}else if( !beginValue.equals(other.beginValue) ){\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tif( endValue == null ){\n\t\t\tif(other.endValue != null){\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}else if( !endValue.equals(other.endValue) ){\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tif( rangeType == null ){\n\t\t\tif(other.rangeType != null){\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}else if( !rangeType.equals(other.rangeType) ){\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\treturn true;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/RowDataPacketGrouper.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.sqlengine.mpp;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.TreeSet;\n\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.util.ByteUtil;\nimport io.mycat.util.CompareUtil;\nimport io.mycat.util.LongUtil;\n\n/**\n * implement group function select a,count(*),sum(*) from A group by a\n * \n * @author wuzhih\n * \n */\npublic class RowDataPacketGrouper {\n\n\tprivate List<RowDataPacket> result = Collections.synchronizedList(new ArrayList<RowDataPacket>());\n\tprivate final MergeCol[] mergCols;\n\tprivate int[] mergeColsIndex;\n\tprivate final int[] groupColumnIndexs;\n\tprivate boolean ishanlderFirstRow = false;   //结果集汇聚时,是否已处理第一条记录.\n\tprivate boolean isMergAvg=false;\n\tprivate HavingCols havingCols;\n\n\tpublic RowDataPacketGrouper(int[] groupColumnIndexs, MergeCol[] mergCols,HavingCols havingCols) {\n\t\tsuper();\n\t\tthis.groupColumnIndexs = groupColumnIndexs;\n\t\tthis.mergCols = mergCols;\n\t\tthis.havingCols = havingCols;\n\t\t\n\t\tif(mergCols!=null&&mergCols.length>0){\n\t\t\tmergeColsIndex = new int[mergCols.length];\n\t\t\tfor(int i = 0;i<mergCols.length;i++){\n\t\t\t\tmergeColsIndex[i] = mergCols[i].colMeta.colIndex;\n\t\t\t}\n\t\t\tArrays.sort(mergeColsIndex);\n\t\t}\n\t}\n\n\tpublic List<RowDataPacket> getResult() {\n\t\tif(!isMergAvg)\n\t\t{\n\t\t\tfor (RowDataPacket row : result)\n\t\t\t{\n\t\t\t\tmergAvg(row);\n\t\t\t}\n\t\t\tisMergAvg=true;\n\t\t}\n\n\t\tif(havingCols != null){\n\t\t\tfilterHaving();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate void filterHaving(){\n\t\tif (havingCols.getColMeta() == null || result == null) {\n\t\t\treturn;\n\t\t}\n\t\tIterator<RowDataPacket> it = result.iterator();\n\t\tbyte[] right = havingCols.getRight().getBytes(\n\t\t\t\tStandardCharsets.UTF_8);\n\t\tint index = havingCols.getColMeta().getColIndex();\n\t\tint colType = havingCols.getColMeta().getColType();\t// Added by winbill. 20160312.\n\t\twhile (it.hasNext()){\n\t\t\tRowDataPacket rowDataPacket = it.next();\n\t\t\tswitch (havingCols.getOperator()) {\n\t\t\tcase \"=\":\n\t\t\t\t/* Add parameter of colType, Modified by winbill. 20160312. */\n\t\t\t\tif (eq(rowDataPacket.fieldValues.get(index),right,colType)) {\n\t\t\t\t\tit.remove();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \">\":\n\t\t\t\t/* Add parameter of colType, Modified by winbill. 20160312. */\n\t\t\t\tif (gt(rowDataPacket.fieldValues.get(index),right,colType)) {\n\t\t\t\t\tit.remove();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"<\":\n\t\t\t\t/* Add parameter of colType, Modified by winbill. 20160312. */\n\t\t\t\tif (lt(rowDataPacket.fieldValues.get(index),right,colType)) {\n\t\t\t\t\tit.remove();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \">=\":\n\t\t\t\t/* Add parameter of colType, Modified by winbill. 20160312. */\n\t\t\t\tif (gt(rowDataPacket.fieldValues.get(index),right,colType) && eq(rowDataPacket.fieldValues.get(index),right,colType)) {\n\t\t\t\t\tit.remove();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"<=\":\n\t\t\t\t/* Add parameter of colType, Modified by winbill. 20160312. */\n\t\t\t\tif (lt(rowDataPacket.fieldValues.get(index),right,colType) && eq(rowDataPacket.fieldValues.get(index),right,colType)) {\n\t\t\t\t\tit.remove();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"!=\":\n\t\t\t\t/* Add parameter of colType, Modified by winbill. 20160312. */\n\t\t\t\tif (neq(rowDataPacket.fieldValues.get(index),right,colType)) {\n\t\t\t\t\tit.remove();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/* \n\t * Using new compare function instead of compareNumberByte \n\t * Modified by winbill. 20160312.\n\t */\n\tprivate boolean lt(byte[] l, byte[] r, final int colType) {\n//\t\treturn -1 != ByteUtil.compareNumberByte(l, r);\n\t\treturn -1 != RowDataPacketGrouper.compareObject(l, r, colType);\n\t}\n\n\tprivate boolean gt(byte[] l, byte[] r, final int colType) {\n//\t\treturn 1 != ByteUtil.compareNumberByte(l, r, havingCol);\n\t\treturn 1 != RowDataPacketGrouper.compareObject(l, r, colType);\n\t}\n\n\tprivate boolean eq(byte[] l, byte[] r, final int colType) {\n//\t\treturn 0 != ByteUtil.compareNumberByte(l, r, havingCol);\n\t\treturn 0 != RowDataPacketGrouper.compareObject(l, r, colType);\n\t}\n\n\tprivate boolean neq(byte[] l, byte[] r, final int colType) {\n//\t\treturn 0 == ByteUtil.compareNumberByte(l, r, havingCol);\n\t\treturn 0 == RowDataPacketGrouper.compareObject(l, r, colType);\n\t}\n\n\t/*\n\t * Compare with the value of having column\n\t * winbill. 20160312.\n\t */\n    public static final int compareObject(byte[] left,byte[] right, final int colType) {\n        switch (colType) {\n        case ColMeta.COL_TYPE_SHORT:\n        case ColMeta.COL_TYPE_INT:\n        case ColMeta.COL_TYPE_INT24:\n\t\tcase ColMeta.COL_TYPE_LONG:\n\t\t\treturn CompareUtil.compareInt(ByteUtil.getInt(left), ByteUtil.getInt(right));\n        case ColMeta.COL_TYPE_LONGLONG:\n            return CompareUtil.compareLong(ByteUtil.getLong(left), ByteUtil.getLong(right));\n        case ColMeta.COL_TYPE_FLOAT:\n        case ColMeta.COL_TYPE_DOUBLE:\n        case ColMeta.COL_TYPE_DECIMAL:\n        case ColMeta.COL_TYPE_NEWDECIMAL:\n            return CompareUtil.compareDouble(ByteUtil.getDouble(left), ByteUtil.getDouble(right));\n        case ColMeta.COL_TYPE_DATE:\n        case ColMeta.COL_TYPE_TIMSTAMP:\n        case ColMeta.COL_TYPE_TIME:\n        case ColMeta.COL_TYPE_YEAR:\n        case ColMeta.COL_TYPE_DATETIME:\n        case ColMeta.COL_TYPE_NEWDATE:\n        case ColMeta.COL_TYPE_BIT:\n        case ColMeta.COL_TYPE_VAR_STRING:\n        case ColMeta.COL_TYPE_STRING:\n        // ENUM和SET类型都是字符串，按字符串处理\n        case ColMeta.COL_TYPE_ENUM:\n        case ColMeta.COL_TYPE_SET:\n            return ByteUtil.compareNumberByte(left, right);\n        // BLOB相关类型和GEOMETRY类型不支持排序，略掉\n        }\n        return 0;\n    }\n\n\tpublic void addRow(RowDataPacket rowDataPkg) {\n\t\tfor (RowDataPacket row : result) {\n\t\t\tif (sameGropuColums(rowDataPkg, row)) {\n\t\t\t\taggregateRow(row, rowDataPkg);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// not aggreated ,insert new\n\t\tresult.add(rowDataPkg);\n\n\t}\n\n\tprivate void aggregateRow(RowDataPacket toRow, RowDataPacket newRow) {\n\t\tif (mergCols == null) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t/*\n\t\t * 这里进行一次判断, 在跨分片聚合的情况下,如果有一个没有记录的分片，最先返回,可能返回有null 的情况.\n\t\t */\n\t\tif(!ishanlderFirstRow&&mergeColsIndex!=null&&mergeColsIndex.length>0){\n\t\t\tList<byte[]> values = toRow.fieldValues;\n            for(int i=0;i<values.size();i++){\n            \tif(Arrays.binarySearch(mergeColsIndex, i)>=0){\n            \t\tcontinue;\n            \t}\n\t           if(values.get(i)==null){\n\t           \t   values.set(i, newRow.fieldValues.get(i));\n\t           }\n            }\n            ishanlderFirstRow = true;\n\t\t}\n\t\t\n\t\tfor (MergeCol merg : mergCols) {\n             if(merg.mergeType!=MergeCol.MERGE_AVG)\n             {\n                 byte[] result = mertFields(\n                         toRow.fieldValues.get(merg.colMeta.colIndex),\n                         newRow.fieldValues.get(merg.colMeta.colIndex),\n                         merg.colMeta.colType, merg.mergeType);\n                 if (result != null)\n                 {\n                     toRow.fieldValues.set(merg.colMeta.colIndex, result);\n                 }\n             }\n\t\t}\n    }\n\n\tprivate void mergAvg(RowDataPacket toRow) {\n\t\tif (mergCols == null) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t\n\n\t\tTreeSet<Integer> rmIndexSet = new TreeSet<Integer>();\n\t\tfor (MergeCol merg : mergCols) {\n\t\t\tif(merg.mergeType==MergeCol.MERGE_AVG)\n\t\t\t{\n\t\t\t\tbyte[] result = mertFields(\n\t\t\t\t\t\ttoRow.fieldValues.get(merg.colMeta.avgSumIndex),\n\t\t\t\t\t\ttoRow.fieldValues.get(merg.colMeta.avgCountIndex),\n\t\t\t\t\t\tmerg.colMeta.colType, merg.mergeType);\n\t\t\t\tif (result != null)\n\t\t\t\t{\n\t\t\t\t\ttoRow.fieldValues.set(merg.colMeta.avgSumIndex, result);\n//\t\t\t\t\ttoRow.fieldValues.remove(merg.colMeta.avgCountIndex) ;\n//\t\t\t\t\ttoRow.fieldCount=toRow.fieldCount-1;\n\t\t\t\t\trmIndexSet.add(merg.colMeta.avgCountIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// remove by index from large to small, to make sure each element deleted correctly\n\t\tfor(int index : rmIndexSet.descendingSet()) {\n\t\t\ttoRow.fieldValues.remove(index);\n\t\t\ttoRow.fieldCount = toRow.fieldCount - 1;\n\t\t}\n\n\n\t}\n\n\tprivate byte[] mertFields(byte[] bs, byte[] bs2, int colType, int mergeType) {\n\t\t// System.out.println(\"mergeType:\"+ mergeType+\" colType \"+colType+\n\t\t// \" field:\"+Arrays.toString(bs)+ \" ->  \"+Arrays.toString(bs2));\n\t\tif(bs2==null || bs2.length==0)\n\t\t{\n\t\t\treturn bs;\n\t\t}else if(bs==null || bs.length==0)\n\t\t{\n\t\t\treturn bs2;\n\t\t}\n\t\tswitch (mergeType) {\n\t\tcase MergeCol.MERGE_SUM:\n\t\t\tif (colType == ColMeta.COL_TYPE_DOUBLE\n\t\t\t\t|| colType == ColMeta.COL_TYPE_FLOAT) {\n\n\t\t\t\tDouble vale = ByteUtil.getDouble(bs) + ByteUtil.getDouble(bs2);\n\t\t\t\treturn vale.toString().getBytes();\n\t\t\t\t// return String.valueOf(vale).getBytes();\n\t\t\t} else if(colType == ColMeta.COL_TYPE_NEWDECIMAL\n\t\t\t\t\t|| colType == ColMeta.COL_TYPE_DECIMAL) {\n\t\t\t\tBigDecimal d1 = new BigDecimal(new String(bs));\n\t\t\t\td1 = d1.add(new BigDecimal(new String(bs2)));\n\t\t\t\treturn String.valueOf(d1).getBytes();\n\t\t\t}\n\t\t\t// continue to count case\n\t\tcase MergeCol.MERGE_COUNT: {\n\t\t\tlong s1 = Long.parseLong(new String(bs));\n\t\t\tlong s2 = Long.parseLong(new String(bs2));\n\t\t\tlong total = s1 + s2;\n\t\t\treturn LongUtil.toBytes(total);\n\t\t}\n\t\tcase MergeCol.MERGE_MAX: {\n\t\t\t// System.out.println(\"value:\"+\n\t\t\t// ByteUtil.getNumber(bs).doubleValue());\n\t\t\t// System.out.println(\"value2:\"+\n\t\t\t// ByteUtil.getNumber(bs2).doubleValue());\n\t\t\t// int compare = CompareUtil.compareDouble(ByteUtil.getNumber(bs)\n\t\t\t// .doubleValue(), ByteUtil.getNumber(bs2).doubleValue());\n\t\t\t// return ByteUtil.compareNumberByte(bs, bs2);\n\t\t\tint compare = ByteUtil.compareNumberByte(bs, bs2);\n\t\t\treturn (compare > 0) ? bs : bs2;\n\n\t\t}\n\t\tcase MergeCol.MERGE_MIN: {\n\t\t\t// int compare = CompareUtil.compareDouble(ByteUtil.getNumber(bs)\n\t\t\t// .doubleValue(), ByteUtil.getNumber(bs2).doubleValue());\n\t\t\t// int compare = ByteUtil.compareNumberArray(bs, bs2);\n\t\t\t//return (compare > 0) ? bs2 : bs;\n\t\t\tint compare = ByteUtil.compareNumberByte(bs, bs2);\n\t\t\treturn (compare > 0) ? bs2 : bs;\n\t\t\t// return ByteUtil.compareNumberArray2(bs, bs2, 2);\n\t\t}\n            case MergeCol.MERGE_AVG: {\n            \tif (colType == ColMeta.COL_TYPE_DOUBLE\n    \t\t\t\t\t|| colType == ColMeta.COL_TYPE_FLOAT) {\n            \t\tdouble aDouble = ByteUtil.getDouble(bs);\n            \t\tlong s2 = Long.parseLong(new String(bs2));\n            \t\tDouble vale = aDouble / s2;\n            \t\treturn vale.toString().getBytes();\n            \t} else if(colType == ColMeta.COL_TYPE_NEWDECIMAL\n    \t\t\t\t\t|| colType == ColMeta.COL_TYPE_DECIMAL) {\n            \t\tBigDecimal sum = new BigDecimal(new String(bs));\n                    // mysql avg 处理精度为 sum结果的精度扩展4, 采用四舍五入\n                    BigDecimal avg = sum.divide(new BigDecimal(new String(bs2)), sum.scale() + 4, RoundingMode.HALF_UP);\n                    return avg.toString().getBytes();\n            \t}\n            }\n\t\tdefault:\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\t// private static final\n\n\tprivate boolean sameGropuColums(RowDataPacket newRow, RowDataPacket existRow) {\n\t\tif (groupColumnIndexs == null) {// select count(*) from aaa , or group\n\t\t\t\t\t\t\t\t\t\t// column\n\t\t\treturn true;\n\t\t}\n\t\tfor (int i = 0; i < groupColumnIndexs.length; i++) {\n\t\t\tif (!Arrays.equals(newRow.fieldValues.get(groupColumnIndexs[i]),\n\t\t\t\t\texistRow.fieldValues.get(groupColumnIndexs[i]))) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t}\n\t\treturn true;\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/RowDataPacketSorter.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.sqlengine.mpp;\r\n\r\nimport java.util.Collection;\r\nimport java.util.Collections;\r\nimport java.util.concurrent.ConcurrentLinkedQueue;\r\n\r\nimport io.mycat.memory.unsafe.utils.BytesTools;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport io.mycat.net.mysql.RowDataPacket;\r\nimport io.mycat.util.ByteUtil;\r\n\r\npublic class RowDataPacketSorter {\r\n\r\n    private static final Logger LOGGER = LoggerFactory.getLogger(RowDataPacketSorter.class);\r\n    protected final OrderCol[] orderCols;\r\n\r\n    private Collection<RowDataPacket> sorted = new ConcurrentLinkedQueue<RowDataPacket>();\r\n    private RowDataPacket[] array, resultTemp;\r\n    private int p1, pr, p2;\r\n\r\n    public RowDataPacketSorter(OrderCol[] orderCols) {\r\n        super();\r\n        this.orderCols = orderCols;\r\n    }\r\n\r\n    public boolean addRow(RowDataPacket row) {\r\n        return this.sorted.add(row);\r\n\r\n    }\r\n\r\n    public Collection<RowDataPacket> getSortedResult() {\r\n        try {\r\n            this.mergeSort(sorted.toArray(new RowDataPacket[sorted.size()]));\r\n        } catch (Exception e) {\r\n            LOGGER.error(\"getSortedResultError\",e);\r\n        }\r\n        if (array != null) {\r\n            Collections.addAll(this.sorted, array);\r\n        }\r\n\r\n        return sorted;\r\n    }\r\n\r\n    private RowDataPacket[] mergeSort(RowDataPacket[] result) throws Exception {\r\n        this.sorted.clear();\r\n        array = result;\r\n        if (result == null || result.length < 2 || this.orderCols == null || orderCols.length < 1) {\r\n            return result;\r\n        }\r\n        mergeR(0, result.length - 1);\r\n\r\n        return array;\r\n    }\r\n\r\n    private void mergeR(int startIndex, int endIndex) {\r\n        if (startIndex < endIndex) {\r\n            int mid = (startIndex + endIndex) / 2;\r\n\r\n            mergeR(startIndex, mid);\r\n\r\n            mergeR(mid + 1, endIndex);\r\n\r\n            merge(startIndex, mid, endIndex);\r\n        }\r\n    }\r\n\r\n    private void merge(int startIndex, int midIndex, int endIndex) {\r\n        resultTemp = new RowDataPacket[(endIndex - startIndex + 1)];\r\n\r\n        pr = 0;\r\n        p1 = startIndex;\r\n        p2 = midIndex + 1;\r\n        while (p1 <= midIndex || p2 <= endIndex) {\r\n            if (p1 == midIndex + 1) {\r\n                while (p2 <= endIndex) {\r\n                    resultTemp[pr++] = array[p2++];\r\n\r\n                }\r\n            } else if (p2 == endIndex + 1) {\r\n                while (p1 <= midIndex) {\r\n                    resultTemp[pr++] = array[p1++];\r\n                }\r\n\r\n            } else {\r\n                compare(0);\r\n            }\r\n        }\r\n        for (p1 = startIndex, p2 = 0; p1 <= endIndex; p1++, p2++) {\r\n            array[p1] = resultTemp[p2];\r\n\r\n        }\r\n    }\r\n\r\n    /**\r\n     * 递归按照排序字段进行排序\r\n     * \r\n     * @param byColumnIndex\r\n     */\r\n    private void compare(int byColumnIndex) {\r\n\r\n        if (byColumnIndex == this.orderCols.length) {\r\n            if (this.orderCols[byColumnIndex - 1].orderType == OrderCol.COL_ORDER_TYPE_ASC) {\r\n\r\n                resultTemp[pr++] = array[p1++];\r\n            } else {\r\n                resultTemp[pr++] = array[p2++];\r\n            }\r\n            return;\r\n        }\r\n\r\n        byte[] left = array[p1].fieldValues.get(this.orderCols[byColumnIndex].colMeta.colIndex);\r\n        byte[] right = array[p2].fieldValues.get(this.orderCols[byColumnIndex].colMeta.colIndex);\r\n\r\n        if (compareObject(left, right, this.orderCols[byColumnIndex]) <= 0) {\r\n            if (compareObject(left, right, this.orderCols[byColumnIndex]) < 0) {\r\n                if (this.orderCols[byColumnIndex].orderType == OrderCol.COL_ORDER_TYPE_ASC) {// 升序\r\n                    resultTemp[pr++] = array[p1++];\r\n                } else {\r\n                    resultTemp[pr++] = array[p2++];\r\n                }\r\n            } else {// 如果当前字段相等，则按照下一个字段排序\r\n                compare(byColumnIndex + 1);\r\n\r\n            }\r\n\r\n        } else {\r\n            if (this.orderCols[byColumnIndex].orderType == OrderCol.COL_ORDER_TYPE_ASC) {// 升序\r\n                resultTemp[pr++] = array[p2++];\r\n            } else {\r\n                resultTemp[pr++] = array[p1++];\r\n            }\r\n\r\n        }\r\n    }\r\n\r\n    public static final int compareObject(Object l, Object r, OrderCol orderCol) {\r\n      return compareObject(( byte[])l, (byte[])r, orderCol);\r\n    }\r\n    \r\n    public static final int compareObject(byte[] left,byte[] right, OrderCol orderCol) {\r\n        int colType = orderCol.getColMeta().getColType();\r\n        switch (colType) {\r\n        case ColMeta.COL_TYPE_DECIMAL:\r\n        case ColMeta.COL_TYPE_FLOAT:\r\n        case ColMeta.COL_TYPE_DOUBLE:\r\n        case ColMeta.COL_TYPE_NEWDECIMAL:\r\n            // 因为mysql的日期也是数字字符串方式表达，因此可以跟整数等一起对待\r\n        \treturn ByteUtil.compareDouble(left, right);\r\n        case ColMeta.COL_TYPE_INT:\r\n        case ColMeta.COL_TYPE_SHORT:\r\n        case ColMeta.COL_TYPE_LONG:\r\n        case ColMeta.COL_TYPE_LONGLONG:\r\n        case ColMeta.COL_TYPE_INT24:\r\n\r\n        case ColMeta.COL_TYPE_DATE:\r\n        case ColMeta.COL_TYPE_TIMSTAMP:\r\n        case ColMeta.COL_TYPE_TIME:\r\n        case ColMeta.COL_TYPE_YEAR:\r\n        case ColMeta.COL_TYPE_NEWDATE:\r\n        case ColMeta.COL_TYPE_BIT:\r\n//            return BytesTools.compareTo(left,right);\r\n        \treturn ByteUtil.compareNumberByte(left, right);\r\n        case ColMeta.COL_TYPE_VAR_STRING:\r\n        case ColMeta.COL_TYPE_STRING:\r\n        // ENUM和SET类型都是字符串，按字符串处理\r\n        case ColMeta.COL_TYPE_ENUM:\r\n        case ColMeta.COL_TYPE_SET:\r\n        //MySQL与SQL Server的DateTime格式不同 需按字符串处理\r\n        case ColMeta.COL_TYPE_DATETIME:\r\n            return BytesTools.compareTo(left,right);\r\n            // BLOB相关类型和GEOMETRY类型不支持排序，略掉\r\n        }\r\n        return 0;\r\n    }\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/UnsafeRowGrouper.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.sqlengine.mpp;\n\nimport io.mycat.MycatServer;\nimport io.mycat.memory.MyCatMemory;\nimport io.mycat.memory.unsafe.KVIterator;\nimport io.mycat.memory.unsafe.map.UnsafeFixedWidthAggregationMap;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.MemoryManager;\nimport io.mycat.memory.unsafe.row.BufferHolder;\nimport io.mycat.memory.unsafe.row.StructType;\nimport io.mycat.memory.unsafe.row.UnsafeRow;\nimport io.mycat.memory.unsafe.row.UnsafeRowWriter;\n\nimport io.mycat.memory.unsafe.utils.BytesTools;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\nimport io.mycat.memory.unsafe.utils.sort.UnsafeExternalRowSorter;\nimport io.mycat.util.ByteUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.annotation.Nonnull;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.nio.charset.StandardCharsets;\nimport java.text.NumberFormat;\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Created by zagnix on 2016/6/26.\n *\n * implement group function select a,count(*),sum(*) from A group by a\n *\n */\npublic class UnsafeRowGrouper {\n\tprivate static final Logger logger = LoggerFactory.getLogger(UnsafeRowGrouper.class);\n\n\tprivate UnsafeFixedWidthAggregationMap aggregationMap = null;\n\tprivate final Map<String, ColMeta> columToIndx;\n\tprivate final MergeCol[] mergCols;\n        private String[] sortColumnsByIndex = null;\n \tprivate final String[] columns;\n\tprivate boolean isMergAvg=false;\n\tprivate HavingCols havingCols;\n\tprivate UnsafeRow groupKey = null;\n\tprivate UnsafeRow valueKey = null;\n\tprivate BufferHolder bufferHolder = null;\n\tprivate UnsafeRowWriter unsafeRowWriter = null;\n\tprivate final int groupKeyfieldCount;\n\tprivate final int valuefieldCount;\n\tprivate StructType groupKeySchema ;\n\tprivate StructType aggBufferSchema;\n\tprivate UnsafeRow emptyAggregationBuffer;\n\tprivate final MyCatMemory myCatMemory;\n\tprivate final MemoryManager memoryManager;\n\tprivate final MycatPropertyConf conf;\n\n\tpublic UnsafeRowGrouper(Map<String,ColMeta> columToIndx,String[] columns, MergeCol[] mergCols, HavingCols havingCols) {\n\t\tsuper();\n\t\tassert columns!=null;\n\t\tassert columToIndx!=null;\n\t\tassert mergCols !=null;\n\t\tthis.columToIndx = columToIndx;\n\t\tthis.columns = columns;\n\t\tthis.mergCols = mergCols;\n\t\tthis.havingCols = havingCols;\n                this.sortColumnsByIndex =  columns !=null ? toSortColumnsByIndex(columns,columToIndx):null;\n\t\tthis.groupKeyfieldCount = columns != null?columns.length:0;\n\t\tthis.valuefieldCount = columToIndx != null?columToIndx.size():0;\n\t\tthis.myCatMemory = MycatServer.getInstance().getMyCatMemory();\n\t\tthis.memoryManager = myCatMemory.getResultMergeMemoryManager();\n\t\tthis.conf = myCatMemory.getConf();\n\n\t\tlogger.debug(\"columToIndx :\" + (columToIndx != null ? columToIndx.toString():\"null\"));\n\n\t\tinitGroupKey();\n\t\tinitEmptyValueKey();\n\n\t\tDataNodeMemoryManager dataNodeMemoryManager =\n\t\t\t\tnew DataNodeMemoryManager(memoryManager,Thread.currentThread().getId());\n\n\t\taggregationMap = new UnsafeFixedWidthAggregationMap(\n\t\t\t\temptyAggregationBuffer,\n\t\t\t\taggBufferSchema,\n\t\t\t\tgroupKeySchema,\n\t\t\t\tdataNodeMemoryManager,\n\t\t\t\t1024,\n\t\t\t\tconf.getSizeAsBytes(\"mycat.buffer.pageSize\", \"32k\"),\n\t\t\t\tfalse);\n\t}\n\n    private String[] toSortColumnsByIndex(String[] columns, Map<String, ColMeta> columToIndx) {\n\n        Map<String,Integer> map = new HashMap<String,Integer>();\n\n        ColMeta curColMeta;\n        for (int i = 0; i < columns.length; i++) {\n            curColMeta = columToIndx.get(columns[i].toUpperCase());\n            if (curColMeta == null) {\n                throw new IllegalArgumentException(\n                        \"all columns in group by clause should be in the selected column list.!\"\n                                + columns[i]);\n            }\n            map.put(columns[i],curColMeta.colIndex);\n        }\n\n\n        String[] sortColumnsByIndex = new String[map.size()];\n\n        List<Map.Entry<String, Integer>> entryList = new ArrayList<\n                Map.Entry<String, Integer>>(\n                map.entrySet());\n\n        Collections.sort(entryList, new Comparator<Map.Entry<String, Integer>>() {\n            @Override\n            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {\n                return o1.getValue().compareTo(o2.getValue());\n            }\n        });\n\n        Iterator<Map.Entry<String, Integer>> iter = entryList.iterator();\n        Map.Entry<String, Integer> tmpEntry = null;\n\n        int index = 0;\n\n        while (iter.hasNext()) {\n            tmpEntry = iter.next();\n            sortColumnsByIndex[index++] = tmpEntry.getKey();\n        }\n\n        return sortColumnsByIndex;\n    }\n\n    private void initGroupKey(){\n\t\t/**\n\t\t * 构造groupKey\n\t\t */\n\t\tMap<String,ColMeta> groupcolMetaMap = new HashMap<String,ColMeta>(this.groupKeyfieldCount);\n\n\t\tgroupKey = new UnsafeRow(this.groupKeyfieldCount);\n\t\tbufferHolder = new BufferHolder(groupKey,0);\n\t\tunsafeRowWriter = new UnsafeRowWriter(bufferHolder,this.groupKeyfieldCount);\n\t\tbufferHolder.reset();\n\n\t\tColMeta curColMeta = null;\n\n\t\tfor (int i = 0; i < this.groupKeyfieldCount; i++) {\n\t\t\tcurColMeta = this.columToIndx.get(sortColumnsByIndex[i].toUpperCase());\n\t\t\tgroupcolMetaMap.put(sortColumnsByIndex[i],curColMeta);\n\n\n\t\t\t\tswitch (curColMeta.colType) {\n\t\t\t\t\tcase ColMeta.COL_TYPE_BIT:\n\t\t\t\t\t\tgroupKey.setByte(i, (byte) 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_INT:\n\t\t\t\t\tcase ColMeta.COL_TYPE_INT24:\n\t\t\t\t\tcase ColMeta.COL_TYPE_LONG:\n\t\t\t\t\t\tgroupKey.setInt(i, 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_SHORT:\n\t\t\t\t\t\tgroupKey.setShort(i, (short) 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_FLOAT:\n\t\t\t\t\t\tgroupKey.setFloat(i, 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_DOUBLE:\n\t\t\t\t\t\tgroupKey.setDouble(i, 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_NEWDECIMAL:\n//\t\t\t\t\t\tgroupKey.setDouble(i, 0);\n\t\t\t\t\t\tunsafeRowWriter.write(i, new BigDecimal(0L));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_LONGLONG:\n\t\t\t\t\t\tgroupKey.setLong(i, 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tunsafeRowWriter.write(i, \"init\".getBytes());\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t}\n\t\tgroupKey.setTotalSize(bufferHolder.totalSize());\n\n\t\tgroupKeySchema = new StructType(groupcolMetaMap,this.groupKeyfieldCount);\n\t\tgroupKeySchema.setOrderCols(null);\n\t}\n\n\tprivate void initEmptyValueKey(){\n\t\t/**\n\t\t * 构造valuerow\n\t\t */\n\t\temptyAggregationBuffer = new UnsafeRow(this.valuefieldCount);\n\t\tbufferHolder = new BufferHolder(emptyAggregationBuffer,0);\n\t\tunsafeRowWriter = new UnsafeRowWriter(bufferHolder,this.valuefieldCount);\n\t\tbufferHolder.reset();\n\n\t\tColMeta curColMeta = null;\n\t\tfor (Map.Entry<String, ColMeta> fieldEntry : columToIndx.entrySet()) {\n            curColMeta = fieldEntry.getValue();\n\n\t\t\t\tswitch (curColMeta.colType) {\n\t\t\t\t\tcase ColMeta.COL_TYPE_BIT:\n\t\t\t\t\t\temptyAggregationBuffer.setByte(curColMeta.colIndex, (byte) 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_INT:\n\t\t\t\t\tcase ColMeta.COL_TYPE_INT24:\n\t\t\t\t\tcase ColMeta.COL_TYPE_LONG:\n\t\t\t\t\t\temptyAggregationBuffer.setInt(curColMeta.colIndex, 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_SHORT:\n\t\t\t\t\t\temptyAggregationBuffer.setShort(curColMeta.colIndex, (short) 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_LONGLONG:\n\t\t\t\t\t\temptyAggregationBuffer.setLong(curColMeta.colIndex, 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_FLOAT:\n\t\t\t\t\t\temptyAggregationBuffer.setFloat(curColMeta.colIndex, 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_DOUBLE:\n\t\t\t\t\t\temptyAggregationBuffer.setDouble(curColMeta.colIndex, 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_NEWDECIMAL:\n//\t\t\t\t\t\temptyAggregationBuffer.setDouble(curColMeta.colIndex, 0);\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex, new BigDecimal(0L));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex, \"init\".getBytes());\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t}\n\n\t\temptyAggregationBuffer.setTotalSize(bufferHolder.totalSize());\n\t\taggBufferSchema = new StructType(columToIndx,this.valuefieldCount);\n\t\taggBufferSchema.setOrderCols(null);\n\t}\n\n\n\tpublic Iterator<UnsafeRow> getResult(@Nonnull UnsafeExternalRowSorter sorter) throws IOException {\n\t\tKVIterator<UnsafeRow,UnsafeRow> iter = aggregationMap.iterator();\n        /**\n         * 求平均值\n         */\n\t\tif (isMergeAvg() && !isMergAvg){\n\t\t\ttry {\n\t\t\t\twhile (iter.next()){\n\t\t\t\t\tmergAvg(iter.getValue());\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\tlogger.error(e.getMessage());\n\t\t\t}\n\t\t\tisMergAvg = true;\n\t\t\tprocessAvgFieldPrecision();\n\t\t}\n        /**\n         * group having\n         */\n        if (havingCols !=null){\n            filterHaving(sorter);\n        }else{\n\n            /**\n             * KVIterator<K,V> ==>Iterator<V>\n             */\n            insertValue(sorter);\n        }\n\t\treturn sorter.sort();\n\t}\n\t\n\t/**\n\t * 处理AVG列精度\n\t */\n\tprivate void processAvgFieldPrecision() {\n\t    for (MergeCol mergCol : mergCols) {\n            if (mergCol.mergeType != MergeCol.MERGE_AVG) {\n                continue ;\n            }\n            for (String key : columToIndx.keySet()) {\n                ColMeta colMeta = columToIndx.get(key);\n                // AVG列的小数点精度默认取SUM小数点精度, 计算和返回的小数点精度应该扩展4\n                if (colMeta.colIndex == mergCol.colMeta.avgSumIndex) {\n                    colMeta.decimals += 4;\n                    break ;\n                }\n            }\n        }\n\t}\n\t\n\t/**\n\t * 判断列是否为AVG列\n\t * @param columnName\n\t * @return\n\t */\n\tprivate boolean isAvgField(String columnName) {\n\t\tPattern pattern = Pattern.compile(\"AVG([1-9]\\\\d*|0)SUM\");\n\t\tMatcher matcher = pattern.matcher(columnName);\n\t\treturn matcher.find();\n\t}\n\n\n    public UnsafeRow getAllBinaryRow(UnsafeRow row) throws UnsupportedEncodingException {\n\n        UnsafeRow value = new UnsafeRow( this.valuefieldCount);\n        bufferHolder = new BufferHolder(value,0);\n        unsafeRowWriter = new UnsafeRowWriter(bufferHolder, this.valuefieldCount);\n        bufferHolder.reset();\n        ColMeta curColMeta = null;\n\n        for (Map.Entry<String, ColMeta> fieldEntry : columToIndx.entrySet()) {\n\t\t\tcurColMeta = fieldEntry.getValue();\n\n\t\t\tif (!row.isNullAt(curColMeta.colIndex)) {\n\t\t\t\tswitch (curColMeta.colType) {\n\t\t\t\t\tcase ColMeta.COL_TYPE_BIT:\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex, row.getByte(curColMeta.colIndex));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_INT:\n\t\t\t\t\tcase ColMeta.COL_TYPE_LONG:\n\t\t\t\t\tcase ColMeta.COL_TYPE_INT24:\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex,\n\t\t\t\t\t\t\t\tBytesTools.int2Bytes(row.getInt(curColMeta.colIndex)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_SHORT:\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex,\n\t\t\t\t\t\t\t\tBytesTools.short2Bytes(row.getShort(curColMeta.colIndex)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_LONGLONG:\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex,\n\t\t\t\t\t\t\t\tBytesTools.long2Bytes(row.getLong(curColMeta.colIndex)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_FLOAT:\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex,\n\t\t\t\t\t\t\t\tBytesTools.float2Bytes(row.getFloat(curColMeta.colIndex)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_DOUBLE:\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex,\n\t\t\t\t\t\t\t\tBytesTools.double2Bytes(row.getDouble(curColMeta.colIndex)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_NEWDECIMAL:\n\t\t\t\t\t\tint scale = curColMeta.decimals;\n\t\t\t\t\t\tBigDecimal decimalVal = row.getDecimal(curColMeta.colIndex, scale);\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex, decimalVal.toString().getBytes());\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex,\n\t\t\t\t\t\t\t\trow.getBinary(curColMeta.colIndex));\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t }else {\n\t\t\t\tunsafeRowWriter.setNullAt(curColMeta.colIndex);\n\t\t\t}\n\t\t\t}\n\n        value.setTotalSize(bufferHolder.totalSize());\n        return value;\n    }\n    \n    private void insertValue(@Nonnull UnsafeExternalRowSorter sorter){\n            KVIterator<UnsafeRow,UnsafeRow> it = aggregationMap.iterator();\n            try {\n                while (it.next()){\n                    UnsafeRow row = getAllBinaryRow(it.getValue());\n                    sorter.insertRow(row);\n                }\n            } catch (IOException e) {\n               logger.error(\"group insertValue err: \" + e.getMessage());\n\t\t\t   free();\n            }\n    }\n\n\tprivate void filterHaving(@Nonnull UnsafeExternalRowSorter sorter){\n\n        if (havingCols.getColMeta() == null || aggregationMap == null) {\n\t\t\treturn;\n\t\t}\n\t\tKVIterator<UnsafeRow,UnsafeRow> it = aggregationMap.iterator();\n\t\tbyte[] right = havingCols.getRight().getBytes(StandardCharsets.UTF_8);\n\t\tint index = havingCols.getColMeta().getColIndex();\n\t\ttry {\n\t\t\twhile (it.next()){\n                UnsafeRow row = getAllBinaryRow(it.getValue());\n                switch (havingCols.getOperator()) {\n                case \"=\":\n                    if (eq(row.getBinary(index),right)) {\n                        sorter.insertRow(row);\n                    }\n                    break;\n                case \">\":\n                    if (gt(row.getBinary(index),right)) {\n                        sorter.insertRow(row);\n                    }\n                    break;\n                case \"<\":\n                    if (lt(row.getBinary(index),right)) {\n                        sorter.insertRow(row);\n                    }\n                    break;\n                case \">=\":\n                    if (gt(row.getBinary(index),right) || eq(row.getBinary(index),right)) {\n                        sorter.insertRow(row);\n                    }\n                    break;\n                case \"<=\":\n                    if (lt(row.getBinary(index),right) || eq(row.getBinary(index),right)) {\n                        sorter.insertRow(row);\n                    }\n                    break;\n                case \"!=\":\n                    if (neq(row.getBinary(index),right)) {\n                        sorter.insertRow(row);\n                    }\n                    break;\n                }\n            }\n\t\t} catch (IOException e) {\n\t\t\tlogger.error(e.getMessage());\n\t\t}\n\n\t}\n\n\tprivate boolean lt(byte[] l, byte[] r) {\n\t\treturn -1 >= ByteUtil.compareNumberByte(l, r);\n\t}\n\n\tprivate boolean gt(byte[] l, byte[] r) {\n\t\treturn 1 <= ByteUtil.compareNumberByte(l, r);\n\t}\n\n\tprivate boolean eq(byte[] l, byte[] r) {\n\t\treturn 0 == ByteUtil.compareNumberByte(l, r);\n\t}\n\n\tprivate boolean neq(byte[] l, byte[] r) {\n\t\treturn 0 != ByteUtil.compareNumberByte(l, r);\n\t}\n\n\t/**\n\t * 构造groupKey\n\t */\n\tprivate UnsafeRow getGroupKey(UnsafeRow row) throws UnsupportedEncodingException {\n\n\t\tUnsafeRow key = null;\n\t\tif(this.sortColumnsByIndex == null){\n            /**\n             * 针对没有group by关键字\n             * select count(*) from table;\n             */\n\t\t\tkey = new UnsafeRow(this.groupKeyfieldCount+1);\n\t\t\tbufferHolder = new BufferHolder(key,0);\n\t\t\tunsafeRowWriter = new UnsafeRowWriter(bufferHolder,this.groupKeyfieldCount+1);\n\t\t\tbufferHolder.reset();\n\t\t\tunsafeRowWriter.write(0,\"same\".getBytes());\n\t\t\tkey.setTotalSize(bufferHolder.totalSize());\n\t\t\treturn key;\n\t\t}\n\n\n\t\tkey = new UnsafeRow(this.groupKeyfieldCount);\n\t\tbufferHolder = new BufferHolder(key,0);\n\t\tunsafeRowWriter = new UnsafeRowWriter(bufferHolder,this.groupKeyfieldCount);\n\t\tbufferHolder.reset();\n\n\n\t\tColMeta curColMeta = null;\n\t\tfor (int i = 0; i < this.groupKeyfieldCount;i++) {\n\t\t\tcurColMeta = this.columToIndx.get(sortColumnsByIndex[i].toUpperCase());\n\t\t     if(!row.isNullAt(curColMeta.colIndex)){\n\t\t\t\tswitch(curColMeta.colType){\n\t\t\t\t\tcase ColMeta.COL_TYPE_BIT:\n\t\t\t\t\t\tkey.setByte(i,row.getByte(curColMeta.colIndex));\n\t\t\t\t\tcase ColMeta.COL_TYPE_INT:\n\t\t\t\t\tcase ColMeta.COL_TYPE_LONG:\n\t\t\t\t\tcase ColMeta.COL_TYPE_INT24:\n\t\t\t\t\t\tkey.setInt(i,\n\t\t\t\t\t\t\t\tBytesTools.getInt(row.getBinary(curColMeta.colIndex)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_SHORT:\n\t\t\t\t\t\tkey.setShort(i,\n\t\t\t\t\t\t\t\tBytesTools.getShort(row.getBinary(curColMeta.colIndex)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_FLOAT:\n\t\t\t\t\t\tkey.setFloat(i,\n\t\t\t\t\t\t\t\t BytesTools.getFloat(row.getBinary(curColMeta.colIndex)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_DOUBLE:\n\t\t\t\t\t\tkey.setDouble(i,\n\t\t\t\t\t\t\t\tBytesTools.getDouble(row.getBinary(curColMeta.colIndex)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_NEWDECIMAL:\n//\t\t\t\t\t\tkey.setDouble(i,\n//\t\t\t\t\t\t\t\tBytesTools.getDouble(row.getBinary(curColMeta.colIndex)));\n\t\t\t\t\t\tunsafeRowWriter.write(i, \n\t\t\t\t\t\t\t\tnew BigDecimal(new String(row.getBinary(curColMeta.colIndex))));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_LONGLONG:\n\t\t\t\t\t\tkey.setLong(i,\n\t\t\t\t\t\t\t\tBytesTools.getLong(row.getBinary(curColMeta.colIndex)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tunsafeRowWriter.write(i,\n\t\t\t\t\t\t\t\trow.getBinary(curColMeta.colIndex));\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t     }else {\n\t\t\t\t key.setNullAt(i);\n\t\t\t }\n\t\t}\n\n\t\tkey.setTotalSize(bufferHolder.totalSize());\n\n\t\treturn key;\n\t}\n\n\n\t/**\n\t * 构造value\n\t */\n\tprivate UnsafeRow getValue(UnsafeRow row) throws UnsupportedEncodingException {\n\n\t\tUnsafeRow value = new UnsafeRow(this.valuefieldCount);\n\t\tbufferHolder = new BufferHolder(value,0);\n\t\tunsafeRowWriter = new UnsafeRowWriter(bufferHolder,this.valuefieldCount);\n\t\tbufferHolder.reset();\n\t\tColMeta curColMeta = null;\n\t\tfor (Map.Entry<String, ColMeta> fieldEntry : columToIndx.entrySet()) {\n\t\t\tcurColMeta = fieldEntry.getValue();\n\t\t\tif(!row.isNullAt(curColMeta.colIndex)) {\n\t\t\t\tswitch (curColMeta.colType) {\n\t\t\t\t\tcase ColMeta.COL_TYPE_BIT:\n\t\t\t\t\t\tvalue.setByte(curColMeta.colIndex, row.getByte(curColMeta.colIndex));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_INT:\n\t\t\t\t\tcase ColMeta.COL_TYPE_LONG:\n\t\t\t\t\tcase ColMeta.COL_TYPE_INT24:\n\t\t\t\t\t\tvalue.setInt(curColMeta.colIndex,\n\t\t\t\t\t\t\t\tBytesTools.getInt(row.getBinary(curColMeta.colIndex)));\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_SHORT:\n\t\t\t\t\t\tvalue.setShort(curColMeta.colIndex,\n\t\t\t\t\t\t\t\tBytesTools.getShort(row.getBinary(curColMeta.colIndex)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_LONGLONG:\n\t\t\t\t\t\tvalue.setLong(curColMeta.colIndex,\n\t\t\t\t\t\t\t\tBytesTools.getLong(row.getBinary(curColMeta.colIndex)));\n\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_FLOAT:\n\t\t\t\t\t\tvalue.setFloat(curColMeta.colIndex,\n\t\t\t\t\t\t\t\tBytesTools.getFloat(row.getBinary(curColMeta.colIndex)));\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_DOUBLE:\n\t\t\t\t\t\tvalue.setDouble(curColMeta.colIndex, BytesTools.getDouble(row.getBinary(curColMeta.colIndex)));\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_NEWDECIMAL:\n//\t\t\t\t\t\tvalue.setDouble(curColMeta.colIndex, BytesTools.getDouble(row.getBinary(curColMeta.colIndex)));\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex, \n\t\t\t\t\t\t\t\tnew BigDecimal(new String(row.getBinary(curColMeta.colIndex))));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex,\n\t\t\t\t\t\t\t\trow.getBinary(curColMeta.colIndex));\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}else {\n\t\t\t\tswitch(curColMeta.colType) {\n\t\t\t\t\tcase ColMeta.COL_TYPE_NEWDECIMAL:\n\t\t\t\t\t\tBigDecimal nullDecimal = null;\n\t\t\t\t\t\tunsafeRowWriter.write(curColMeta.colIndex, nullDecimal);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tvalue.setNullAt(curColMeta.colIndex);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\n\t\tvalue.setTotalSize(bufferHolder.totalSize());\n\t\treturn value;\n\t}\n\n\tpublic void addRow(UnsafeRow rowDataPkg) throws UnsupportedEncodingException {\n\t\tUnsafeRow key = getGroupKey(rowDataPkg);\n\t\tUnsafeRow value = getValue(rowDataPkg);\n\n\t\tif(aggregationMap.find(key)){\n\t\t\tUnsafeRow rs = aggregationMap.getAggregationBuffer(key);\n\t\t\taggregateRow(key,rs,value);\n\t\t}else {\n\t\t\taggregationMap.put(key,value);\n\t\t}\n\n\t\treturn;\n\t}\n\n\n\tprivate boolean isMergeAvg(){\n\n\t\tif (mergCols == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (MergeCol merg : mergCols) {\n\t\t\tif(merg.mergeType == MergeCol.MERGE_AVG) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate void aggregateRow(UnsafeRow key,UnsafeRow toRow, UnsafeRow newRow) throws UnsupportedEncodingException {\n\t\tif (mergCols == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (MergeCol merg : mergCols) {\n             if(merg.mergeType != MergeCol.MERGE_AVG && merg.colMeta !=null) {\n\t\t\t\t byte[] result = null;\n\t\t\t\t byte[] left = null;\n\t\t\t\t byte[] right = null;\n\t\t\t\t int type = merg.colMeta.colType;\n\t\t\t\t int index = merg.colMeta.colIndex;\n\t\t\t\t switch(type){\n\t\t\t\t\t case ColMeta.COL_TYPE_INT:\n\t\t\t\t\t case ColMeta.COL_TYPE_LONG:\n\t\t\t\t\t case ColMeta.COL_TYPE_INT24:\n\t\t\t\t\t\t if (!toRow.isNullAt(index)) {\n\t\t\t\t\t\t \tleft = BytesTools.int2Bytes(toRow.getInt(index));\n\t\t\t\t\t\t }\n\t\t\t\t\t\t if (!newRow.isNullAt(index)) {\n\t\t\t\t\t\t \tright = BytesTools.int2Bytes(newRow.getInt(index));\n\t\t\t\t\t\t }\n\t\t\t\t\t\t break;\n\t\t\t\t\t case ColMeta.COL_TYPE_SHORT:\n\t\t\t\t\t\t left = BytesTools.short2Bytes(toRow.getShort(index));\n\t\t\t\t\t\t right =BytesTools.short2Bytes(newRow.getShort(index));\n\t\t\t\t\t\t break;\n\t\t\t\t\t case ColMeta.COL_TYPE_LONGLONG:\n\t\t\t\t\t\t left = BytesTools.long2Bytes(toRow.getLong(index));\n\t\t\t\t\t\t right = BytesTools.long2Bytes(newRow.getLong(index));\n\t\t\t\t\t\t break;\n\t\t\t\t\t case ColMeta.COL_TYPE_FLOAT:\n\t\t\t\t\t\t left = BytesTools.float2Bytes(toRow.getFloat(index));\n\t\t\t\t\t\t right = BytesTools.float2Bytes(newRow.getFloat(index));\n\t\t\t\t\t\t break;\n\t\t\t\t\t case ColMeta.COL_TYPE_DOUBLE:\n\t\t\t\t\t\t left = BytesTools.double2Bytes(toRow.getDouble(index));\n\t\t\t\t\t\t right = BytesTools.double2Bytes(newRow.getDouble(index));\n\t\t\t\t\t\t break;\n\t\t\t\t\t case ColMeta.COL_TYPE_NEWDECIMAL:\n//\t\t\t\t\t\t left = BytesTools.double2Bytes(toRow.getDouble(index));\n//\t\t\t\t\t\t right = BytesTools.double2Bytes(newRow.getDouble(index));\n\t\t\t\t\t\t int scale = merg.colMeta.decimals;\n\t\t\t\t\t\t BigDecimal decimalLeft = toRow.getDecimal(index, scale);\n\t\t\t\t\t\t BigDecimal decimalRight = newRow.getDecimal(index, scale);\n\t\t\t\t\t\t left = decimalLeft == null ? null : decimalLeft.toString().getBytes();\n\t\t\t\t\t\t right = decimalRight == null ? null : decimalRight.toString().getBytes();\n\t\t\t\t\t\t break;\n\t\t\t\t\t case ColMeta.COL_TYPE_DATE:\n\t\t\t\t\t case ColMeta.COL_TYPE_TIMSTAMP:\n\t\t\t\t\t case ColMeta.COL_TYPE_TIME:\n\t\t\t\t\t case ColMeta.COL_TYPE_YEAR:\n\t\t\t\t\t case ColMeta.COL_TYPE_DATETIME:\n\t\t\t\t\t case ColMeta.COL_TYPE_NEWDATE:\n\t\t\t\t\t case ColMeta.COL_TYPE_BIT:\n\t\t\t\t\t case ColMeta.COL_TYPE_VAR_STRING:\n\t\t\t\t\t case ColMeta.COL_TYPE_STRING:\n\t\t\t\t\t case ColMeta.COL_TYPE_ENUM:\n\t\t\t\t\t case ColMeta.COL_TYPE_SET:\n\t\t\t\t\t\t left = toRow.getBinary(index);\n\t\t\t\t\t\t right = newRow.getBinary(index);\n\t\t\t\t\t\t break;\n\t\t\t\t\t default:\n\t\t\t\t\t\t break;\n\t\t\t\t }\n\n                 result = mertFields(left,right,type,merg.mergeType);\n\n\t\t\t\t if (result != null) {\n\t\t\t\t\t switch(type){\n\t\t\t\t\t \t case ColMeta.COL_TYPE_BIT:\n\t\t\t\t\t \t \ttoRow.setByte(index,result[0]);\n\t\t\t\t\t\t case ColMeta.COL_TYPE_INT:\n\t\t\t\t\t\t case ColMeta.COL_TYPE_LONG:\n\t\t\t\t\t\t case ColMeta.COL_TYPE_INT24:\n\t\t\t\t\t\t\ttoRow.setInt(index,BytesTools.getInt(result));\n\t\t\t\t\t\t\t break;\n\t\t\t\t\t\t case ColMeta.COL_TYPE_SHORT:\n\t\t\t\t\t\t\t toRow.setShort(index,BytesTools.getShort(result));\n\t\t\t\t\t\t\t break;\n\t\t\t\t\t\t case ColMeta.COL_TYPE_LONGLONG:\n\t\t\t\t\t\t\t toRow.setLong(index,BytesTools.getLong(result));\n\t\t\t\t\t\t\t break;\n\t\t\t\t\t\t case ColMeta.COL_TYPE_FLOAT:\n\t\t\t\t\t\t\t toRow.setFloat(index,BytesTools.getFloat(result));\n\t\t\t\t\t\t\t break;\n\t\t\t\t\t\t case ColMeta.COL_TYPE_DOUBLE:\n                             toRow.setDouble(index,BytesTools.getDouble(result));\n\t\t\t\t\t\t\t break;\n\t\t\t\t\t\t case ColMeta.COL_TYPE_NEWDECIMAL:\n//                           toRow.setDouble(index,BytesTools.getDouble(result));\n\t\t\t\t\t\t\t toRow.updateDecimal(index, new BigDecimal(new String(result)));\n\t\t\t\t\t\t\t break;\n\t\t\t\t\t\t /**\n\t\t\t\t\t\t  *TODO UnsafeFixedWidthAggregationMap 中存放\n\t\t\t\t\t\t  * UnsafeRow时，非数值类型的列不可更改其值，\n\t\t\t\t\t\t  * 为了统一处理聚合函数这块\n\t\t\t\t\t\t  * 做max或者min聚合时候，目前解决方法\n\t\t\t\t\t\t  * 先free原来 UnsafeFixedWidthAggregationMap对象。\n\t\t\t\t\t\t  * 然后重新创建一个UnsafeFixedWidthAggregationMap对象\n\t\t\t\t\t\t  * 然后存放最新的max或者min值作为下次比较。\n\t\t\t\t\t\t  **/\n\t\t\t\t\t\t case ColMeta.COL_TYPE_DATE:\n\t\t\t\t\t\t case ColMeta.COL_TYPE_TIMSTAMP:\n\t\t\t\t\t\t case ColMeta.COL_TYPE_TIME:\n\t\t\t\t\t\t case ColMeta.COL_TYPE_YEAR:\n\t\t\t\t\t\t case ColMeta.COL_TYPE_DATETIME:\n\t\t\t\t\t\t case ColMeta.COL_TYPE_NEWDATE:\n\t\t\t\t\t\t case ColMeta.COL_TYPE_VAR_STRING:\n\t\t\t\t\t\t case ColMeta.COL_TYPE_STRING:\n\t\t\t\t\t\t case ColMeta.COL_TYPE_ENUM:\n\t\t\t\t\t\t case ColMeta.COL_TYPE_SET:\n\t\t\t\t\t\t\t aggregationMap.free();\n\t\t\t\t\t\t\t DataNodeMemoryManager dataNodeMemoryManager =\n\t\t\t\t\t\t\t\t\t new DataNodeMemoryManager(memoryManager,Thread.currentThread().getId());\n\t\t\t\t\t\t\t aggregationMap = new UnsafeFixedWidthAggregationMap(\n\t\t\t\t\t\t\t\t\t emptyAggregationBuffer,\n\t\t\t\t\t\t\t\t\t aggBufferSchema,\n\t\t\t\t\t\t\t\t\t groupKeySchema,\n\t\t\t\t\t\t\t\t\t dataNodeMemoryManager,\n\t\t\t\t\t\t\t\t\t 1024,\n\t\t\t\t\t\t\t\t\t conf.getSizeAsBytes(\"mycat.buffer.pageSize\", \"32k\"),\n\t\t\t\t\t\t\t\t\t false);\n\t\t\t\t\t\t\t UnsafeRow unsafeRow = new UnsafeRow(toRow.numFields());\n\t\t\t\t\t\t\t bufferHolder = new BufferHolder(unsafeRow, 0);\n\t\t\t\t\t\t\t unsafeRowWriter = new UnsafeRowWriter(bufferHolder, toRow.numFields());\n\t\t\t\t\t\t\t bufferHolder.reset();\n\t\t\t\t\t\t\t for (int i = 0; i < toRow.numFields(); i++) {\n\n\t\t\t\t\t\t\t\t if (i == index) {\n\t\t\t\t\t\t\t\t\t unsafeRowWriter.write(i,result);\n\t\t\t\t\t\t\t\t } else if (!toRow.isNullAt(i)) {\n\t\t\t\t\t\t\t\t\t unsafeRowWriter.write(i, toRow.getBinary(i));\n\t\t\t\t\t\t\t\t } else if (toRow.isNullAt(i)){\n\t\t\t\t\t\t\t\t\t unsafeRow.setNullAt(i);\n\t\t\t\t\t\t\t\t }\n\t\t\t\t\t\t\t }\n\t\t\t\t\t\t\t unsafeRow.setTotalSize(bufferHolder.totalSize());\n\t\t\t\t\t\t\t aggregationMap.put(key, unsafeRow);\n\t\t\t\t\t\t\t toRow = unsafeRow;\n\t\t\t\t\t\t\t break;\n\t\t\t\t\t\t default:\n\t\t\t\t\t\t\t break;\n\t\t\t\t\t }\n                 }\n             }\n\t\t}\n    }\n\n\tprivate void mergAvg(UnsafeRow toRow) throws UnsupportedEncodingException {\n\n\t\tif (mergCols == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (MergeCol merg : mergCols) {\n\t\t\tif(merg.mergeType==MergeCol.MERGE_AVG) {\n\t\t\t\tbyte[] result = null;\n\t\t\t\tbyte[] avgSum = null;\n\t\t\t\tbyte[] avgCount = null;\n\n\t\t\t\tint type = merg.colMeta.colType;\n\t\t\t\tint avgSumIndex = merg.colMeta.avgSumIndex;\n\t\t\t\tint avgCountIndex = merg.colMeta.avgCountIndex;\n\n\t\t\t\tswitch(type){\n\t\t\t\t\tcase ColMeta.COL_TYPE_BIT:\n\t\t\t\t\t\tavgSum = BytesTools.toBytes(toRow.getByte(avgSumIndex));\n\t\t\t\t\t\tavgCount = BytesTools.toBytes(toRow.getLong(avgCountIndex));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_INT:\n\t\t\t\t\tcase ColMeta.COL_TYPE_LONG:\n\t\t\t\t\tcase ColMeta.COL_TYPE_INT24:\n\t\t\t\t\t\tavgSum = BytesTools.int2Bytes(toRow.getInt(avgSumIndex));\n\t\t\t\t\t\tavgCount = BytesTools.long2Bytes(toRow.getLong(avgCountIndex));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_SHORT:\n\t\t\t\t\t\tavgSum =BytesTools.short2Bytes(toRow.getShort(avgSumIndex));\n\t\t\t\t\t\tavgCount = BytesTools.long2Bytes(toRow.getLong(avgCountIndex));\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase ColMeta.COL_TYPE_LONGLONG:\n\t\t\t\t\t\tavgSum = BytesTools.long2Bytes(toRow.getLong(avgSumIndex));\n\t\t\t\t\t\tavgCount = BytesTools.long2Bytes(toRow.getLong(avgCountIndex));\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_FLOAT:\n\t\t\t\t\t\tavgSum = BytesTools.float2Bytes(toRow.getFloat(avgSumIndex));\n\t\t\t\t\t\tavgCount = BytesTools.long2Bytes(toRow.getLong(avgCountIndex));\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_DOUBLE:\n\t\t\t\t\t\tavgSum = BytesTools.double2Bytes(toRow.getDouble(avgSumIndex));\n\t\t\t\t\t\tavgCount = BytesTools.long2Bytes(toRow.getLong(avgCountIndex));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ColMeta.COL_TYPE_NEWDECIMAL:\n//\t\t\t\t\t\tavgSum = BytesTools.double2Bytes(toRow.getDouble(avgSumIndex));\n//\t\t\t\t\t\tavgCount = BytesTools.long2Bytes(toRow.getLong(avgCountIndex));\n\t\t\t\t\t\tint scale = merg.colMeta.decimals;\n\t\t\t\t\t\tBigDecimal sumDecimal = toRow.getDecimal(avgSumIndex, scale);\n\t\t\t\t\t\tavgSum = sumDecimal == null ? null : sumDecimal.toString().getBytes();\n\t\t\t\t\t\tavgCount = BytesTools.long2Bytes(toRow.getLong(avgCountIndex));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tresult = mertFields(avgSum,avgCount,merg.colMeta.colType,merg.mergeType);\n\n\t\t\t\tif (result != null) {\n                    switch(type){\n                    \tcase ColMeta.COL_TYPE_BIT:\n                    \t\ttoRow.setByte(avgSumIndex,result[0]);\n\t\t\t\t\t\t\tbreak;\n                        case ColMeta.COL_TYPE_INT:\n\t\t\t\t\t\tcase ColMeta.COL_TYPE_LONG:\n\t\t\t\t\t\tcase ColMeta.COL_TYPE_INT24:\n                            toRow.setInt(avgSumIndex,BytesTools.getInt(result));\n                            break;\n                        case ColMeta.COL_TYPE_SHORT:\n                            toRow.setShort(avgSumIndex,BytesTools.getShort(result));\n                            break;\n                        case ColMeta.COL_TYPE_LONGLONG:\n                            toRow.setLong(avgSumIndex,BytesTools.getLong(result));\n                            break;\n                        case ColMeta.COL_TYPE_FLOAT:\n                            toRow.setFloat(avgSumIndex,BytesTools.getFloat(result));\n                            break;\n                        case ColMeta.COL_TYPE_DOUBLE:\n                            toRow.setDouble(avgSumIndex,ByteUtil.getDouble(result));\n                            break;\n                        case ColMeta.COL_TYPE_NEWDECIMAL:\n//                          toRow.setDouble(avgSumIndex,ByteUtil.getDouble(result));\n                      \ttoRow.updateDecimal(avgSumIndex, new BigDecimal(new String(result)));\n                          break;\n                        default:\n                            break;\n                    }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate byte[] mertFields(byte[] bs, byte[] bs2, int colType, int mergeType) throws UnsupportedEncodingException {\n\n\t\tif(bs2==null || bs2.length==0) {\n\t\t\treturn bs;\n\t\t}else if(bs==null || bs.length==0) {\n\t\t\treturn bs2;\n\t\t}\n\n\t\tswitch (mergeType) {\n\t\t\tcase MergeCol.MERGE_SUM:\n\t\t\t\tif (colType == ColMeta.COL_TYPE_DOUBLE\n\t\t\t\t\t|| colType == ColMeta.COL_TYPE_FLOAT){\n\t\t\t\t\tdouble value = BytesTools.getDouble(bs) +\n\t\t\t\t\t\t\tBytesTools.getDouble(bs2);\n\n\t\t\t\t\treturn BytesTools.double2Bytes(value);\n\t\t\t\t} else if(colType == ColMeta.COL_TYPE_NEWDECIMAL\n\t\t\t\t\t\t|| colType == ColMeta.COL_TYPE_DECIMAL) {\n\t\t\t\t\tBigDecimal decimal = new BigDecimal(new String(bs));\n\t\t\t\t\tdecimal = decimal.add(new BigDecimal(new String(bs2)));\n\t\t\t\t\treturn decimal.toString().getBytes();\n\t\t\t\t}\n\n\n\t\t\tcase MergeCol.MERGE_COUNT: {\n\t\t\t\tlong s1 = BytesTools.getLong(bs);\n\t\t\t\tlong s2 = BytesTools.getLong(bs2);\n\t\t\t\tlong total = s1 + s2;\n\t\t\t\treturn BytesTools.long2Bytes(total);\n\t\t\t}\n\n\t\t\tcase MergeCol.MERGE_MAX: {\n\t\t\t\tint compare = ByteUtil.compareNumberByte(bs,bs2);\n\t\t\t\treturn (compare > 0) ? bs : bs2;\n\t\t\t}\n\n\t\t\tcase MergeCol.MERGE_MIN: {\n\t\t\t\tint compare = ByteUtil.compareNumberByte(bs,bs2);\n\t\t\t\treturn (compare > 0) ? bs2 : bs;\n\n\t\t\t}\n\t\t\tcase MergeCol.MERGE_AVG: {\n\t\t\t\t/**\n\t\t\t\t * 元素总个数\n\t\t\t\t */\n\t\t\t\tlong count = BytesTools.getLong(bs2);\n\t\t\t\tif (colType == ColMeta.COL_TYPE_DOUBLE\n\t\t\t\t\t\t|| colType == ColMeta.COL_TYPE_FLOAT) {\n\t\t\t\t\t/**\n\t\t\t\t\t * 数值总和\n\t\t\t\t\t */\n\t\t\t\t\tdouble sum = BytesTools.getDouble(bs);\n\t\t\t\t\tdouble value = sum / count;\n\t\t\t\t\treturn BytesTools.double2Bytes(value);\n\t\t\t\t} else if(colType == ColMeta.COL_TYPE_NEWDECIMAL\n\t\t\t\t\t\t|| colType == ColMeta.COL_TYPE_DECIMAL){\n\t\t\t\t\tBigDecimal sum = new BigDecimal(new String(bs));\n\t\t\t\t\t// AVG计算时候小数点精度扩展4, 并且四舍五入\n\t\t\t\t\tBigDecimal avg = sum.divide(new BigDecimal(count), sum.scale() + 4, RoundingMode.HALF_UP);\n\t\t\t\t\treturn avg.toString().getBytes();\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic void  free(){\n\t\tif(aggregationMap != null)\n\t\taggregationMap.free();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/model/NodeRowDataPacket.java",
    "content": "package io.mycat.sqlengine.mpp.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.mycat.net.mysql.RowDataPacket;\nimport io.mycat.route.RouteResultsetNode;\n\npublic class NodeRowDataPacket {\n\n\tprivate RouteResultsetNode node;\n\tprivate long trimTotal = 0;\n\t\n\tprivate int trimSize = 0;\n\n\tprivate List<RangRowDataPacket> trimRangRDPacketList = new ArrayList<RangRowDataPacket>();\n\tprivate List<RangRowDataPacket> rangRDPacketList = new ArrayList<RangRowDataPacket>();\n\t\n\tpublic NodeRowDataPacket(RouteResultsetNode node, int trimSize) {\n\t\tthis.node = node;\n\t\tthis.trimSize = trimSize;\n\t}\n\t\n\tpublic void newRang() {\n\t\tRangRowDataPacket rangPacket = new RangRowDataPacket();\n\t\trangRDPacketList.add(rangPacket);\n\t}\n\t\n\tpublic long loadTotal() {\n\t\treturn this.loadTrimTotal() + this.loadNotTrimTotal();\n\t}\n\t\n\tpublic long loadTrimTotal() {\n\t\tthis.trimTotal = 0;\n\t\tfor (RangRowDataPacket packet : trimRangRDPacketList) {\n\t\t\tif (packet.isTrim()) {\n\t\t\t\tthis.trimTotal += packet.allSize();\n\t\t\t}\n\t\t}\n\t\treturn this.trimTotal;\n\t}\n\t\n\tpublic long loadNotTrimTotal() {\n\t\tlong total = 0;\n\t\tfor (RangRowDataPacket packet : rangRDPacketList) {\n\t\t\ttotal += packet.allSize();\n\t\t}\n\t\treturn total;\n\t}\n\t\n\tpublic void moveToTrim() {\n\t\tRangRowDataPacket head = this.loadHeadPacket();\n        if (head != null && this.rangRDPacketList.remove(head)) {\n            this.trimRangRDPacketList.add(head);\n            if (head.allSize() == this.trimSize) {\n                head.leftHeadTail();\n            }\n        }\n\t}\n\t\n\tpublic void moveHeadTail3ToTrim() {\n\t\tif (this.rangRDPacketList.size() >= 3) {\n\t\t\tint m = 0;\n\t\t\twhile ((m = this.rangRDPacketList.size()) > 3) {\n\t\t\t\tRangRowDataPacket packet = this.rangRDPacketList.remove(0);\n\t\t\t\tif (packet.allSize() == this.trimSize) {\n\t\t\t\t\tpacket.leftHeadTail();\n\t\t\t\t}\n\t\t\t\taddTrimWithCombine(packet);\n\t\t\t}\n\t\t\t\n\t\t}\n\t}\n\t\n\tprivate void addTrimWithCombine(RangRowDataPacket packet) {\n\t\tif (packet.allSize() == this.trimSize) {\n\t\t\tif (this.trimRangRDPacketList.isEmpty()) {\n\t\t\t\tthis.trimRangRDPacketList.add(packet);\n\t\t\t} else {\n\t\t\t\tint last = this.trimRangRDPacketList.size() - 1;\n\t\t\t\tRangRowDataPacket lastPacket = \n\t\t\t\t\tthis.trimRangRDPacketList.get(last);\n\t\t\t\tif (lastPacket.isTrim()) {\n\t\t\t\t\tlastPacket.combine(packet);\n\t\t\t\t} else {\n\t\t\t\t\t//异常\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic void moveAllToTrim() {\n\t\tint m = 0;\n\t\twhile ((m = this.rangRDPacketList.size()) > 0) {\n\t\t\tRangRowDataPacket packet = this.rangRDPacketList.remove(0);\n\t\t\tif (packet.getRowDataPacketList().size() == this.trimSize) {\n\t\t\t\tpacket.leftHeadTail();\n\t\t\t}\n\t\t\taddTrimWithCombine(packet);\n\t\t}\n\t}\n\t\n\tpublic void addPacket(RowDataPacket packet) {\n\t\tRangRowDataPacket rangPacket = rangRDPacketList.get(rangRDPacketList.size() - 1);\n\t\trangPacket.appendPacket(packet);\n\t}\n\t\n\tpublic RouteResultsetNode getNode() {\n\t\treturn node;\n\t}\n\t\n\tpublic List<RowDataPacket> loadData() {\n\t\tList<RowDataPacket> result = new ArrayList<RowDataPacket>();\n\t\tfor (RangRowDataPacket packet : rangRDPacketList) {\n\t\t\tresult.addAll(packet.getRowDataPacketList());\n\t\t}\n\t\tfor (RangRowDataPacket packet : trimRangRDPacketList) {\n\t\t\tif (!packet.isTrim()) {\n\t\t\t\tresult.addAll(packet.getRowDataPacketList());\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\t\n\tpublic RangRowDataPacket loadHeadPacket() {\n\t\tif (rangRDPacketList != null && !rangRDPacketList.isEmpty()) {\n\t\t\treturn rangRDPacketList.get(0);\n\t\t}\n\t\treturn null;\n\t}\n\t\n\tpublic RangRowDataPacket loadTailPacket() {\n\t\treturn this.loadTailPacket(1);\n\t}\n\t\n\tpublic RangRowDataPacket loadTailPacket(int tailIndex) {\n\t\tint size = rangRDPacketList.size() - tailIndex;\n\t\tif (size >= 0) {\n\t\t\treturn rangRDPacketList.get(size);\n\t\t}\n\t\treturn null;\n\t}\n\t\n}"
  },
  {
    "path": "src/main/java/io/mycat/sqlengine/mpp/model/RangRowDataPacket.java",
    "content": "package io.mycat.sqlengine.mpp.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.mycat.net.mysql.RowDataPacket;\n\npublic class RangRowDataPacket {\n\tpublic static final int DATA_TYPE_ALL = 100;\n\tpublic static final int DATA_TYPE_TRIM = 200;\n\tprivate int dataType = DATA_TYPE_ALL;\n\tprivate List<RowDataPacket> rowDataPacketList = new ArrayList<RowDataPacket>();\n\t\n\tprivate int trimCount = 0;\n\tpublic int getTrimCount() {\n\t\treturn trimCount;\n\t}\n\n\tpublic void appendPacket(List<RowDataPacket> packetList) {\n\t\tthis.rowDataPacketList.addAll(packetList);\n\t}\n\t\n\tpublic void appendPacket(RowDataPacket packet) {\n\t\tthis.rowDataPacketList.add(packet);\n\t}\n\t\n\tpublic boolean isTrim() {\n\t\treturn dataType == DATA_TYPE_TRIM ? true : false;\n\t}\n\t\n\tpublic void leftHeadTail() {\n\t\twhile (this.rowDataPacketList.size() > 2) {\n\t\t\tthis.rowDataPacketList.remove(1);\n\t\t\ttrimCount++;\n\t\t}\n\t\tdataType = DATA_TYPE_TRIM;\n\t}\n\t\n\tpublic int allSize() {\n\t\tif (dataType == DATA_TYPE_TRIM) {\n\t\t\treturn trimCount + rowDataPacketList.size();\n\t\t} else {\n\t\t\treturn rowDataPacketList.size();\n\t\t}\n\t}\n\t\n\tpublic void combine(RangRowDataPacket rowData) {\n\t\tif (dataType == DATA_TYPE_TRIM) {\n\t\t\tif (this.rowDataPacketList.isEmpty()) {\n\t\t\t\tthis.trimCount = rowData.getTrimCount();\n\t\t\t\tthis.rowDataPacketList.addAll(rowData.getRowDataPacketList());\n\t\t\t} else {\n\t\t\t\tif (rowData.allSize() == 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.rowDataPacketList.remove(1);\n\t\t\t\t\n\t\t\t\tif (rowData.allSize() == 1) {\n\t\t\t\t\tthis.trimCount += 1 + rowData.getTrimCount();\n\t\t\t\t\tthis.rowDataPacketList.add(rowData.getHead());\n\t\t\t\t} else if (rowData.allSize() >= 2) {\n\t\t\t\t\tthis.trimCount += 2 + rowData.getTrimCount();\n\t\t\t\t\tthis.rowDataPacketList.add(rowData.getTail());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\t\n\tpublic List<RowDataPacket> getRowDataPacketList() {\n\t\treturn rowDataPacketList;\n\t}\n\t\n\tpublic RowDataPacket getHead() {\n\t\tif (this.rowDataPacketList.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn this.rowDataPacketList.get(0);\n\t}\n\t\n\tpublic RowDataPacket getTail() {\n\t\tif (this.rowDataPacketList.size() < 2) {\n\t\t\treturn null;\n\t\t}\n\t\treturn this.rowDataPacketList.get(this.rowDataPacketList.size()-1);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/statistic/CommandCount.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.statistic;\n\n/**\n * @author mycat\n */\npublic class CommandCount {\n\n    private long initDB;\n    private long query;\n    private long stmtPrepare;\n    private long stmtSendLongData;\n    private long stmtReset;\n    private long stmtExecute;\n    private long stmtClose;\n    private long ping;\n    private long kill;\n    private long quit;\n    private long heartbeat;\n\tprivate long setOption;\n    private long other;\n    public CommandCount(){\n\n    }\n    public void doInitDB() {\n        ++initDB;\n    }\n\n    public long initDBCount() {\n        return initDB;\n    }\n\n    public void doQuery() {\n        ++query;\n    }\n\n    public long queryCount() {\n        return query;\n    }\n\n    public void doStmtPrepare() {\n        ++stmtPrepare;\n    }\n\n    public long stmtPrepareCount() {\n        return stmtPrepare;\n    }\n    \n    public void doStmtSendLongData() {\n    \t++stmtSendLongData;\n    }\n    \n    public long stmtSendLongDataCount() {\n    \treturn stmtSendLongData;\n    }\n    \n    public void doStmtReset() {\n    \t++stmtReset;\n    }\n    \n    public long stmtResetCount() {\n    \treturn stmtReset;\n    }\n\n    public void doStmtExecute() {\n        ++stmtExecute;\n    }\n\n    public long stmtExecuteCount() {\n        return stmtExecute;\n    }\n\n    public void doStmtClose() {\n        ++stmtClose;\n    }\n\n    public long stmtCloseCount() {\n        return stmtClose;\n    }\n\n    public void doPing() {\n        ++ping;\n    }\n\n    public long pingCount() {\n        return ping;\n    }\n\n    public void doKill() {\n        ++kill;\n    }\n\n    public long killCount() {\n        return kill;\n    }\n\n    public void doQuit() {\n        ++quit;\n    }\n\n    public long quitCount() {\n        return quit;\n    }\n\n    public void doOther() {\n        ++other;\n    }\n\n    public long heartbeat() {\n        return heartbeat;\n    }\n\n    public void doHeartbeat() {\n        ++heartbeat;\n    }\n\n\tpublic void doSetOption() {\n\t\t++setOption;\n\t}\n\n    public long otherCount() {\n        return other;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/statistic/DataSourceSyncRecorder.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.statistic;\n\nimport java.text.SimpleDateFormat;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger; import org.slf4j.LoggerFactory;\n\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.util.TimeUtil;\n\n/**\n * 记录最近3个时段的平均响应时间，默认1，10，30分钟。\n * \n * @author songwie\n */\npublic class                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          DataSourceSyncRecorder {\n\n    private Map<String, String> records;\n    private final List<Record> asynRecords;//value,time\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(\"DataSourceSyncRecorder\");\n\n    \n    private static final long SWAP_TIME = 24 * 60 * 60 * 1000L;\n    \n    //日期处理\n    private static final SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n    private int switchType = 2;\n\n    public DataSourceSyncRecorder() {\n        this.records = new HashMap<String, String>()  ;\n        this.asynRecords = new LinkedList<Record>();\n    }\n\n    public String get() {\n         return records.toString();\n    }\n\n    public void set(Map<String, String> resultResult,int switchType) {\n    \ttry{\n    \t\tlong time = TimeUtil.currentTimeMillis();\n            this.switchType = switchType;\n\n            remove(time);\n\n            if (resultResult!=null && !resultResult.isEmpty()) {\n            \tthis.records = resultResult;\n            \tif(switchType==DataHostConfig.SYN_STATUS_SWITCH_DS){  //slave\n            \t\tString sencords = resultResult.get(\"Seconds_Behind_Master\");\n            \t\tlong Seconds_Behind_Master = -1;\n            \t\tif(sencords!=null){\n                \t\tSeconds_Behind_Master = Long.parseLong(sencords);\n            \t\t} \n            \t\tthis.asynRecords.add(new Record(TimeUtil.currentTimeMillis(),Seconds_Behind_Master));\n            \t}\n                if(switchType==DataHostConfig.CLUSTER_STATUS_SWITCH_DS){//cluster\n                \tdouble wsrep_local_recv_queue_avg = Double.valueOf(resultResult.get(\"wsrep_local_recv_queue_avg\"));\n            \t\tthis.asynRecords.add(new Record(TimeUtil.currentTimeMillis(),wsrep_local_recv_queue_avg));\n            \t}\n            \t\n                return;\n            }\n    \t}catch(Exception e){ \n    \t\tLOGGER.error(\"record DataSourceSyncRecorder error \" + e.getMessage());\n    \t}\n        \n    }\n\n    /**\n     * 删除超过统计时间段的数据\n     */\n    private void remove(long time) {\n        final List<Record> recordsAll = this.asynRecords;\n        while (recordsAll.size() > 0) {\n            Record record = recordsAll.get(0);\n            if (time >= record.time + SWAP_TIME) {\n            \trecordsAll.remove(0);\n            } else {\n                break;\n            }\n        }\n    }\n\n    public int getSwitchType() {\n\t\treturn this.switchType;\n\t}\n\tpublic void setSwitchType(int switchType) {\n\t\tthis.switchType = switchType;\n\t}\n\tpublic Map<String, String> getRecords() {\n\t\treturn this.records;\n\t}\n\tpublic List<Record> getAsynRecords() {\n\t\treturn this.asynRecords;\n\t}\n\tpublic static SimpleDateFormat getSdf() {\n\t\treturn sdf;\n\t}\n\n\t/**\n     * @author mycat\n     */\n    public static class Record {\n    \tprivate Object value;\n    \tprivate long time;\n\n        Record(long time, Object value) {\n            this.time = time;\n            this.value = value;\n        }\n\t\tpublic Object getValue() {\n\t\t\treturn this.value;\n\t\t}\n\t\tpublic void setValue(Object value) {\n\t\t\tthis.value = value;\n\t\t}\n\t\tpublic long getTime() {\n\t\t\treturn this.time;\n\t\t}\n\t\tpublic void setTime(long time) {\n\t\t\tthis.time = time;\n\t\t}\n        \n        \n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/statistic/HeartbeatRecorder.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.statistic;\n\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentLinkedQueue;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.util.TimeUtil;\n\n/**\n * 记录最近3个时段的平均响应时间，默认1，10，30分钟。\n * \n * @author mycat\n */\npublic class HeartbeatRecorder {\n\n    private static final int MAX_RECORD_SIZE = 256;\n    private static final long AVG1_TIME = 60 * 1000L;\n    private static final long AVG2_TIME = 10 * 60 * 1000L;\n    private static final long AVG3_TIME = 30 * 60 * 1000L;\n    private static final long SWAP_TIME = 24 * 60 * 60 * 1000L;\n\n    private long avg1;\n    private long avg2;\n    private long avg3;\n    private final Queue<Record> records;\n    private final Queue<Record> recordsAll;\n    \n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(\"DataSourceSyncRecorder\");\n\n    public HeartbeatRecorder() {\n        this.records = new ConcurrentLinkedQueue<Record>();\n        this.recordsAll = new ConcurrentLinkedQueue<Record>();\n    }\n\n    public String get() {\n        return new StringBuilder().append(avg1).append(',').append(avg2).append(',').append(avg3).toString();\n    }\n\n    public void set(long value) {\n    \ttry{\n    \t\tlong time = TimeUtil.currentTimeMillis();\n            if (value < 0) {\n                recordsAll.offer(new Record(0, time));\n                return;\n            }\n            remove(time);\n            int size = records.size();\n            if (size == 0) {\n                records.offer(new Record(value, time));\n                avg1 = avg2 = avg3 = value;\n                return;\n            }\n            if (size >= MAX_RECORD_SIZE) {\n                records.poll();\n            }\n            records.offer(new Record(value, time));\n            recordsAll.offer(new Record(value, time));\n            calculate(time);\n    \t}catch(Exception e){ \n    \t\tLOGGER.error(\"record HeartbeatRecorder error \" ,e);\n    \t}\n    }\n\n    /**\n     * 删除超过统计时间段的数据\n     */\n    private void remove(long time) {\n        final Queue<Record> records = this.records;\n        while (records.size() > 0) {\n            Record record = records.peek();\n            if (time >= record.time + AVG3_TIME) {\n                records.poll();\n            } else {\n                break;\n            }\n        }\n        \n        final Queue<Record> recordsAll = this.recordsAll;\n        while (recordsAll.size() > 0) {\n            Record record = recordsAll.peek();\n            if (time >= record.time + SWAP_TIME) {\n            \trecordsAll.poll();\n            } else {\n                break;\n            }\n        }\n    }\n\n    /**\n     * 计算记录的统计数据\n     */\n    private void calculate(long time) {\n        long v1 = 0L, v2 = 0L, v3 = 0L;\n        int c1 = 0, c2 = 0, c3 = 0;\n        for (Record record : records) {\n            long t = time - record.time;\n            if (t <= AVG1_TIME) {\n                v1 += record.value;\n                ++c1;\n            }\n            if (t <= AVG2_TIME) {\n                v2 += record.value;\n                ++c2;\n            }\n            if (t <= AVG3_TIME) {\n                v3 += record.value;\n                ++c3;\n            }\n        }\n        avg1 = (v1 / c1);\n        avg2 = (v2 / c2);\n        avg3 = (v3 / c3);\n    }\n\n    public Queue<Record> getRecordsAll() {\n\t\treturn this.recordsAll;\n\t}\n\n\t/**\n     * @author mycat\n     */\n    public static class Record {\n    \tprivate long value;\n    \tprivate long time;\n\n        Record(long value, long time) {\n            this.value = value;\n            this.time = time;\n        }\n\t\tpublic long getValue() {\n\t\t\treturn this.value;\n\t\t}\n\t\tpublic void setValue(long value) {\n\t\t\tthis.value = value;\n\t\t}\n\t\tpublic long getTime() {\n\t\t\treturn this.time;\n\t\t}\n\t\tpublic void setTime(long time) {\n\t\t\tthis.time = time;\n\t\t}\n        \n        \n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/statistic/SQLRecord.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.statistic;\n\n/**\n * @author mycat\n */\npublic final class SQLRecord implements Comparable<SQLRecord> {\n\n    public String host;\n    public String schema;\n    public String statement;\n    public long startTime;\n    public long executeTime;\n    public String dataNode;\n    public int dataNodeIndex;\n\n    @Override\n    public int compareTo(SQLRecord o) {\n        //执行时间从大到小\n        long para =  o.executeTime - executeTime;\n        //开始时间从大到小\n        return (int) (para == 0 ? (o.startTime - startTime) : para );\n    }\n\n    @Override\n    public boolean equals(Object arg0) {\n        return super.equals(arg0);\n    }\n\n    @Override\n    public int hashCode() {\n        // TODO Auto-generated method stub\n        return super.hashCode();\n    }\n    \n    \n\n}"
  },
  {
    "path": "src/main/java/io/mycat/statistic/SQLRecorder.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.statistic;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * SQL统计排序记录器\n *\n * @author mycat\n */\npublic final class SQLRecorder {\n\n    private final int count;\n    SortedSet<SQLRecord> records;\n\n    public SQLRecorder(int count) {\n        this.count = count;\n        this.records = new ConcurrentSkipListSet<>();\n    }\n\n    public List<SQLRecord> getRecords() {\n        List<SQLRecord> keyList = new ArrayList<SQLRecord>(records);\n        return keyList;\n    }\n\n\n    public void add(SQLRecord record) {\n        records.add(record);\n    }\n\n    public void clear() {\n        records.clear();\n    }\n\n    public void recycle(){\n        if(records.size() > count){\n            SortedSet<SQLRecord> records2 = new ConcurrentSkipListSet<>();\n            List<SQLRecord> keyList = new ArrayList<SQLRecord>(records);\n            int i = 0;\n            for(SQLRecord key : keyList){\n                if(i == count) {\n                    break;\n                }\n                records2.add(key);\n                i++;\n            }\n            records = records2;\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/Histogram.java",
    "content": "package io.mycat.statistic.stat;\n\nimport java.util.concurrent.atomic.AtomicLongArray;\n\npublic class Histogram {\n\n    private final long[] ranges;\n    private final AtomicLongArray rangeCounters;\n\n    public Histogram(long... ranges){\n        this.ranges = ranges;\n        this.rangeCounters = new AtomicLongArray(ranges.length);\n    }\n\n    public void reset() {\n        for (int i = 0; i < rangeCounters.length(); i++) {\n            rangeCounters.set(i, 0);\n        }\n    }\n\n    public void record(long range) {\n        int index = rangeCounters.length();\n        for (int i = 0; i < ranges.length; i++) {\n            if (range == ranges[i]) {\n                index = i;\n                break;\n            }\n        }\n\n        rangeCounters.incrementAndGet(index);\n    }\n\n    public long get(int index) {\n        return rangeCounters.get(index);\n    }\n\n    public long[] toArray() {\n        long[] array = new long[rangeCounters.length()];\n        for (int i = 0; i < rangeCounters.length(); i++) {\n            array[i] = rangeCounters.get(i);\n        }\n        return array;\n    }\n\n    public long[] toArrayAndReset() {\n        long[] array = new long[rangeCounters.length()];\n        for (int i = 0; i < rangeCounters.length(); i++) {\n            array[i] = rangeCounters.getAndSet(i, 0);\n        }\n\n        return array;\n    }\n\n    public long[] getRanges() {\n        return ranges;\n    }\n\n    public long getValue(int index) {\n        return rangeCounters.get(index);\n    }\n\n    public long getSum() {\n        long sum = 0;\n        for (int i = 0; i < rangeCounters.length(); ++i) {\n            sum += rangeCounters.get(i);\n        }\n        return sum;\n    }\n\n    public String toString() {\n        StringBuilder buf = new StringBuilder();\n        buf.append('[');\n        for (int i = 0; i < rangeCounters.length(); i++) {\n            if (i != 0) {\n                buf.append(\", \");\n            }\n            buf.append(rangeCounters.get(i));\n        }\n        buf.append(']');\n        return buf.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/HostStatAnalyzer.java",
    "content": "package io.mycat.statistic.stat;\r\n\r\n/**\r\n * 前端SQL客户端主机 的访问统计\r\n * \r\n * @author zhuam\r\n *\r\n */\r\npublic class HostStatAnalyzer implements QueryResultListener {\r\n\r\n\t@Override\r\n\tpublic void onQueryResult(QueryResult query) {\r\n\t\t\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/QueryConditionAnalyzer.java",
    "content": "package io.mycat.statistic.stat;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\nimport java.util.concurrent.atomic.AtomicLong;\r\nimport java.util.concurrent.locks.ReentrantLock;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport com.alibaba.druid.sql.ast.SQLStatement;\r\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\r\nimport com.alibaba.druid.stat.TableStat.Condition;\r\n\r\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\r\nimport io.mycat.server.parser.ServerParse;\r\n\r\n/**\r\n * 特定 SQL 查询条件的统计分析\r\n * --------------------------------------------------\r\n * \r\n * 例:\r\n * SELECT * FROM v1user Where userName=? AND cityName =?\r\n * SELECT * FROM v1user Where userName=?\r\n * SELECT * FROM v1user Where userName=? AND age > 20\r\n * \r\n * SELECT * FROM v1user Where userName = \"张三\" AND cityName = \"北京\";\r\n * SELECT * FROM v1user Where userName = \"李四\" \r\n * SELECT * FROM v1user Where userName = \"张三\" AND age > 20\r\n * \r\n * 现在我们希望知道DB 中 业务比较关注的 userName 有哪些，次数是多少, 怎么处理哩，如下\r\n * \r\n * 设置： 表名&条件列  ( v1user&userName ) 即可，取消请设置 NULL\r\n * \r\n * @author zhuam\r\n *\r\n */\r\npublic class QueryConditionAnalyzer implements QueryResultListener {\r\n\tprivate final static long MAX_QUERY_MAP_SIZE = 100000;\r\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(QueryConditionAnalyzer.class);\r\n\t\r\n\tprivate String tableName = null;\r\n\tprivate String columnName = null;\r\n\t\r\n\t// column value -> count\r\n//\tprivate final HashMap<Object, Long> map = new HashMap<Object, Long>();\r\n\tprivate final Map<Object, AtomicLong> map = new ConcurrentHashMap<>();\r\n\r\n\tprivate ReentrantLock lock = new ReentrantLock();\r\n\t\r\n\tprivate SQLParser sqlParser = new SQLParser();\r\n    \r\n    private final static QueryConditionAnalyzer instance  = new QueryConditionAnalyzer();\r\n    \r\n    private QueryConditionAnalyzer() {}\r\n    \r\n    public static QueryConditionAnalyzer getInstance() {\r\n        return instance;\r\n    }  \r\n    \r\n\t\r\n\t@Override\r\n\tpublic void onQueryResult(QueryResult queryResult) {\r\n\t\t\r\n//\t\tthis.lock.lock();\r\n//\t\ttry {\r\n\t\t\t\r\n\t\t\tint sqlType = queryResult.getSqlType();\r\n\t\t\tString sql = queryResult.getSql();\r\n\t\r\n\t\t\tswitch(sqlType) {\r\n\t    \tcase ServerParse.SELECT:\t\t\r\n    \t\t\tList<Object> values = sqlParser.parseConditionValues(sql, this.tableName, this.columnName);\r\n\t    \t\tif ( values != null ) {\r\n\t    \t\t\t\r\n\t    \t\t\tif ( this.map.size() < MAX_QUERY_MAP_SIZE ) {\r\n\t    \t\t\t\t\r\n\t\t    \t\t\tfor(Object value : values) {\r\n\t\t\t\t\t\t\tAtomicLong count = this.map.get(value);\r\n\t\t    \t\t\t\tif (count == null) {\r\n\t\t    \t\t\t\t\tcount = new AtomicLong(1L);\r\n\t\t    \t\t\t\t} else {\r\n\t\t    \t\t\t\t\tcount.getAndIncrement();\r\n\t\t    \t\t\t\t}\t    \t\t\t\t\r\n\t\t    \t\t\t\tthis.map.put(value, count);\t    \t\t\t\t\r\n\t\t    \t\t\t}\r\n\t\t    \t\t\t\r\n\t    \t\t\t} else {\r\n\t    \t\t\t\tLOGGER.debug(\" this map is too large size \");\r\n\t    \t\t\t}\r\n\t    \t\t}\r\n\t\t\t}\t\r\n\t\t\t\r\n//\t\t} finally {\r\n//\t\t\tthis.lock.unlock();\r\n//\t\t}\r\n\t}\r\n\t\r\n\tpublic boolean setCf(String cf) {\r\n\t\t\r\n\t\tboolean isOk = false;\r\n\t\t\r\n\t\tthis.lock.lock();  \r\n\t\ttry {  \r\n\t\t\t\r\n\t\t\tif ( !\"NULL\".equalsIgnoreCase(cf) ) {\r\n\t\t\t\t\r\n\t\t\t\tString[] table_column = cf.split(\"&\");\r\n\t\t\t\tif ( table_column != null && table_column.length == 2 ) {\t\t\t\t\t\r\n\t\t\t\t\tthis.tableName = table_column[0];\r\n\t\t\t\t\tthis.columnName = table_column[1];\r\n\t\t\t\t\tthis.map.clear();\r\n\t\t\t\t\t\r\n\t\t\t\t\tisOk = true;\r\n\t\t\t\t}\t\r\n\t\t\t\t\r\n\t\t\t} else {\t\r\n\t\t\t\t\r\n\t\t\t\tthis.tableName = null;\r\n\t\t\t\tthis.columnName = null;\t\t\t\t\r\n\t\t\t\tthis.map.clear();\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\tisOk = true;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t} finally {  \r\n\t\t\tthis.lock.unlock();   \r\n\t\t}  \r\n\t\t\r\n\t\treturn isOk;\t\t\r\n\t}\r\n\t\r\n\tpublic String getKey() {\r\n\t\treturn this.tableName + \".\" + this.columnName;\r\n\t}\r\n\t\r\n\tpublic List<Map.Entry<Object, AtomicLong>> getValues() {\r\n\t\tList<Map.Entry<Object, AtomicLong>> list = new ArrayList<Map.Entry<Object, AtomicLong>>(map.entrySet());\r\n\t\treturn list;\r\n\t}\r\n\t\r\n\t\r\n    // SQL 解析\r\n\tclass SQLParser {\r\n\t\t\r\n\t\t/**\r\n\t\t * 去掉库名、去掉``\r\n\t\t * @param tableName\r\n\t\t * @return\r\n\t\t */\r\n\t\tprivate String fixName(String tableName) {\r\n\t\t\tif ( tableName != null ) {\r\n\t\t\t\ttableName = tableName.replace(\"`\", \"\");\r\n\t\t\t\tint dotIdx = tableName.indexOf(\".\");\r\n\t\t\t\tif ( dotIdx > 0 ) {\r\n\t\t\t\t\ttableName = tableName.substring(1 + dotIdx).trim();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn tableName;\r\n\t\t}\r\n\t\t\r\n\t\t/**\r\n\t\t * 解析 SQL 获取指定表及条件列的值\r\n\t\t * \r\n\t\t * @param sql\r\n\t\t * @param tableName\r\n\t\t * @param colnumName\r\n\t\t * @return\r\n\t\t */\r\n\t\tpublic List<Object> parseConditionValues(String sql, String tableName, String colnumName)  {\r\n\t\t\t\r\n\t\t\tList<Object> values = null;\r\n\t\t\t\r\n\t\t\tif ( sql != null && tableName != null && columnName != null ) {\r\n\t\t\t\r\n\t\t\t\tvalues = new ArrayList<Object>();\r\n\t\t\t\t\r\n\t\t\t\tMySqlStatementParser parser = new MySqlStatementParser(sql);\r\n\t\t\t\tSQLStatement stmt = parser.parseStatement();\r\n\t\t\t\t\r\n\t\t\t\tMycatSchemaStatVisitor visitor = new MycatSchemaStatVisitor();\r\n\t\t\t\tstmt.accept(visitor);\r\n\t\t\t\t\r\n\t\t\t\tString currentTable = visitor.getCurrentTable();\r\n\t\t\t\tif ( tableName.equalsIgnoreCase( currentTable ) ) {\r\n\t\t\t\t\t\r\n\t\t\t\t\tList<Condition> conditions = visitor.getConditions();\r\n\t\t\t\t\tfor(Condition condition: conditions) {\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tString ccN = condition.getColumn().getName();\r\n\t\t\t\t\t\tccN = fixName(ccN);\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tif ( colnumName.equalsIgnoreCase( ccN ) ) {\t\t\t\t\t\r\n\t\t\t\t\t\t\tList<Object> ccVL = condition.getValues();\r\n\t\t\t\t\t\t\tvalues.addAll( ccVL );\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\t\t\t\t\r\n\t\t\t}\r\n\t\t\treturn values;\r\n\t\t}\t\t\r\n\t}\r\n\t\r\n   /* -----------------------------------------------------------------\r\n    public static void main(String arg[]) {\r\n    \t\r\n    \tString sql = \"SELECT `fnum`, `forg`, `fdst`, `airline`, `ftype` , `ports_of_call`, \" +\r\n\t\t\t\t\t\"`scheduled_deptime`, `scheduled_arrtime`, `actual_deptime`, `actual_arrtime`, \" +\r\n\t\t\t\t\t\"`flight_status_code` FROM dynamic \" +\r\n\t\t\t\t\t\"WHERE `fnum` = 'CA123'  AND `forg` = 'PEK'  AND `fdst` = 'SHA' \" +\r\n\t\t\t\t\t\"AND `scheduled_deptime` BETWEEN 1212121 AND 232323233 \" +\r\n\t\t\t\t\t\"AND `fservice` = 'J' AND `fcategory` = 1 \" +\r\n\t\t\t\t\t\"AND `share_execute_flag` = 1 ORDER BY scheduled_deptime\";\r\n    \t\r\n    \tQueryResult qr = new QueryResult(\"zhuam\", ServerParse.SELECT, sql, 0);\r\n    \t\r\n    \tQueryConditionAnalyzer analyzer = QueryConditionAnalyzer.getInstance();\r\n    \tanalyzer.setTableColumnFilter(\"dynamic&fnum\");\r\n    \tanalyzer.onQuery(qr);\r\n    \t\r\n    \tList<Map.Entry<Object, Long>> list = analyzer.getValues();\r\n    \tSystem.out.println( list );\r\n     }\r\n    */\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/QueryResult.java",
    "content": "package io.mycat.statistic.stat;\n\n/**\n * SQL 执行结果\n * \n * @author zhuam\n *\n */\npublic class QueryResult {\n    private String schema;      // 逻辑schema\n\tprivate String user;\t\t//用户\n\tprivate int sqlType;\t\t//SQL类型\n\tprivate String sql;\t\t\t//SQL\n\tprivate long sqlRows;\t\t//SQL 返回或影响的结果集长度\n\tprivate long netInBytes;\t//NET IN 字节数\n\tprivate long netOutBytes;\t//NET OUT 字节数\n\tprivate long startTime;\t\t//开始时间\n\tprivate long endTime;\t\t//结束时间\n\tprivate int resultSize;     //结果集大小\n\tprivate String host;\n\n    public QueryResult(String schema, String user, int sqlType, String sql, long sqlRows,\n                       long netInBytes, long netOutBytes, long startTime, long endTime\n            , int resultSize, String host) {\n\t\tsuper();\n        this.schema = schema == null ? \"\" : schema;\n\t\tthis.user = user;\n\t\tthis.sqlType = sqlType;\n\t\tthis.sql = sql;\n\t\tthis.sqlRows = sqlRows;\n\t\tthis.netInBytes = netInBytes;\n\t\tthis.netOutBytes = netOutBytes;\n\t\tthis.startTime = startTime;\n\t\tthis.endTime = endTime;\n\t\tthis.resultSize=resultSize;\n\t\tthis.host = host;\n\t}\n\n\tpublic String getUser() {\n\t\treturn user;\n\t}\n\n\tpublic int getSqlType() {\n\t\treturn sqlType;\n\t}\n\n\tpublic String getSql() {\n\t\treturn sql;\n\t}\n\n\tpublic long getSqlRows() {\n\t\treturn sqlRows;\n\t}\n\n\tpublic long getNetInBytes() {\n\t\treturn netInBytes;\n\t}\n\n\tpublic long getNetOutBytes() {\n\t\treturn netOutBytes;\n\t}\n\t\n\tpublic long getStartTime() {\n\t\treturn startTime;\n\t}\n\n\tpublic long getEndTime() {\n\t\treturn endTime;\n\t}\n\t\n\tpublic int getResultSize() {\n\t\treturn resultSize;\n\t}\n\n\tpublic String getHost() {\n\t\treturn host;\n\t}\n\n    public String getSchema() {\n        return schema;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/QueryResultDispatcher.java",
    "content": "package io.mycat.statistic.stat;\r\n\r\nimport java.util.List;\r\nimport java.util.concurrent.CopyOnWriteArrayList;\r\n\r\nimport org.slf4j.Logger; \r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport io.mycat.MycatServer;\r\n\r\n/**\r\n * SQL执行后的派发  QueryResult 事件\r\n * \r\n * @author zhuam\r\n *\r\n */\r\npublic class QueryResultDispatcher {\r\n\t\r\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(QueryResultDispatcher.class);\r\n\t\r\n\tprivate static List<QueryResultListener> listeners = new CopyOnWriteArrayList<QueryResultListener>();\r\n\r\n\t// 初始化强制加载\r\n\tstatic {\r\n\t\tlisteners.add( UserStatAnalyzer.getInstance() );\r\n\t\tlisteners.add( TableStatAnalyzer.getInstance() );\r\n\t\tlisteners.add( QueryConditionAnalyzer.getInstance() );\r\n\t}\r\n\t\r\n\tpublic static void addListener(QueryResultListener listener) {\r\n\t\tif (listener == null) {\r\n\t\t\tthrow new NullPointerException();\r\n\t\t}\r\n\t\tlisteners.add(listener);\r\n\t}\r\n\r\n\tpublic static void removeListener(QueryResultListener listener) {\r\n\t\tlisteners.remove(listener);\r\n\t}\r\n\r\n\tpublic static void removeAllListener() {\r\n\t\tlisteners.clear();\r\n\t}\r\n\t\r\n\tpublic static void dispatchQuery(final QueryResult queryResult) {\r\n\t\t\r\n\t\t\r\n\t\t// 是否派发 QueryResult 事件\r\n\t\tint useSqlStat = MycatServer.getInstance().getConfig().getSystem().getUseSqlStat();\r\n\t\tif ( useSqlStat == 0 ) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\t\r\n\t\t//TODO：异步分发，待进一步调优 \r\n\t\tMycatServer.getInstance().getBusinessExecutor().execute(new Runnable() {\r\n\t\t\t\r\n\t\t\tpublic void run() {\t\t\r\n\t\t\t\t\r\n\t\t\t\tfor(QueryResultListener listener: listeners) {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tlistener.onQueryResult( queryResult );\r\n\t\t\t\t\t} catch(Exception e) {\r\n\t\t\t\t\t\tLOGGER.error(\"error:\",e);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\t\t\t\t\t\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/QueryResultListener.java",
    "content": "package io.mycat.statistic.stat;\n\npublic interface QueryResultListener {\n\t\n\tpublic void onQueryResult(QueryResult queryResult);\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/SqlFrequency.java",
    "content": "package io.mycat.statistic.stat;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\npublic class SqlFrequency implements Comparable<SqlFrequency>{\n\tprivate String sql;\n\tprivate AtomicLong count = new AtomicLong(0);\n\tprivate long lastTime = 0;\n\tprivate long executeTime = 0;\n\tprivate long allExecuteTime = 0;\n\tprivate long maxTime = 0;\n\tprivate long avgTime = 0;\n\tprivate long minTime = 0;\n\tprivate String host;\n\t\n\tpublic String getSql() {\n\t\treturn sql;\n\t}\n\n\tpublic void setSql(String sql) {\n\t\tthis.sql = sql;\n\t}\n\n\tpublic long getCount() {\n\t\treturn this.count.get();\n\t}\n\n\tpublic void incCount() {\n\t\tthis.count.getAndIncrement();\n\t}\n\n\tpublic long getLastTime() {\n\t\treturn lastTime;\n\t}\n\t\n\tpublic void setLastTime(long lastTime) {\n\t\tthis.lastTime = lastTime;\n\t}\n\t\n\tpublic long getExecuteTime() {\n\t\treturn executeTime;\n\t}\n\t\n\tpublic long getMaxTime() {\n\t\treturn maxTime;\n\t}\n\t\n\tpublic long getMinTime() {\n\t\treturn minTime;\n\t}\n\t\n\tpublic long getAvgTime() {\n\t\treturn avgTime;\n\t}\t\n\t\n\tpublic void setExecuteTime(long execTime) {\n\t\tif (execTime > this.maxTime) {\n\t\t\tthis.maxTime = execTime;\n\t\t}\n\t\tif (this.minTime == 0) {\n\t\t\tthis.minTime = execTime;\n\t\t}\n\t\tif (execTime > 0\n\t\t\t\t&& execTime < this.minTime) {\n\t\t\t\tthis.minTime = execTime;\n\t\t}\n\t\tthis.allExecuteTime+=execTime;\n\t\tif (count.get() > 0) {\n\t\t\tthis.avgTime = this.allExecuteTime / this.count.get();\n\t\t}\t\t\n\t\tthis.executeTime = execTime;\n\t}\n\n\t@Override\n\tpublic int compareTo(SqlFrequency o) {\n\t\tlong para = o.count.get() - count.get();\n\t\tlong para2 = o.lastTime - lastTime;\n\t\treturn  para == 0L ? (int)(para2 == 0L ? o.allExecuteTime - allExecuteTime : para2) : (int)para ;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif(obj instanceof SqlFrequency) {\n\t\t\treturn this.compareTo((SqlFrequency)obj) == 0;\n\t\t} else {\n\t\t\treturn super.equals(obj);\n\t\t}\n\t}\n\n\tpublic String getHost() {\n\t\treturn host;\n\t}\n\n\tpublic void setHost(String host) {\n\t\tthis.host = host;\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/SqlResultSet.java",
    "content": "package io.mycat.statistic.stat;\r\n/**\r\n * 结果集记录模型\r\n */\r\npublic class SqlResultSet {\r\n\tprivate String sql;\r\n\tprivate int resultSetSize = 0;\r\n\tprivate int count;\r\n\t\r\n\tpublic String getSql() {\r\n\t\treturn sql;\r\n\t}\r\n\tpublic void setSql(String sql) {\r\n\t\tthis.sql = sql;\r\n\t}\r\n\tpublic int getResultSetSize() {\r\n\t\treturn resultSetSize;\r\n\t}\r\n\tpublic void setResultSetSize(int resultSetSize) {\r\n\t\tthis.resultSetSize = resultSetSize;\r\n\t}\r\n\tpublic int getCount() {\r\n\t\treturn count;\r\n\t}\r\n\tpublic void count() {\r\n\t\tthis.count++;\r\n\t}\r\n\t\r\n\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/SqlResultSizeRecorder.java",
    "content": "package io.mycat.statistic.stat;\r\n\r\nimport java.util.concurrent.ConcurrentHashMap;\r\n\r\nimport com.alibaba.druid.DbType;\r\nimport com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils;\r\n\r\n/**\r\n * 大结果集 SQL\r\n *\r\n */\r\npublic class SqlResultSizeRecorder {\r\n\t\r\n\tprivate ConcurrentHashMap<String, SqlResultSet> sqlResultSetMap = new ConcurrentHashMap<String, SqlResultSet>();\t\r\n\t\r\n\r\n\t\r\n\tpublic void addSql(String sql,int resultSetSize ){\r\n\t\tSqlResultSet sqlResultSet;\r\n\t\tSqlParser sqlParserHigh = new SqlParser();\r\n\t\tsql=sqlParserHigh.mergeSql(sql);\r\n\t\tif(this.sqlResultSetMap.containsKey(sql)){\r\n\t\t    sqlResultSet =this.sqlResultSetMap.get(sql);\r\n\t\t\tsqlResultSet.count();\r\n\t\t\tsqlResultSet.setSql(sql);\r\n\t\t\t//System.out.println(sql);\r\n\t\t\tsqlResultSet.setResultSetSize(resultSetSize);\r\n\t\t}else{\r\n\t\tsqlResultSet = new SqlResultSet();\r\n\t\tsqlResultSet.setResultSetSize(resultSetSize);\r\n\t\tsqlResultSet.setSql(sql);\r\n        this.sqlResultSetMap.put(sql, sqlResultSet);     \r\n\t\t}\r\n\t}\t\t\r\n\r\n\t\r\n\t/**\r\n\t * 获取 SQL 大结果集记录\r\n\t */\r\n\tpublic ConcurrentHashMap<String, SqlResultSet>  getSqlResultSet() {\r\n\t\t\r\n        return sqlResultSetMap;\r\n\t}\t\r\n\t\r\n\t\r\n\tpublic void clearSqlResultSet() {\t\t\r\n\t\tsqlResultSetMap.clear();\r\n\t}\r\n\t\r\n   class SqlParser {\r\n\t\t\r\n\t\tpublic String fixSql(String sql) {\r\n\t\t\tif ( sql != null)\r\n\t\t\t\treturn sql.replace(\"\\n\", \" \");\r\n\t\t\treturn sql;\r\n\t    }\r\n\t\t\r\n\t\tpublic String mergeSql(String sql) {\r\n\t\t\t\r\n\t\t\tString newSql = ParameterizedOutputVisitorUtils.parameterize(sql, DbType.mysql);\r\n\t\t\treturn fixSql( newSql );\r\n\t    }\r\n\r\n\t}\r\n\t\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/TableStat.java",
    "content": "package io.mycat.statistic.stat;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\nimport java.util.concurrent.atomic.AtomicLong;\r\n\r\nimport io.mycat.server.parser.ServerParse;\r\n\r\n/**\r\n * SQL统计中,统计出来每个表的读,写的TPS,分辨出当前最热的表，\r\n * 并且根据是否有关联JOIN来区分为几个不同的“区域”,是一个重要功能,意味着,某些表可以转移到其他的数据库里,做智能优化。\r\n * \r\n * 首先是每个表的读写TPS 2个指标,有时段。然后是 哪那些表有JOIN查询 ，来区分 独立的区域\r\n * \r\n * @author zhuam\r\n *\r\n */\r\npublic class TableStat implements Comparable<TableStat> {\r\n\t\r\n\t//1、读写\r\n\t//2、主表\r\n\t//3、关联表  次数\r\n\t//4、读写 TPS\r\n\t\r\n\tpublic String table;\r\n\r\n\tprivate final AtomicLong rCount = new AtomicLong(0);\r\n    private final AtomicLong wCount = new AtomicLong(0);\r\n    \r\n    // 关联表\r\n    private final ConcurrentHashMap<String, RelaTable> relaTableMap = new ConcurrentHashMap<String, RelaTable>();    \r\n    \r\n    /**\r\n     * 最后执行时间\r\n     */\r\n    private long lastExecuteTime;\r\n    \r\n    \r\n    public TableStat(String table) {\r\n\t\tsuper();\r\n\t\tthis.table = table;\t\t\r\n    }\r\n    \r\n\tpublic void reset() {\t\t\r\n\t\tthis.rCount.set(0);\r\n\t\tthis.wCount.set(0);\t\t\r\n\t\tthis.relaTableMap.clear();\r\n\t\tthis.lastExecuteTime = 0;\r\n\t}\r\n\t\r\n\tpublic void update(int sqlType, String sql, long startTime, long endTime, List<String> relaTables) {\r\n\t\t\r\n\t\t//记录 RW\r\n\t\tswitch(sqlType) {\r\n    \tcase ServerParse.SELECT:\t\t\r\n\t\t\tthis.rCount.incrementAndGet();\t\t\r\n    \t\tbreak;\r\n    \tcase ServerParse.UPDATE:\t\t\t\r\n    \tcase ServerParse.INSERT:\t\t\r\n    \tcase ServerParse.DELETE:\r\n    \tcase ServerParse.REPLACE:\r\n    \t\tthis.wCount.incrementAndGet();\r\n    \t\tbreak;\r\n    \t}\r\n\t\t\r\n\t\t// 记录 关联表执行情况\r\n\t\tfor(String table: relaTables) {\r\n\t\t\tRelaTable relaTable = this.relaTableMap.get( table );\r\n\t\t\tif ( relaTable == null ) {\r\n\t\t\t\trelaTable = new RelaTable(table, 1);\r\n\t\t\t} else {\r\n\t\t\t\trelaTable.incCount();\r\n\t\t\t}\r\n\t\t\tthis.relaTableMap.put(table, relaTable);\r\n\t\t}\r\n\t\r\n\t\tthis.lastExecuteTime = endTime;\r\n\t}\r\n\t\r\n    public String getTable() {\r\n\t\treturn table;\r\n\t}\r\n\r\n\tpublic long getRCount() {\r\n        return this.rCount.get();\r\n    }\r\n    \r\n    public long getWCount() {\r\n        return this.wCount.get();\r\n    }\r\n    \r\n\tpublic int getCount() {\r\n\t\treturn (int)(getRCount()+getWCount());\r\n\t}    \r\n\t\r\n    public List<RelaTable> getRelaTables() {    \t\r\n    \tList<RelaTable> tables = new ArrayList<RelaTable>();\r\n    \ttables.addAll( this.relaTableMap.values() );\r\n    \treturn tables;\r\n    }\r\n    \r\n    public long getLastExecuteTime() {\r\n\t\treturn lastExecuteTime;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int compareTo(TableStat o) {\r\n\t\tlong para = o.getCount() - getCount();\r\n\t\tlong para2 = o.getLastExecuteTime() - getLastExecuteTime();\r\n\t\treturn para == 0? (para2 == 0? o.getTable().hashCode() - getTable().hashCode() :(int) para2) : (int)para ;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean equals(Object obj) {\r\n\t\tif(obj instanceof TableStat) {\r\n\t\t\treturn this.compareTo((TableStat)obj) == 0;\r\n\t\t} else {\r\n\t\t\treturn super.equals(obj);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n     * 关联表\r\n     * @author Ben\r\n     *\r\n     */\r\n    public static class RelaTable {\r\n    \t\r\n    \tprivate String tableName;\r\n    \tprivate int count;\r\n    \t\r\n\t\tpublic RelaTable(String tableName, int count) {\r\n\t\t\tsuper();\r\n\t\t\tthis.tableName = tableName;\r\n\t\t\tthis.count = count;\r\n\t\t}\r\n\r\n\t\tpublic String getTableName() {\r\n\t\t\treturn this.tableName;\r\n\t\t}\r\n\t\t\r\n\t\tpublic int getCount() {\r\n\t\t\treturn this.count;\r\n\t\t}\r\n\t\t\r\n\t\tpublic void incCount() {\r\n\t\t\tthis.count++;\r\n\t\t}    \t\r\n    }\r\n\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/TableStatAnalyzer.java",
    "content": "package io.mycat.statistic.stat;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.DbType;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLExprTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLInsertStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLReplaceStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLSelectStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;\nimport com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;\nimport com.alibaba.druid.sql.parser.SQLParserUtils;\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\nimport com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter;\n\nimport io.mycat.server.parser.ServerParse;\n\n/**\n * 按SQL表名进行计算\n * \n * @author zhuam\n *\n */\npublic class TableStatAnalyzer implements QueryResultListener {\n\t\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(TableStatAnalyzer.class);\n\t\n\tprivate Map<String, TableStat> tableStatMap = new ConcurrentHashMap<>();\n\tprivate ReentrantLock lock  = new ReentrantLock();\n\t\n\t//解析SQL 提取表名\n\tprivate SQLParser sqlParser = new SQLParser();\n\t\n    private final static TableStatAnalyzer instance  = new TableStatAnalyzer();\n    \n    private TableStatAnalyzer() {}\n    \n    public static TableStatAnalyzer getInstance() {\n        return instance;\n    }  \n    \n\t@Override\n\tpublic void onQueryResult(QueryResult queryResult) {\n\t\t\n\t\tint sqlType = queryResult.getSqlType();\n\t\tString sql = queryResult.getSql();\n\t\tswitch(sqlType) {\n    \tcase ServerParse.SELECT:\t\t\n    \tcase ServerParse.UPDATE:\t\t\t\n    \tcase ServerParse.INSERT:\t\t\n    \tcase ServerParse.DELETE:\n    \tcase ServerParse.REPLACE:  \n    \t\t\n    \t\t//关联表提取\n    \t\tString masterTable = null;\n    \t\tList<String> relaTables = new ArrayList<String>();\n    \t\t\n    \t\tList<String> tables = sqlParser.parseTableNames(sql);\n    \t\tfor(int i = 0; i < tables.size(); i++) {\n    \t\t\tString table = tables.get(i);\n    \t\t\tif ( i == 0 ) {\n    \t\t\t\tmasterTable = table;\n    \t\t\t} else {\n    \t\t\t\trelaTables.add( table );\n    \t\t\t}\n    \t\t}\n    \t\t\n    \t\tif ( masterTable != null ) {\n    \t\t\tTableStat tableStat = getTableStat( masterTable );\n    \t\t\ttableStat.update(sqlType, sql, queryResult.getStartTime(), queryResult.getEndTime(), relaTables);\t\t\n    \t\t}    \t\t\n    \t\tbreak;\n    \t}\t\t\n\t}\t\n\t\n\tprivate TableStat getTableStat(String tableName) {\n\t\tTableStat userStat = tableStatMap.get(tableName);\n\t\tif (userStat == null) {\n\t\t\tif(lock.tryLock()){\n\t\t\t\ttry{\n\t\t\t\t\tuserStat = new TableStat(tableName);\n\t\t\t\t\ttableStatMap.put(tableName, userStat);\n\t\t\t\t} finally {\n\t\t\t\t\tlock.unlock();\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\twhile(userStat == null){\n\t\t\t\t\tuserStat = tableStatMap.get(tableName);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn userStat;\n    }\n\t\n\tpublic Map<String, TableStat> getTableStatMap() {\n\t\tMap<String, TableStat> map = new LinkedHashMap<String, TableStat>(tableStatMap.size());\n\t\tmap.putAll(tableStatMap);\n        return map;\n\t}\n\t\n\t/**\n\t * 获取 table 访问排序统计\n\t */\n\tpublic List<TableStat> getTableStats(boolean isClear) {\n\t\tSortedSet<TableStat> tableStatSortedSet = new TreeSet<>(tableStatMap.values());\n\t\tList<TableStat> list =  new ArrayList<>(tableStatSortedSet);\n        return list;\n\t}\t\n\t\n\tpublic void ClearTable() {\n\t\ttableStatMap.clear();\n\t}\n\n\t\n\t/**\n\t * 解析 table name\n\t */\n\tprivate static class SQLParser {\n\t\t\n\t\tprivate SQLStatement parseStmt(String sql) {\n\t\t\tSQLStatementParser statParser = SQLParserUtils.createSQLStatementParser(sql, \"mysql\");\n\t\t\tSQLStatement stmt = statParser.parseStatement();\n\t\t\treturn stmt;\t\t\n\t\t}\t\t\n\t\t\n\t\t/**\n\t\t * 去掉库名、去掉``\n\t\t * @param tableName\n\t\t * @return\n\t\t */\n\t\tprivate String fixName(String tableName) {\n\t\t\tif ( tableName != null ) {\n\t\t\t\ttableName = tableName.replace(\"`\", \"\");\n\t\t\t\tint dotIdx = tableName.indexOf(\".\");\n\t\t\t\tif ( dotIdx > 0 ) {\n\t\t\t\t\ttableName = tableName.substring(1 + dotIdx).trim();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn tableName;\n\t\t}\n\t\t\n\t\t/**\n\t\t * 解析 SQL table name\n\t\t */\n\t\tpublic List<String> parseTableNames(String sql) {\n\t\t\tfinal List<String> tables = new ArrayList<String>();\n\t\t  try{\t\t\t\n\t\t\t\n\t\t\tSQLStatement stmt = parseStmt(sql);\n\t\t\tif (stmt instanceof SQLReplaceStatement) {\n\t\t\t\tString table = ((SQLReplaceStatement) stmt).getTableName().getSimpleName();\n\t\t\t\ttables.add( fixName( table ) );\n\t\t\t\t\n\t\t\t} else if (stmt instanceof SQLInsertStatement ) {\n\t\t\t\tString table = ((SQLInsertStatement)stmt).getTableName().getSimpleName();\n\t\t\t\ttables.add( fixName( table ) );\n\t\t\t\t\n\t\t\t} else if (stmt instanceof SQLUpdateStatement ) {\n\t\t\t\tString table = ((SQLUpdateStatement)stmt).getTableName().getSimpleName();\n\t\t\t\ttables.add( fixName( table ) );\n\t\t\t\t\n\t\t\t} else if (stmt instanceof SQLDeleteStatement ) {\n\t\t\t\t// Ken.li 2020/04/02\n\t\t\t\t//String table = ((SQLDeleteStatement)stmt).getTableName().getSimpleName();\n\t\t\t\t//tables.add( fixName( table ) );\n\t\t\t\t((SQLDeleteStatement)stmt).getTableSource().accept(new SQLASTVisitorAdapter() {\n\t\t\t\t\tpublic boolean visit(SQLExprTableSource x){\n\t\t\t\t\t\ttables.add( fixName( x.toString() ) );\n\t\t\t\t\t\treturn super.visit(x);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\t\n\t\t\t\tif (((SQLDeleteStatement)stmt).getFrom() != null) {\n\t\t\t\t\t((SQLDeleteStatement)stmt).getFrom().accept(new SQLASTVisitorAdapter() {\n\t\t\t\t\t\tpublic boolean visit(SQLExprTableSource x){\n\t\t\t\t\t\t\tif (tables.contains(x.getAlias())) {\n\t\t\t\t\t\t\t\ttables.remove(x.getAlias());\n\t\t\t\t\t\t\t\ttables.add( fixName( x.toString() ) );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn super.visit(x);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (stmt instanceof SQLSelectStatement ) {\n\t\t\t\t\n\t\t\t\t//TODO: modify by owenludong\n\t\t\t\tDbType dbType = ((SQLSelectStatement) stmt).getDbType();\n\t\t\t\tif (DbType.mysql.equals(dbType)) {\n\t\t\t\t\tstmt.accept(new MySqlASTVisitorAdapter() {\n\t\t\t\t\t\tpublic boolean visit(SQLExprTableSource x){\n\t\t\t\t\t\t\ttables.add( fixName( x.toString() ) );\n\t\t\t\t\t\t\treturn super.visit(x);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\t\n\t\t\t\t} else {\n\t\t\t\t\tstmt.accept(new SQLASTVisitorAdapter() {\n\t\t\t\t\t\tpublic boolean visit(SQLExprTableSource x){\n\t\t\t\t\t\t\ttables.add( fixName( x.toString() ) );\n\t\t\t\t\t\t\treturn super.visit(x);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\t\n\t\t  } catch (Exception e) {\n\t\t\t  LOGGER.error(\"TableStatAnalyzer err:\" + sql, e);\n\t\t  }\n\t\t  \n\t\t return tables;\n\t\t}\n\t}\t\n\t\n\t\n/*\tpublic static void main(String[] args) {\n\t\t\n\t\tList<String> sqls = new ArrayList<String>();\n\t\t\n\t\tsqls.add( \"SELECT id, name, age FROM v1select1 a LEFT OUTER JOIN v1select2 b ON  a.id = b.id WHERE a.name = 12 \");\n\t\tsqls.add( \"insert into v1user_insert(id, name) values(1,3)\");\n\t\tsqls.add( \"delete from v1user_delete where id= 2\");\n\t\tsqls.add( \"update v1user_update set id=2 where id=3\");\n\t\tsqls.add( \"select ename,deptno,sal from v1user_subquery1 where deptno=(select deptno from v1user_subquery2 where loc='NEW YORK')\");\n\t\tsqls.add( \"replace into v1user_insert(id, name) values(1,3)\");\n\t\tsqls.add( \"select * from v1xx where id=3 group by zz\");\n\t\tsqls.add( \"select * from v1yy where xx=3 limit 0,3\");\n\t\tsqls.add( \"SELECT * FROM (SELECT * FROM posts ORDER BY dateline DESC) GROUP BY  tid ORDER BY dateline DESC LIMIT 10\");\n\t\t\n\t\tfor(String sql: sqls) {\n\t\t\tList<String> tables = TableStatAnalyzer.getInstance().sqlParser.parseTableNames(sql);\n\t\t\tfor(String t: tables) {\n\t\t\t\tSystem.out.println( t );\n\t\t\t}\n\t\t}\t\t\n\t}\n\t*/\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/UserSqlHighStat.java",
    "content": "package io.mycat.statistic.stat;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.DbType;\nimport com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils;\n\npublic class UserSqlHighStat {\n\n  private static final int CAPACITY_SIZE = 1024;\n\n  private ConcurrentHashMap<String, SqlFrequency> sqlFrequencyMap = new ConcurrentHashMap<>();\n\n  private SqlParser sqlParser = new SqlParser();\n  private static final Logger LOGGER = LoggerFactory.getLogger(UserSqlHighStat.class);\n\n  public void addSql(String sql, long executeTime, long startTime, long endTime,String host) {\n    String newSql = this.sqlParser.mergeSql(sql);\n    if (newSql != null) {\n      this.sqlFrequencyMap.compute(newSql,\n          (s, sqlFrequency) -> {\n            if (sqlFrequency == null) {\n              sqlFrequency = new SqlFrequency();\n              sqlFrequency.setSql(s);\n            }\n            sqlFrequency.setLastTime(endTime);\n            sqlFrequency.incCount();\n            sqlFrequency.setExecuteTime(executeTime);\n            sqlFrequency.setHost(host);\n            return sqlFrequency;\n          });\n    }\n  }\n\n\n  /**\n   * 获取 SQL 访问频率\n   */\n  public List<SqlFrequency> getSqlFrequency(boolean isClear) {\n    List<SqlFrequency> list = new ArrayList<>(this.sqlFrequencyMap.values());\n    if (isClear) {\n      clearSqlFrequency();\n    }\n    return list;\n  }\n\n\n  private void clearSqlFrequency() {\n    sqlFrequencyMap.clear();\n  }\n\n  public void recycle() {\n    if (sqlFrequencyMap.size() > CAPACITY_SIZE) {\n      ConcurrentHashMap<String, SqlFrequency> sqlFrequencyMap2 = new ConcurrentHashMap<>();\n      SortedSet<SqlFrequency> sqlFrequencySortedSet = new TreeSet<>(this.sqlFrequencyMap.values());\n      List<SqlFrequency> keyList = new ArrayList<SqlFrequency>(sqlFrequencySortedSet);\n      int i = 0;\n      for (SqlFrequency key : keyList) {\n        if (i == CAPACITY_SIZE) {\n          break;\n        }\n        sqlFrequencyMap2.put(key.getSql(), key);\n        i++;\n      }\n      sqlFrequencyMap = sqlFrequencyMap2;\n    }\n  }\n\n\n  private static class SqlParser {\n\n    public String fixSql(String sql) {\n      if (sql != null) {\n        return sql.replace(\"\\n\", \" \");\n      }\n      return sql;\n    }\n\n    public String mergeSql(String sql) {\n      try {\n\t\t\tString newSql = ParameterizedOutputVisitorUtils.parameterize(sql, DbType.mysql);\n        return fixSql(newSql);\n      } catch (Exception e) {\n        LOGGER.warn(\"user sql high parse:{} error\",sql, e);\n        return null;\n      }\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/UserSqlLargeStat.java",
    "content": "package io.mycat.statistic.stat;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.SortedSet;\nimport java.util.concurrent.ConcurrentSkipListSet;\n\npublic class UserSqlLargeStat {\n\t\n    private final int count;\n    private SortedSet<SqlLarge> sqls;\n\n    public UserSqlLargeStat(int count) {\n        this.count = count;\n        this.sqls = new ConcurrentSkipListSet<>();\n    }\n\n    public List<SqlLarge> getSqls() {\n        List<SqlLarge> list = new ArrayList<>(sqls);\n        return list;\n    }\n\n    public void add(String sql, long sqlRows, long executeTime, long startTime, long endTime,String host) {\n    \tSqlLarge sqlLarge = new SqlLarge(sql, sqlRows, executeTime, startTime, endTime,host);\n    \tthis.add( sqlLarge );\n    }\n    \n    public void add(SqlLarge sql) {\n        sqls.add(sql);\n    }\n    \n    public void reset() {\n    \tthis.clear();\n    }\n\n    public void clear() {\n        sqls.clear();\n    }\n\n    public void recycle() {\n        if(sqls.size() > count){\n            SortedSet<SqlLarge> sqls2 = new ConcurrentSkipListSet<>();\n            List<SqlLarge> keyList = new ArrayList<SqlLarge>(sqls);\n            int i = 0;\n            for(SqlLarge key : keyList){\n                if(i == count) {\n                    break;\n                }\n                sqls2.add(key);\n                i++;\n            }\n            sqls = sqls2;\n        }\n    }\n    \n    /**\n     * 记录 SQL 及返回行数\n     */\n    public static class SqlLarge implements Comparable<SqlLarge> {\n    \t\n    \tprivate String sql;\n    \tprivate long sqlRows;\n    \tprivate long executeTime;\n    \tprivate long startTime;\n    \tprivate long endTime;\n\t\tprivate String host;\n\n\t\tpublic SqlLarge(String sql, long sqlRows, long executeTime, long startTime, long endTime,String host) {\n\t\t\tsuper();\n\t\t\tthis.sql = sql;\n\t\t\tthis.sqlRows = sqlRows;\n\t\t\tthis.executeTime = executeTime;\n\t\t\tthis.startTime = startTime;\n\t\t\tthis.endTime = endTime;\n\t\t\tthis.host = host;\n\t\t}\n\n\t\tpublic String getSql() {\n\t\t\treturn sql;\n\t\t}\n\n\t\tpublic long getSqlRows() {\n\t\t\treturn sqlRows;\n\t\t}\n\n\t\tpublic long getStartTime() {\n\t\t\treturn startTime;\n\t\t}\n\n\t\tpublic long getExecuteTime() {\n\t\t\treturn executeTime;\n\t\t}\n\n\t\tpublic long getEndTime() {\n\t\t\treturn endTime;\n\t\t}\n\t\t\n\t\t@Override\n\t    public int compareTo(SqlLarge o) {\n            long para = o.sqlRows - sqlRows;\n\t        return para == 0 ? (o.sql.hashCode() - sql.hashCode()) :(int) (para);\n\t    }\n\n\t    @Override\n\t    public boolean equals(Object arg0) {\n\t        return super.equals(arg0);\n\t    }\n\n\t    @Override\n\t    public int hashCode() {\n\t        return super.hashCode();\n\t    }\n\n\t\tpublic String getHost() {\n\t\t\treturn host;\n\t\t}\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/UserSqlLastStat.java",
    "content": "package io.mycat.statistic.stat;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * 最后执行的 Sql\n * \n * @author zhuam\n *\n */\npublic class UserSqlLastStat {\n\tprivate static final int MAX_RECORDS = 1024;\n\tprivate SortedSet<SqlLast> sqls;\n\t \n\tpublic UserSqlLastStat(int count) {\t\t\n        this.sqls = new ConcurrentSkipListSet<>();\n\t}\n\n\tpublic List<SqlLast> getSqls() {\n\t\tList<SqlLast> keyList = new ArrayList<SqlLast>(sqls);\n\t\treturn keyList;\n\t}\n\n    public void add(String sql,  long executeTime, long startTime, long endTime,String host) {\n    \tSqlLast sqlLast = new SqlLast(sql, executeTime, startTime, endTime,host);\n        sqls.add(sqlLast);\n    }\n\n    public void reset() {\n    \tthis.clear();\n    }\n    \n    public void clear() {\n\t\tsqls.clear();\n\t}\n\n\tpublic void recycle(){\n\t\tif(sqls.size() > MAX_RECORDS){\n\t\t\tSortedSet<SqlLast> sqls2 = new ConcurrentSkipListSet<>();\n\t\t\tList<SqlLast> keyList = new ArrayList<SqlLast>(sqls);\n\t\t\tint i = 0;\n\t\t\tfor(SqlLast key : keyList){\n\t\t\t\tif(i == MAX_RECORDS) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tsqls2.add(key);\n\t\t\t\ti++;\n\t\t\t}\n\t\t\tsqls = sqls2;\n\t\t}\n\t}\n    /**\n     * 记录SQL\n     */\n    public static class SqlLast implements Comparable<SqlLast>{\n    \t\n    \tprivate String sql;\n    \tprivate long executeTime;\n    \tprivate long startTime;\n    \tprivate long endTime;\n\t\tprivate String host;\n\n\t\tpublic SqlLast(String sql, long executeTime, long startTime, long endTime,String host) {\n\t\t\tsuper();\n\t\t\tthis.sql = sql;\n\t\t\tthis.executeTime = executeTime;\n\t\t\tthis.startTime = startTime;\n\t\t\tthis.endTime = endTime;\n\t\t\tthis.host = host;\n\t\t}\n\n\t\tpublic String getSql() {\n\t\t\treturn sql;\n\t\t}\n\n\t\tpublic long getStartTime() {\n\t\t\treturn startTime;\n\t\t}\n\n\t\tpublic long getExecuteTime() {\n\t\t\treturn executeTime;\n\t\t}\n\n\t\tpublic long getEndTime() {\n\t\t\treturn endTime;\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(SqlLast o) {\n\t\t\tlong st1 = o == null ? 0 : o.getStartTime();\n\t\t\treturn (int) ( st1 - this.getStartTime());\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif(obj instanceof SqlLast) {\n\t\t\t\treturn this.compareTo((SqlLast)obj) == 0;\n\t\t\t} else {\n\t\t\t\treturn super.equals(obj);\n\t\t\t}\n\t\t}\n\n\t\tpublic String getHost() {\n\t\t\treturn host;\n\t\t}\n    }\n    \n}"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/UserSqlRWStat.java",
    "content": "package io.mycat.statistic.stat;\n\nimport java.util.TimeZone;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport io.mycat.server.parser.ServerParse;\n\n/**\n * SQL R/W 执行状态\n * 因为这里的所有元素都近似为非必须原子更新的，即：\n * 例如：rCount和netInBytes没有必要非得保持同步更新，在同一个事务内\n * 只要最后更新了即可，所以将其中的元素拆成一个一个原子类，没必要保证精确的保持原样不加任何锁\n * \n * @author zhuam\n *\n */\npublic class UserSqlRWStat {\n\t\n\n\t/**\n\t * R/W 次数\n\t */\n\tprivate AtomicLong rCount = new AtomicLong(0L);\n    private AtomicLong wCount = new AtomicLong(0L);\n    \n    /**\n     * 每秒QPS\n     */\n    private int qps = 0;\n    \n    /**\n     * Net In/Out 字节数\n     */\n    private AtomicLong netInBytes = new AtomicLong(0L);\n    private AtomicLong netOutBytes = new AtomicLong(0L);\n    \n\t/**\n\t * 最大的并发\n\t */\n    private int concurrentMax = 1;\n\t\n    /**\n     * 执行耗时\n     * \n     * 10毫秒内、 10 - 200毫秒内、 1秒内、 超过 1秒\n     */\n\tprivate final Histogram timeHistogram = new Histogram( new long[] { 10, 200, 1000, 2000 } );\n\t\n\t/**\n\t * 执行所在时段\n\t * \n\t * 22-06 夜间、 06-13 上午、 13-18下午、 18-22 晚间\n\t */\n\tprivate final Histogram executeHistogram = new Histogram(new long[] { 6, 13, 18, 22 });\n\n    /**\n     * 最后执行时间\n\t * 不用很精确，所以不加任何锁\n     */\n    private long lastExecuteTime;\n    \n\t\n\tprivate int time_zone_offset = 0;\n\tprivate int one_hour = 3600 * 1000;\n\t\n\tpublic UserSqlRWStat() {\n\t\tthis.time_zone_offset = TimeZone.getDefault().getRawOffset();\n\t}\n\t\n\tpublic void reset() {\n\t\tthis.rCount = new AtomicLong(0L);\n\t\tthis.wCount = new AtomicLong(0L);\n\t\tthis.concurrentMax = 1;\t\t\n\t\tthis.lastExecuteTime = 0;\n\t\tthis.netInBytes = new AtomicLong(0L);\n\t\tthis.netOutBytes = new AtomicLong(0L);\n\t\t\n\t\tthis.timeHistogram.reset();\n\t\tthis.executeHistogram.reset();\n\t}\n\t\n\tpublic void add(int sqlType, String sql, long executeTime, long netInBytes, long netOutBytes, long startTime, long endTime) {\n\t\t\n\t\n\t\tswitch(sqlType) {\n    \tcase ServerParse.SELECT:\n    \tcase ServerParse.SHOW:\n    \t\tthis.rCount.incrementAndGet();\n    \t\tbreak;\n    \tcase ServerParse.UPDATE:\n    \tcase ServerParse.INSERT:\n    \tcase ServerParse.DELETE:\n    \tcase ServerParse.REPLACE:\n    \t\tthis.wCount.incrementAndGet();\n    \t\tbreak;\n    \t}\n    \t\n    \t//SQL执行所在的耗时区间\n    \tif ( executeTime <= 10 ) {\n    \t\tthis.timeHistogram.record(10);\n    \t\t\n    \t} else if ( executeTime > 10 && executeTime <= 200 ) {\n    \t\tthis.timeHistogram.record(200);\n    \t\t\n    \t} else if ( executeTime > 200 && executeTime <= 1000 ) {\n    \t\tthis.timeHistogram.record(1000);\n    \t\t\n    \t} else if ( executeTime > 1000) {\n    \t\tthis.timeHistogram.record(2000);\n    \t}\n    \t\n    \t//SQL执行所在的时间区间\t\n\t\tlong hour0 = endTime / ( 24L * (long)one_hour ) * ( 24L * (long)one_hour )- (long)time_zone_offset;\n\t\tlong hour06 = hour0 + 6L * (long)one_hour - 1L;\n\t\tlong hour13 = hour0 + 13L * (long)one_hour - 1L;\n\t\tlong hour18 = hour0 + 18L * (long)one_hour - 1L;\n\t\tlong hour22 = hour0 + 22L * (long)one_hour - 1L;\n\t\t\n\t\tif ( endTime <= hour06 || endTime > hour22 ) {\n\t\t\tthis.executeHistogram.record(6);\n\t\t\t\n\t\t} else if ( endTime > hour06 && endTime <= hour13 ) {\n\t\t\tthis.executeHistogram.record(13);\n\t\t\t\n\t\t} else if ( endTime > hour13 && endTime <= hour18 ) {\n\t\t\tthis.executeHistogram.record(18);\n\t\t\t\n\t\t} else if ( endTime > hour18 && endTime <= hour22 ) {\n\t\t\tthis.executeHistogram.record(22);\t\n\t\t}\t\t\n\t\t\n\t\tthis.lastExecuteTime = endTime;\n\t\t\n\t\tthis.netInBytes.addAndGet(netInBytes);\n\t\tthis.netOutBytes.addAndGet(netOutBytes);\n\t}\n\t\n    public long getLastExecuteTime() {\n        return lastExecuteTime;\n    }\t\n    \n    public long getNetInBytes() {\n    \treturn netInBytes.get();\n    }\n    \n    public long getNetOutBytes() {\n    \treturn netOutBytes.get();\n    }\n\n\tpublic int getConcurrentMax() {\n        return concurrentMax;\n    }\n\t\n\tpublic void setConcurrentMax(int concurrentMax) {\n\t\tthis.concurrentMax = concurrentMax;\n\t}\n\t\n    public int getQps() {\n\t\treturn qps;\n\t}\n\n\tpublic void setQps(int qps) {\n\t\tthis.qps = qps;\n\t}\n\n\tpublic long getRCount() {\n        return this.rCount.get();\n    }\n    \n    public long getWCount() {\n        return this.wCount.get();\n    }\n\t\n\tpublic Histogram getTimeHistogram() {\n        return this.timeHistogram;\n    }\n\t\n\tpublic Histogram getExecuteHistogram() {\n        return this.executeHistogram;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/UserStat.java",
    "content": "package io.mycat.statistic.stat;\n\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\nimport io.mycat.MycatServer;\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.statistic.SQLRecord;\nimport io.mycat.statistic.SQLRecorder;\n\n/**\n * 用户状态\n * \n * @author Ben\n */\npublic class UserStat {\n\t\n\tprivate long SQL_SLOW_TIME = 100;\n\t\n\tprivate String user;\n\t\n\t/**\n\t * 最大的并发\n\t */\n    private final AtomicInteger runningCount  = new AtomicInteger();\n\tprivate final AtomicInteger concurrentMax = new AtomicInteger();\n\t\n\t/**\n\t * SQL 大集合插入、返回记录\n\t */\n\tprivate UserSqlLargeStat sqlLargeStat = null;\n\t\n\t/**\n\t * SQL 执行记录\n\t */\n\tprivate UserSqlLastStat sqlLastStat = null;\n\t\n\t/**\n\t * CURD 执行分布\n\t */\n\tprivate UserSqlRWStat sqlRwStat = null;\n\t\n\t/**\n\t * 用户高频SQL分析\n\t */\n\tprivate UserSqlHighStat sqlHighStat = null;\n\t\n\t/**\n\t * 慢查询记录器  TOP 10\n\t */\n\tprivate SQLRecorder sqlRecorder;\n\t\n\t/**\n\t * 大结果集记录\n\t */\n\tprivate SqlResultSizeRecorder sqlResultSizeRecorder = null;\n\t\n\t/**\n\t * 读写锁\n\t */\n//\tprivate ReentrantReadWriteLock lock  = new ReentrantReadWriteLock();\n\n\tpublic UserStat(String user) {\n\t\tsuper();\n\n\t\tint size = MycatServer.getInstance().getConfig().getSystem().getSqlRecordCount();\n\t\t\n\t\tthis.user = user;\t\t\n\t\tthis.sqlRwStat = new UserSqlRWStat();\n\t\tthis.sqlLastStat = new UserSqlLastStat(50);\n\t\tthis.sqlLargeStat = new UserSqlLargeStat(10);\n\t\tthis.sqlHighStat = new UserSqlHighStat();\t\t\n\t\tthis.sqlRecorder = new SQLRecorder( size );\n\t\tthis.sqlResultSizeRecorder =  new SqlResultSizeRecorder();\n\t}\n\n\tpublic String getUser() {\n\t\treturn user;\n\t}\n\n\tpublic SQLRecorder getSqlRecorder() {\n\t\treturn sqlRecorder;\n\t}\n\n\tpublic UserSqlRWStat getRWStat() {\n\t\treturn sqlRwStat;\n\t}\n\n\tpublic UserSqlLastStat getSqlLastStat() {\n\t\treturn this.sqlLastStat;\n\t}\n\t\n\tpublic UserSqlLargeStat getSqlLargeRowStat() {\n\t\treturn this.sqlLargeStat;\n\t}\n\t\n\tpublic UserSqlHighStat getSqlHigh(){\n\t\treturn this.sqlHighStat;\n\t}\n\t\n\tpublic SqlResultSizeRecorder getSqlResultSizeRecorder() {\n\t\treturn this.sqlResultSizeRecorder;\n\t}\n\t\n\t\n\tpublic void setSlowTime(long time) {\n\t\tthis.SQL_SLOW_TIME = time;\n\t\tthis.sqlRecorder.clear();\n\t}\n\t\n\tpublic void clearSql() {\n\t\tthis.sqlLastStat.reset();\n\t}\n\t\n\tpublic void clearSqlslow() {\n\t\tthis.sqlRecorder.clear();\n\t}\n\t\n\tpublic void clearRwStat() {\n\t\tthis.sqlRwStat.reset();\n\t}\n\t\n\tpublic void reset() {\t\t\n\t\tthis.sqlRecorder.clear();\n\t\tthis.sqlResultSizeRecorder.clearSqlResultSet();\n\t\tthis.sqlRwStat.reset();\n\t\tthis.sqlLastStat.reset();\n\t\t\n\t\tthis.runningCount.set(0);\n\t\tthis.concurrentMax.set(0);\n\t}\n\t\n\t/**\n\t * 更新状态\n\t * \n\t * @param sqlType\n\t * @param sql\n\t * @param startTime\n\t */\n    public void update(String schema, int sqlType, String sql, long sqlRows,\n\t\t\tlong netInBytes, long netOutBytes, long startTime, long endTime ,int rseultSetSize,String host) {\n\t\t\n\t\t//before 计算最大并发数\n\t\t//-----------------------------------------------------\n\t\tint invoking = runningCount.incrementAndGet();\n        for (;;) {\n            int max = concurrentMax.get();\n            if (invoking > max) {\n                if (concurrentMax.compareAndSet(max, invoking)) {\n                    break;\n                }\n            } else {\n                break;\n            }\n        }\n        //-----------------------------------------------------\n\t\t\n//\t\tthis.lock.writeLock().lock();\n//        try {\n        \t\n\t\t\t//慢查询记录\n    \t\t\tlong executeTime = endTime - startTime;\t\t\n    \t\t\tif ( executeTime >= SQL_SLOW_TIME ){\t\t\t\n    \t\t\t\tSQLRecord record = new SQLRecord();\n    \t\t\t\trecord.executeTime = executeTime;\n    \t\t\t\trecord.statement = sql;\n    \t\t\t\trecord.startTime = startTime;\n    \t\t\t\trecord.host = host;\n                    record.schema = schema;\n    \t\t\t\tthis.sqlRecorder.add(record);\n    \t\t\t}\n\t\t\t\n\t\t\t//执行状态记录\n\t\t\tthis.sqlRwStat.setConcurrentMax( concurrentMax.get() );\n\t\t\tthis.sqlRwStat.add(sqlType, sql, executeTime, netInBytes, netOutBytes, startTime, endTime);\n\t\t\t\n\t\t\t//记录最新执行的SQL\n\t\t\tthis.sqlLastStat.add(sql, executeTime, startTime, endTime ,host);\n\t\t\t\n\t\t\t//记录高频SQL\n\t\t\tthis.sqlHighStat.addSql(sql, executeTime, startTime, endTime,host);\n\t\t\t\n\t\t\t//记录SQL Select 返回超过 10000 行的 大结果集\n\t\t\tif ( sqlType == ServerParse.SELECT && sqlRows > 10000 ) {\n\t\t\t\tthis.sqlLargeStat.add(sql, sqlRows, executeTime, startTime, endTime,host);\n\t\t\t}\n\t\t\t\n\t\t\t//记录超过阈值的大结果集sql\n\t\t\tif(rseultSetSize>=MycatServer.getInstance().getConfig().getSystem().getMaxResultSet()){\n\t\t\t    this.sqlResultSizeRecorder.addSql(sql, rseultSetSize);\n\t\t\t}\n\t\t\t\n//        } finally {\n//        \tthis.lock.writeLock().unlock();\n//        }\n        \n\t\t//after\n\t\t//-----------------------------------------------------\n\t\trunningCount.decrementAndGet();\t\t\n\t}\n}"
  },
  {
    "path": "src/main/java/io/mycat/statistic/stat/UserStatAnalyzer.java",
    "content": "package io.mycat.statistic.stat;\n\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.ExecutionException;\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport io.mycat.server.parser.ServerParse;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 按访问用户 计算SQL的运行状态\n * \n * @author Ben\n *\n */\npublic class UserStatAnalyzer implements QueryResultListener {\n\t\n\tprivate Cache<String, UserStat> userStatMap = CacheBuilder.newBuilder().maximumSize(8192).build();\n\t\n    private final static UserStatAnalyzer instance  = new UserStatAnalyzer();\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(UserStatAnalyzer.class);\n\n    private UserStatAnalyzer() {\n    }\n    \n    public static UserStatAnalyzer getInstance() {\n        return instance;\n    }  \n\t\n\t@Override\n\tpublic void onQueryResult(QueryResult query) {\t\t\n\t\tswitch( query.getSqlType() ) {\n    \tcase ServerParse.SELECT:\t\t\n    \tcase ServerParse.UPDATE:\t\t\t\n    \tcase ServerParse.INSERT:\t\t\n    \tcase ServerParse.DELETE:\n    \tcase ServerParse.REPLACE:\n\t\t\tString host = query.getHost();\n\t\t\tif (host==null){\n\t\t\t\thost = \"\";\n\t\t\t}\n\n        String schema = query.getSchema();\n    \t\tString user = query.getUser();\n    \t\tint sqlType = query.getSqlType();\n    \t\tString sql = query.getSql();\n    \t\tlong sqlRows = query.getSqlRows();\n    \t\tlong netInBytes = query.getNetInBytes();\n    \t\tlong netOutBytes = query.getNetOutBytes();\n    \t\tlong startTime = query.getStartTime();\n    \t\tlong endTime = query.getEndTime();\n    \t\tint resultSetSize=query.getResultSize();\n    \t\ttry {\n\t\t\t\tUserStat userStat = userStatMap.get(user, new Callable<UserStat>() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic UserStat call() throws Exception {\n\t\t\t\t\t\treturn new UserStat(user);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tuserStat.update(schema,sqlType, sql, sqlRows, netInBytes, netOutBytes, startTime, endTime, resultSetSize, host);\n\t\t\t}catch (ExecutionException e){\n\t\t\t\tLOGGER.error(\"new UserStat occurs error\",e);\n\t\t\t}\n      break;\n\t\t}\n\t}\n\t\n\tpublic Map<String, UserStat> getUserStatMap() {\n\t\treturn userStatMap.asMap();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/ByteBufferUtil.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.mycat.util;\n\n/*\n * BE ADVISED: New imports added here might introduce new dependencies for\n * the clientutil jar.  If in doubt, run the `ant test-clientutil-jar' target\n * afterward, and ensure the tests still pass.\n */\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.InetAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.CharacterCodingException;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\n\n\n\n/**\n * Utility methods to make ByteBuffers less painful\n * The following should illustrate the different ways byte buffers can be used\n *\n *        public void testArrayOffet()\n *        {\n *\n *            byte[] b = \"test_slice_array\".getBytes();\n *            ByteBuffer bb = ByteBuffer.allocate(1024);\n *\n *            assert bb.position() == 0;\n *            assert bb.limit()    == 1024;\n *            assert bb.capacity() == 1024;\n *\n *            bb.put(b);\n *\n *            assert bb.position()  == b.length;\n *            assert bb.remaining() == bb.limit() - bb.position();\n *\n *            ByteBuffer bb2 = bb.slice();\n *\n *            assert bb2.position()    == 0;\n *\n *            //slice should begin at other buffers current position\n *            assert bb2.arrayOffset() == bb.position();\n *\n *            //to match the position in the underlying array one needs to\n *            //track arrayOffset\n *            assert bb2.limit()+bb2.arrayOffset() == bb.limit();\n *\n *\n *            assert bb2.remaining() == bb.remaining();\n *\n *        }\n *\n * }\n *\n */\npublic class ByteBufferUtil\n{\n    public static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.wrap(new byte[0]);\n\n\n    public static int compareUnsigned(ByteBuffer o1, ByteBuffer o2)\n    {\n        return FastByteOperations.compareUnsigned(o1, o2);\n    }\n\n\n    public static int compare(byte[] o1, ByteBuffer o2)\n    {\n        return FastByteOperations.compareUnsigned(o1, 0, o1.length, o2);\n    }\n\n\n    public static int compare(ByteBuffer o1, byte[] o2)\n    {\n        return FastByteOperations.compareUnsigned(o1, o2, 0, o2.length);\n    }\n\n    /**\n     * Decode a String representation.\n     * This method assumes that the encoding charset is UTF_8.\n     *\n     * @param buffer a byte buffer holding the string representation\n     * @return the decoded string\n     */\n    public static String string(ByteBuffer buffer) throws CharacterCodingException\n    {\n        return string(buffer, StandardCharsets.UTF_8);\n    }\n\n    /**\n     * Decode a String representation.\n     * This method assumes that the encoding charset is UTF_8.\n     *\n     * @param buffer a byte buffer holding the string representation\n     * @param position the starting position in {@code buffer} to start decoding from\n     * @param length the number of bytes from {@code buffer} to use\n     * @return the decoded string\n     */\n    public static String string(ByteBuffer buffer, int position, int length) throws CharacterCodingException\n    {\n        return string(buffer, position, length, StandardCharsets.UTF_8);\n    }\n\n    /**\n     * Decode a String representation.\n     *\n     * @param buffer a byte buffer holding the string representation\n     * @param position the starting position in {@code buffer} to start decoding from\n     * @param length the number of bytes from {@code buffer} to use\n     * @param charset the String encoding charset\n     * @return the decoded string\n     */\n    public static String string(ByteBuffer buffer, int position, int length, Charset charset) throws CharacterCodingException\n    {\n        ByteBuffer copy = buffer.duplicate();\n        copy.position(position);\n        copy.limit(copy.position() + length);\n        return string(copy, charset);\n    }\n\n    /**\n     * Decode a String representation.\n     *\n     * @param buffer a byte buffer holding the string representation\n     * @param charset the String encoding charset\n     * @return the decoded string\n     */\n    public static String string(ByteBuffer buffer, Charset charset) throws CharacterCodingException\n    {\n        return charset.newDecoder().decode(buffer.duplicate()).toString();\n    }\n\n    /**\n     * You should almost never use this.  Instead, use the write* methods to avoid copies.\n     */\n    public static byte[] getArray(ByteBuffer buffer)\n    {\n        int length = buffer.remaining();\n\n        if (buffer.hasArray())\n        {\n            int boff = buffer.arrayOffset() + buffer.position();\n            return Arrays.copyOfRange(buffer.array(), boff, boff + length);\n        }\n        // else, DirectByteBuffer.get() is the fastest route\n        byte[] bytes = new byte[length];\n        buffer.duplicate().get(bytes);\n\n        return bytes;\n    }\n\n    /**\n     * ByteBuffer adaptation of org.apache.commons.lang3.ArrayUtils.lastIndexOf method\n     *\n     * @param buffer the array to traverse for looking for the object, may be <code>null</code>\n     * @param valueToFind the value to find\n     * @param startIndex the start index (i.e. BB position) to travers backwards from\n     * @return the last index (i.e. BB position) of the value within the array\n     * [between buffer.position() and buffer.limit()]; <code>-1</code> if not found.\n     */\n    public static int lastIndexOf(ByteBuffer buffer, byte valueToFind, int startIndex)\n    {\n        assert buffer != null;\n\n        if (startIndex < buffer.position())\n        {\n            return -1;\n        }\n        else if (startIndex >= buffer.limit())\n        {\n            startIndex = buffer.limit() - 1;\n        }\n\n        for (int i = startIndex; i >= buffer.position(); i--)\n        {\n            if (valueToFind == buffer.get(i)) {\n                return i;\n            }\n        }\n\n        return -1;\n    }\n\n    /**\n     * Encode a String in a ByteBuffer using UTF_8.\n     *\n     * @param s the string to encode\n     * @return the encoded string\n     */\n    public static ByteBuffer bytes(String s)\n    {\n        return ByteBuffer.wrap(s.getBytes(StandardCharsets.UTF_8));\n    }\n\n    /**\n     * Encode a String in a ByteBuffer using the provided charset.\n     *\n     * @param s the string to encode\n     * @param charset the String encoding charset to use\n     * @return the encoded string\n     */\n    public static ByteBuffer bytes(String s, Charset charset)\n    {\n        return ByteBuffer.wrap(s.getBytes(charset));\n    }\n\n    /**\n     * @return a new copy of the data in @param buffer\n     * USUALLY YOU SHOULD USE ByteBuffer.duplicate() INSTEAD, which creates a new Buffer\n     * (so you can mutate its position without affecting the original) without copying the underlying array.\n     */\n    public static ByteBuffer clone(ByteBuffer buffer)\n    {\n        assert buffer != null;\n\n        if (buffer.remaining() == 0) {\n            return EMPTY_BYTE_BUFFER;\n        }\n\n        ByteBuffer clone = ByteBuffer.allocate(buffer.remaining());\n\n        if (buffer.hasArray())\n        {\n            System.arraycopy(buffer.array(), buffer.arrayOffset() + buffer.position(), clone.array(), 0, buffer.remaining());\n        }\n        else\n        {\n            clone.put(buffer.duplicate());\n            clone.flip();\n        }\n\n        return clone;\n    }\n\n    public static void arrayCopy(ByteBuffer src, int srcPos, byte[] dst, int dstPos, int length)\n    {\n        FastByteOperations.copy(src, srcPos, dst, dstPos, length);\n    }\n\n    /**\n     * Transfer bytes from one ByteBuffer to another.\n     * This function acts as System.arrayCopy() but for ByteBuffers.\n     *\n     * @param src the source ByteBuffer\n     * @param srcPos starting position in the source ByteBuffer\n     * @param dst the destination ByteBuffer\n     * @param dstPos starting position in the destination ByteBuffer\n     * @param length the number of bytes to copy\n     */\n    public static void arrayCopy(ByteBuffer src, int srcPos, ByteBuffer dst, int dstPos, int length)\n    {\n        FastByteOperations.copy(src, srcPos, dst, dstPos, length);\n    }\n\n\n\n    public static void writeWithLength(byte[] bytes, DataOutput out) throws IOException\n    {\n        out.writeInt(bytes.length);\n        out.write(bytes);\n    }\n\n\n\n\n\n    /* @return An unsigned short in an integer. */\n    public static int readShortLength(DataInput in) throws IOException\n    {\n        return in.readUnsignedShort();\n    }\n\n\n\n\n\n\n    public static byte[] readBytes(DataInput in, int length) throws IOException\n    {\n        assert length > 0 : \"length is not > 0: \" + length;\n        byte[] bytes = new byte[length];\n        in.readFully(bytes);\n        return bytes;\n    }\n\n    /**\n     * Convert a byte buffer to an integer.\n     * Does not change the byte buffer position.\n     *\n     * @param bytes byte buffer to convert to integer\n     * @return int representation of the byte buffer\n     */\n    public static int toInt(ByteBuffer bytes)\n    {\n        return bytes.getInt(bytes.position());\n    }\n\n    public static long toLong(ByteBuffer bytes)\n    {\n        return bytes.getLong(bytes.position());\n    }\n\n    public static float toFloat(ByteBuffer bytes)\n    {\n        return bytes.getFloat(bytes.position());\n    }\n\n    public static double toDouble(ByteBuffer bytes)\n    {\n        return bytes.getDouble(bytes.position());\n    }\n\n    public static ByteBuffer bytes(int i)\n    {\n        return ByteBuffer.allocate(4).putInt(0, i);\n    }\n\n    public static ByteBuffer bytes(long n)\n    {\n        return ByteBuffer.allocate(8).putLong(0, n);\n    }\n\n    public static ByteBuffer bytes(float f)\n    {\n        return ByteBuffer.allocate(4).putFloat(0, f);\n    }\n\n    public static ByteBuffer bytes(double d)\n    {\n        return ByteBuffer.allocate(8).putDouble(0, d);\n    }\n\n    public static InputStream inputStream(ByteBuffer bytes)\n    {\n        final ByteBuffer copy = bytes.duplicate();\n\n        return new InputStream()\n        {\n            public int read()\n            {\n                if (!copy.hasRemaining()) {\n                    return -1;\n                }\n\n                return copy.get() & 0xFF;\n            }\n\n            @Override\n            public int read(byte[] bytes, int off, int len)\n            {\n                if (!copy.hasRemaining()) {\n                    return -1;\n                }\n\n                len = Math.min(len, copy.remaining());\n                copy.get(bytes, off, len);\n                return len;\n            }\n\n            @Override\n            public int available()\n            {\n                return copy.remaining();\n            }\n        };\n    }\n\n\n\n\n\n    /**\n     * Compare two ByteBuffer at specified offsets for length.\n     * Compares the non equal bytes as unsigned.\n     * @param bytes1 First byte buffer to compare.\n     * @param offset1 Position to start the comparison at in the first array.\n     * @param bytes2 Second byte buffer to compare.\n     * @param offset2 Position to start the comparison at in the second array.\n     * @param length How many bytes to compare?\n     * @return -1 if byte1 is less than byte2, 1 if byte2 is less than byte1 or 0 if equal.\n     */\n    public static int compareSubArrays(ByteBuffer bytes1, int offset1, ByteBuffer bytes2, int offset2, int length)\n    {\n        if (bytes1 == null) {\n            return bytes2 == null ? 0 : -1;\n        }\n        if (bytes2 == null) {\n            return 1;\n        }\n\n        assert bytes1.limit() >= offset1 + length : \"The first byte array isn't long enough for the specified offset and length.\";\n        assert bytes2.limit() >= offset2 + length : \"The second byte array isn't long enough for the specified offset and length.\";\n        for (int i = 0; i < length; i++)\n        {\n            byte byte1 = bytes1.get(offset1 + i);\n            byte byte2 = bytes2.get(offset2 + i);\n//            if (byte1 == byte2)\n//                continue;\n            // compare non-equal bytes as unsigned\n            if( byte1 != byte2 ) {\n                return (byte1 & 0xFF) < (byte2 & 0xFF) ? -1 : 1;\n            }\n        }\n        return 0;\n    }\n\n    public static ByteBuffer bytes(InetAddress address)\n    {\n        return ByteBuffer.wrap(address.getAddress());\n    }\n\n\n\n    // Returns whether {@code prefix} is a prefix of {@code value}.\n    public static boolean isPrefix(ByteBuffer prefix, ByteBuffer value)\n    {\n        if (prefix.remaining() > value.remaining()) {\n            return false;\n        }\n\n        int diff = value.remaining() - prefix.remaining();\n        return prefix.equals(value.duplicate().limit(value.remaining() - diff));\n    }\n\n    /** trims size of bytebuffer to exactly number of bytes in it, to do not hold too much memory */\n    public static ByteBuffer minimalBufferFor(ByteBuffer buf)\n    {\n        return buf.capacity() > buf.remaining() || !buf.hasArray() ? ByteBuffer.wrap(getArray(buf)) : buf;\n    }\n\n    // Doesn't change bb position\n    public static int getShortLength(ByteBuffer bb, int position)\n    {\n        int length = (bb.get(position) & 0xFF) << 8;\n        return length | (bb.get(position + 1) & 0xFF);\n    }\n\n    // changes bb position\n    public static int readShortLength(ByteBuffer bb)\n    {\n        int length = (bb.get() & 0xFF) << 8;\n        return length | (bb.get() & 0xFF);\n    }\n\n    // changes bb position\n    public static void writeShortLength(ByteBuffer bb, int length)\n    {\n        bb.put((byte) ((length >> 8) & 0xFF));\n        bb.put((byte) (length & 0xFF));\n    }\n\n    // changes bb position\n    public static ByteBuffer readBytes(ByteBuffer bb, int length)\n    {\n        ByteBuffer copy = bb.duplicate();\n        copy.limit(copy.position() + length);\n        bb.position(bb.position() + length);\n        return copy;\n    }\n\n    // changes bb position\n    public static ByteBuffer readBytesWithShortLength(ByteBuffer bb)\n    {\n        int length = readShortLength(bb);\n        return readBytes(bb, length);\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/ByteUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.nio.charset.Charset;\nimport java.util.Date;\n\npublic class ByteUtil {\n\n\t/**\n\t * compare to number or dicamal ascii byte array, for number :123456 ,store\n\t * to array [1,2,3,4,5,6]\n\t * \n\t * @param b1\n\t * @param b2\n\t * @return -1 means b1 < b2, or 0 means b1=b2 else return 1\n\t */\n\tpublic static int compareNumberByte(byte[] b1, byte[] b2) {\n\t\tif(b1 == null || b1.length == 0) {\n\t\t\treturn -1;\n\t\t}\n\t\telse if(b2 == null || b2.length == 0) {\n\t\t\treturn 1;\n\t\t}\n\t\tboolean isNegetive = b1[0] == 45 || b2[0] == 45;\n\t\tif (isNegetive == false && b1.length != b2.length) {\n\t\t\treturn b1.length - b2.length;\n\t\t}\n\t\tint len = b1.length > b2.length ? b2.length : b1.length;\n\t\tint result = 0;\n\t\tint index = -1;\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tint b1val = b1[i];\n\t\t\tint b2val = b2[i];\n\t\t\tif (b1val > b2val) {\n\t\t\t\tresult = 1;\n\t\t\t\tindex = i;\n\t\t\t\tbreak;\n\t\t\t} else if (b1val < b2val) {\n\t\t\t\tindex = i;\n\t\t\t\tresult = -1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (index == 0) {\n\t\t\t// first byte compare\n\t\t\treturn result;\n\t\t} else {\n            if( b1.length != b2.length ) {\n\n                int lenDelta = b1.length - b2.length;\n                return isNegetive ? 0 - lenDelta : lenDelta;\n\n            } else {\n                return isNegetive ? 0 - result : result;\n            }\n\t\t}\n\t}\n\n\tpublic static byte[] compareNumberArray2(byte[] b1, byte[] b2, int order) {\n\t\tif (b1.length <= 0 && b2.length > 0) {\n\t\t\treturn b2;\n\t\t}\n\t\tif (b1.length > 0 && b2.length <= 0) {\n\t\t\treturn b1;\n\t\t}\n\t\tint len = b1.length > b2.length ? b1.length : b2.length;\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tif (b1[i] != b2[i]) {\n\t\t\t\tif (order == 1) {\n\t\t\t\t\treturn ((b1[i] & 0xff) - (b2[i] & 0xff)) > 0 ? b1 : b2;\n\t\t\t\t} else {\n\t\t\t\t\treturn ((b1[i] & 0xff) - (b2[i] & 0xff)) > 0 ? b2 : b1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn b1;\n\t}\n\n\tpublic static byte[] getBytes(short data) {\n\t\tbyte[] bytes = new byte[2];\n\t\tbytes[0] = (byte) (data & 0xff);\n\t\tbytes[1] = (byte) ((data & 0xff00) >> 8);\n\t\treturn bytes;\n\t}\n\n\tpublic static byte[] getBytes(char data) {\n\t\tbyte[] bytes = new byte[2];\n\t\tbytes[0] = (byte) (data);\n\t\tbytes[1] = (byte) (data >> 8);\n\t\treturn bytes;\n\t}\n\n\tpublic static byte[] getBytes(int data) {\n\t\tbyte[] bytes = new byte[4];\n\t\tbytes[0] = (byte) (data & 0xff);\n\t\tbytes[1] = (byte) ((data & 0xff00) >> 8);\n\t\tbytes[2] = (byte) ((data & 0xff0000) >> 16);\n\t\tbytes[3] = (byte) ((data & 0xff000000) >> 24);\n\t\treturn bytes;\n\t}\n\n\tpublic static byte[] getBytes(long data) {\n\t\tbyte[] bytes = new byte[8];\n\t\tbytes[0] = (byte) (data & 0xff);\n\t\tbytes[1] = (byte) ((data >> 8) & 0xff);\n\t\tbytes[2] = (byte) ((data >> 16) & 0xff);\n\t\tbytes[3] = (byte) ((data >> 24) & 0xff);\n\t\tbytes[4] = (byte) ((data >> 32) & 0xff);\n\t\tbytes[5] = (byte) ((data >> 40) & 0xff);\n\t\tbytes[6] = (byte) ((data >> 48) & 0xff);\n\t\tbytes[7] = (byte) ((data >> 56) & 0xff);\n\t\treturn bytes;\n\t}\n\n\tpublic static byte[] getBytes(float data) {\n\t\tint intBits = Float.floatToIntBits(data);\n\t\treturn getBytes(intBits);\n\t}\n\n\tpublic static byte[] getBytes(double data) {\n\t\tlong intBits = Double.doubleToLongBits(data);\n\t\treturn getBytes(intBits);\n\t}\n\n\tpublic static byte[] getBytes(String data, String charsetName) {\n\t\tCharset charset = Charset.forName(charsetName);\n\t\treturn data.getBytes(charset);\n\t}\n\n\tpublic static byte[] getBytes(String data) {\n\t\treturn getBytes(data, \"GBK\");\n\t}\n\n\tpublic static short getShort(byte[] bytes) {\n\t\treturn Short.parseShort(new String(bytes));\n//\t\treturn (short) ((0xff & bytes[0]) | (0xff00 & (bytes[1] << 8)));\n\t}\n\n\tpublic static char getChar(byte[] bytes) {\n\t\treturn (char) ((0xff & bytes[0]) | (0xff00 & (bytes[1] << 8)));\n\t}\n\n\tpublic static int getInt(byte[] bytes) {\n\t\treturn Integer.parseInt(new String(bytes));\n\t\t// return (0xff & bytes[0]) | (0xff00 & (bytes[1] << 8)) | (0xff0000 &\n\t\t// (bytes[2] << 16)) | (0xff000000 & (bytes[3] << 24));\n\t}\n\n\tpublic static long getLong(byte[] bytes) {\n\t\treturn Long.parseLong(new String(bytes));\n\t\t// return(0xffL & (long)bytes[0]) | (0xff00L & ((long)bytes[1] << 8)) |\n\t\t// (0xff0000L & ((long)bytes[2] << 16)) | (0xff000000L & ((long)bytes[3]\n\t\t// << 24))\n\t\t// | (0xff00000000L & ((long)bytes[4] << 32)) | (0xff0000000000L &\n\t\t// ((long)bytes[5] << 40)) | (0xff000000000000L & ((long)bytes[6] <<\n\t\t// 48)) | (0xff00000000000000L & ((long)bytes[7] << 56));\n\t}\n\n\tpublic static double getDouble(byte[] bytes) {\n\t\treturn Double.parseDouble(new String(bytes));\n\t}\n\t\n\tpublic static float getFloat(byte[] bytes) {\n\t\treturn Float.parseFloat(new String(bytes));\n\t}\n\n\tpublic static String getString(byte[] bytes, String charsetName) {\n\t\treturn new String(bytes, Charset.forName(charsetName));\n\t}\n\n\tpublic static String getString(byte[] bytes) {\n\t\treturn getString(bytes, \"UTF-8\");\n\t}\n\n\tpublic static String getDate(byte[] bytes) {\n\t\treturn new String(bytes);\n\t}\n\t\n\tpublic static String getTime(byte[] bytes) {\n\t\treturn new String(bytes);\n\t}\n\n\tpublic static String getTimestmap(byte[] bytes) {\n\t\treturn new String(bytes);\n\t}\n\t\n\tpublic static byte[] getBytes(Date date, boolean isTime) {\n\t\tif(isTime) {\n\t\t\treturn getBytesFromTime(date);\n\t\t} else {\n\t\t\treturn getBytesFromDate(date);\n\t\t}\n    }\n\n    /**\n     * short从ASCII类型的值转换成数学二进制值。java里面不存在unsigned short，因此转换mysql unsiged值时需要用到java int.\n     * @param bytes  mysql里面的unsigned short字段，采用ASCII类型表达的值\n     * @return  数学二进制值\n     */\n    public static byte[] convertUnsignedShort2Binary(byte[] bytes) {\n        int value = Integer.parseInt(new String(bytes));\n        byte[] binaryBytes = new byte[2];\n        binaryBytes[0] = (byte) (value & 0xff);\n        binaryBytes[1] = (byte) ((value & 0xff00) >> 8);\n        return binaryBytes;\n    }\n\tprivate static byte[] getBytesFromTime(Date date) {\n\t\tint day = 0;\n\t\tint hour = DateUtil.getHour(date);\n\t\tint minute = DateUtil.getMinute(date);\n    \tint second = DateUtil.getSecond(date);\n    \tint microSecond = DateUtil.getMicroSecond(date);\n    \tbyte[] bytes = null;\n    \tbyte[] tmp = null;\n    \tif(day == 0 && hour == 0 && minute == 0\n    \t\t\t&& second == 0 && microSecond == 0) {\n    \t\tbytes = new byte[1];\n    \t\tbytes[0] = (byte) 0;\n    \t} else if(microSecond == 0) {\n    \t\tbytes = new byte[1 + 8];\n    \t\tbytes[0] = (byte) 8;\n    \t\tbytes[1] = (byte) 0; // is_negative (1) -- (1 if minus, 0 for plus)\n    \t\ttmp = getBytes(day);\n    \t\tbytes[2] = tmp[0];\n    \t\tbytes[3] = tmp[1];\n    \t\tbytes[4] = tmp[2];\n    \t\tbytes[5] = tmp[3];\n    \t\tbytes[6] = (byte) hour;\n    \t\tbytes[7] = (byte) minute;\n    \t\tbytes[8] = (byte) second;\n    \t} else {\n    \t\tbytes = new byte[1 + 12];\n    \t\tbytes[0] = (byte) 12;\n    \t\tbytes[1] = (byte) 0; // is_negative (1) -- (1 if minus, 0 for plus)\n    \t\ttmp = getBytes(day);\n    \t\tbytes[2] = tmp[0];\n    \t\tbytes[3] = tmp[1];\n    \t\tbytes[4] = tmp[2];\n    \t\tbytes[5] = tmp[3];\n    \t\tbytes[6] = (byte) hour;\n    \t\tbytes[7] = (byte) minute;\n    \t\tbytes[8] = (byte) second;\n    \t\ttmp = getBytes(microSecond);\n    \t\tbytes[9] = tmp[0];\n    \t\tbytes[10] = tmp[1];\n    \t\tbytes[11] = tmp[2];\n    \t\tbytes[12] = tmp[3];\n    \t}\n    \treturn bytes;\n\t}\n\t\n\tprivate static byte[] getBytesFromDate(Date date) {\n\t\tint year = DateUtil.getYear(date);\n    \tint month = DateUtil.getMonth(date);\n    \tint day = DateUtil.getDay(date);\n    \tint hour = DateUtil.getHour(date);\n    \tint minute = DateUtil.getMinute(date);\n    \tint second = DateUtil.getSecond(date);\n    \tint microSecond = DateUtil.getMicroSecond(date);\n    \tbyte[] bytes = null;\n    \tbyte[] tmp = null;\n    \tif(year == 0 && month == 0 && day == 0 \n    \t\t\t&& hour == 0 && minute == 0 && second == 0\n    \t\t\t&& microSecond == 0) {\n    \t\tbytes = new byte[1];\n    \t\tbytes[0] = (byte) 0;\n    \t} else if(hour == 0 && minute == 0 && second == 0\n    \t\t\t&& microSecond == 0) {\n    \t\tbytes = new byte[1 + 4];\n    \t\tbytes[0] = (byte) 4;\n    \t\ttmp = getBytes((short) year);\n    \t\tbytes[1] = tmp[0];\n    \t\tbytes[2] = tmp[1];\n    \t\tbytes[3] = (byte) month;\n    \t\tbytes[4] = (byte) day;\n    \t} else if(microSecond == 0) {\n    \t\tbytes = new byte[1 + 7];\n    \t\tbytes[0] = (byte) 7;\n    \t\ttmp = getBytes((short) year);\n    \t\tbytes[1] = tmp[0];\n    \t\tbytes[2] = tmp[1];\n    \t\tbytes[3] = (byte) month;\n    \t\tbytes[4] = (byte) day;\n    \t\tbytes[5] = (byte) hour;\n    \t\tbytes[6] = (byte) minute;\n    \t\tbytes[7] = (byte) second;\n    \t} else {\n    \t\tbytes = new byte[1 + 11];\n    \t\tbytes[0] = (byte) 11;\n    \t\ttmp = getBytes((short) year);\n    \t\tbytes[1] = tmp[0];\n    \t\tbytes[2] = tmp[1];\n    \t\tbytes[3] = (byte) month;\n    \t\tbytes[4] = (byte) day;\n    \t\tbytes[5] = (byte) hour;\n    \t\tbytes[6] = (byte) minute;\n    \t\tbytes[7] = (byte) second;\n    \t\ttmp = getBytes(microSecond);\n    \t\tbytes[8] = tmp[0];\n    \t\tbytes[9] = tmp[1];\n    \t\tbytes[10] = tmp[2];\n    \t\tbytes[11] = tmp[3];\n    \t}\n    \treturn bytes;\n\t}\n\t\n\t// 支持 byte dump\n\t//---------------------------------------------------------------------\n\tpublic static String dump(byte[] data, int offset, int length) {\n\t\t\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\" byte dump log \");\n\t\tsb.append(System.lineSeparator());\n\t\tsb.append(\" offset \").append( offset );\n\t\tsb.append(\" length \").append( length );\n\t\tsb.append(System.lineSeparator());\n\t\tint lines = (length - 1) / 16 + 1;\n\t\tfor (int i = 0, pos = 0; i < lines; i++, pos += 16) {\n\t\t\tsb.append(String.format(\"0x%04X \", i * 16));\n\t\t\tfor (int j = 0, pos1 = pos; j < 16; j++, pos1++) {\n\t\t\t\tsb.append(pos1 < length ? String.format(\"%02X \", data[offset + pos1]) : \"   \");\n\t\t\t}\n\t\t\tsb.append(\" \");\n\t\t\tfor (int j = 0, pos1 = pos; j < 16; j++, pos1++) {\n\t\t\t\tsb.append(pos1 < length ? print(data[offset + pos1]) : '.');\n\t\t\t}\n\t\t\tsb.append(System.lineSeparator());\n\t\t}\n\t\tsb.append(length).append(\" bytes\").append(System.lineSeparator());\n\t\treturn sb.toString();\n\t}\n\n\tpublic static char print(byte b) {\n\t\treturn (b < 32 || b > 127) ? '.' : (char) b;\n\t}\n\n\t/*\n\t * 返回小数点的位置\n\t * @return 找不到 ,其他都是小数点的位置\n\t * */\n\tpublic static int getDot(byte[] array) {\t\t\n\t\tfor(int s = 0; s < array.length; s++){\n\t\t   if(array[s] == 46) {\n\t\t\t   return s;\n\t\t   }\n\t\t}\n\t\treturn array.length;\n\t}\n\t/*\n\t * @return 返回是否是科学计数法\n\t * */\n\tpublic static boolean hasE(byte[] array) {\t\t\n\t\tfor(int s = 0; s < array.length; s++){\n\t\t   if(array[s] == 'E' || array[s] == 'e') {\n\t\t\t   return true;\n\t\t   }\n\t\t}\n\t\treturn false;\n\t}\n\t/*\n\t * @比較        對於b1取0 到b1End進行比較\n\t *          對於b2取0 到b2End進行比較\n\t * */\n\tpublic static int compareNumberByte(byte[] b1, int b1End, byte[] b2, int b2End) {\n\t\tif(b1 == null || b1.length == 0) {\n\t\t\treturn -1;\n\t\t}\n\t\telse if(b2 == null || b2.length == 0) {\n\t\t\treturn 1;\n\t\t}\n\t\tboolean isNegetive = b1[0] == 45 || b2[0] == 45; // 45 表示负数符号\n\n\t\t//正数 长度不等 直接返回 长度 \t\t//都是正数长度不等,直接返回长度的比较\n\t\tif (isNegetive == false && b1End != b2End) {\n\t\t\treturn b1End - b2End;\n\t\t}\n\t\t//取短的一个\n\t\tint len = b1End > b2End ? b2End : b1End;\n\t\tint result = 0;\n\t\tint index = -1;\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tint b1val = b1[i];\n\t\t\tint b2val = b2[i];\n\t\t\tif (b1val > b2val) {\n\t\t\t\tresult = 1;\n\t\t\t\tindex = i;\n\t\t\t\tbreak;\n\t\t\t} else if (b1val < b2val) {\n\t\t\t\tindex = i;\n\t\t\t\tresult = -1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\t\t\n\t\tif (index == 0) {\n\t\t\t//正负数直接符号比较\n\t\t\t// first byte compare ,数值与符号的比较 一正 一负数\n\t\t\treturn result;\n\t\t} else {\n            if( b1End != b2End ) {\n            \t//都是正数 长度不等, 都是负数 长度不等\n                int lenDelta = b1End - b2End;\n                return isNegetive ? 0 - lenDelta : lenDelta;\n\n            } else {\n            \t//长度相等 符号相同\n            \t//位数相同 直接取比较结果的 正数就是结果,否则就是比较结果取反\n                return isNegetive ? 0 - result : result;\n            }\n\t\t}\n\t}\n\t/*\n\t * double類型的b1 b2進行比較\n\t * 首先:判斷是否是科學計數法 是直接創建Double對象進行比較\n\t *      否則利用byte進行比較 \n\t *         先判斷整數位 再判斷小數位\n\t * */\n\tpublic static int compareDouble(byte[] b1, byte[] b2) {\n\t\tif(b1 == null || b1.length == 0) {\n\t\t\treturn -1;\n\t\t}\n\t\telse if(b2 == null || b2.length == 0) {\n\t\t\treturn 1;\n\t\t}\n\t\tboolean isHasE = hasE(b1) || hasE(b2);\n\t\tif(isHasE){\t\t\t\n\t\t\treturn Double.valueOf(new String(b1)).compareTo( Double.valueOf(new String(b2))) ;\n\t\t}\n\t\tint d1 = getDot(b1);\n\t\tint d2 = getDot(b2);\n\t\t//判斷整數位\n\t\tint result = compareNumberByte(b1, d1, b2, d2);\n\t\t//符号相等\n\t\tif(result == 0){\n\t\t\t//判斷小數位\n\t\t\tboolean isNegetive = b1[0] == 45 || b2[0] == 45; // 45 表示负数符号\n\t\t\tint xsLen1 = b1.length - d1;\n\t\t\tint xsLen2 = b2.length - d2;\n\t\t\tint len = xsLen1 > xsLen2 ? xsLen2 : xsLen1; //小数位数中的 小数\n\t\t\tint temp = 0;\n\t\t\tfor(int i = 0; i < len ; i++) {\n\t\t\t\ttemp = b1[i + d1] - b2 [i+ d2];\n\t\t\t\tif(temp != 0){\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(temp == 0){\n\t\t\t\t//0.12 0.123 或者 -0.12 -0.123\n\t\t\t\tint lenDelta = xsLen1 - xsLen2;\n                result = isNegetive? 0 - lenDelta : lenDelta;\n\t\t\t} else{\n\t\t\t\t//0.12 0.113 或者 -0.12 -0.113\n\t\t\t\tresult = isNegetive? 0 - temp : temp;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/CircularArrayList.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.util.AbstractList;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.RandomAccess;\n\npublic class CircularArrayList<E>\n        extends AbstractList<E> implements RandomAccess {\n  \n    private final int n; // buffer length\n    private final List<E> buf; // a List implementing RandomAccess\n    private int head = 0;\n    private int tail = 0;\n  \n    public CircularArrayList(int capacity) {\n        n = capacity + 1;\n        buf = new ArrayList<E>(Collections.nCopies(n, (E) null));\n    }\n  \n    public int capacity() {\n        return n - 1;\n    }\n  \n    private int wrapIndex(int i) {\n        int m = i % n;\n        if (m < 0) { // java modulus can be negative\n            m += n;\n        }\n        return m;\n    }\n  \n    // This method is O(n) but will never be called if the\n    // CircularArrayList is used in its typical/intended role.\n    private void shiftBlock(int startIndex, int endIndex) {\n        assert (endIndex > startIndex);\n        for (int i = endIndex - 1; i >= startIndex; i--) {\n            set(i + 1, get(i));\n        }\n    }\n  \n    @Override\n    public int size() {\n        return tail - head + (tail < head ? n : 0);\n    }\n  \n    @Override\n    public E get(int i) {\n        if (i < 0 || i >= size()) {\n            throw new IndexOutOfBoundsException();\n        }\n        return buf.get(wrapIndex(head + i));\n    }\n  \n    @Override\n    public E set(int i, E e) {\n        if (i < 0 || i >= size()) {\n            throw new IndexOutOfBoundsException();\n        }\n        return buf.set(wrapIndex(head + i), e);\n    }\n  \n    @Override\n    public void add(int i, E e) {\n        int s = size();\n        if (s == n - 1) {\n            throw new IllegalStateException(\"Cannot add element.\"\n                    + \" CircularArrayList is filled to capacity.\");\n        }\n        \n        if (i < 0 || i > s) {\n            throw new IndexOutOfBoundsException();\n        }\n        tail = wrapIndex(tail + 1);\n        if (i < s) {\n            shiftBlock(i, s);\n        }\n        set(i, e);\n    }\n  \n    @Override\n    public E remove(int i) {\n        int s = size();\n        if (i < 0 || i >= s) {\n            throw new IndexOutOfBoundsException();\n        }\n        E e = get(i);\n        if (i > 0) {\n            shiftBlock(0, i);\n        }\n        head = wrapIndex(head + 1);\n        return e;\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/CollectionUtil.java",
    "content": "/*\r\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\r\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\r\n *\r\n * This code is free software;Designed and Developed mainly by many Chinese \r\n * opensource volunteers. you can redistribute it and/or modify it under the \r\n * terms of the GNU General Public License version 2 only, as published by the\r\n * Free Software Foundation.\r\n *\r\n * This code is distributed in the hope that it will be useful, but WITHOUT\r\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r\n * version 2 for more details (a copy is included in the LICENSE file that\r\n * accompanied this code).\r\n *\r\n * You should have received a copy of the GNU General Public License version\r\n * 2 along with this work; if not, write to the Free Software Foundation,\r\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r\n * \r\n * Any questions about this component can be directed to it's project Web address \r\n * https://code.google.com/p/opencloudb/.\r\n *\r\n */\r\npackage io.mycat.util;\r\n\r\nimport java.util.Collection;\r\nimport java.util.Collections;\r\nimport java.util.HashSet;\r\nimport java.util.Map;\r\nimport java.util.Set;\r\n\r\n/**\r\n * @author mycat\r\n */\r\npublic class CollectionUtil {\r\n    /**\r\n     * @param orig\r\n     *            if null, return intersect\r\n     */\r\n    public static Set<? extends Object> intersectSet(Set<? extends Object> orig, Set<? extends Object> intersect) {\r\n        if (orig == null) {\r\n            return intersect;\r\n        }\r\n        if (intersect == null || orig.isEmpty()) {\r\n            return Collections.emptySet();\r\n        }\r\n        Set<Object> set = new HashSet<Object>(orig.size());\r\n        for (Object p : orig) {\r\n            if (intersect.contains(p)) {\r\n                set.add(p);\r\n            }\r\n        }\r\n        return set;\r\n    }\r\n    public static boolean isEmpty(Collection<?> collection){\r\n    \treturn collection==null || collection.isEmpty();\r\n    }\r\n    public static boolean isEmpty(Map<?,?> map){\r\n    \treturn map==null || map.isEmpty();\r\n    }\r\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/CompareUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CompareUtil {\n    private static final Logger LOGGER = LoggerFactory.getLogger(CompareUtil.class);\n\tpublic static int compareInt(int l,int r){\t \n\t\t  \n\t\t  if(l > r){\n\t\t\t  return 1;\n\t\t  }else if(l < r){\n\t\t\t  return -1;\n\t\t  }else{\n\t\t\t  return 0;\n\t\t  }\n\t\t  \n\t  }\n\t  \n\t  public static int compareDouble(double l,double r){\n\t\t  \n\t\t  if(l > r){\n\t\t\t  return 1;\n\t\t  }else if(l < r){\n\t\t\t  return -1;\n\t\t  }else{\n\t\t\t  return 0;\n\t\t  }\n\t\t  \n\t  } \n\t  public static int compareFloat(float l,float r){\n\t\t  \n\t\t  if(l > r){\n\t\t\t  return 1;\n\t\t  }else if(l < r){\n\t\t\t  return -1;\n\t\t  }else{\n\t\t\t  return 0;\n\t\t  }\n\t\t  \n\t  }\n\t  \n\t  public static int compareLong(long l,long r){ \n//\t\t  System.out.println(l + \"      \" +  r);\n\t\t  if(l > r){\n\t\t\t  return 1;\n\t\t  }else if(l < r){\n\t\t\t  return -1;\n\t\t  }else{\n\t\t\t  return 0;\n\t\t  }\n\t\t  \n\t  }\n\t  \n\t  public static int compareString(String l,String r){\n//\t\t  return compareStringForChinese(l,r);\n\t\t  if(l == null) {\n\t\t\t  return -1;\n\t\t  }\n\t\t   else if(r == null) {\n\t\t\t  return 1;\n\t\t  }\n\t\t  return l.compareTo(r); \n\t  }\n\t  \n\t  public static int compareChar(char l,char r){\n\t\t  \n\t\t  if(l > r){\n\t\t\t  return 1;\n\t\t  }else if(l < r){\n\t\t\t  return -1;\n\t\t  }else{\n\t\t\t  return 0;\n\t\t  }\n\t\t  \n\t  }\n\t  \n\t  public static int compareUtilDate(Object left,Object right){\n\t\t  \n\t\t  java.util.Date l = (java.util.Date)left;\n\t\t  java.util.Date r = (java.util.Date)right;\n\t\t  \n\t\t  return l.compareTo(r);\n\t\t  \n\t  }\n\t  \n\t  public static int compareSqlDate(Object left,Object right){\n\t\t  \n\t\t  java.sql.Date l = (java.sql.Date)left;\n\t\t  java.sql.Date r = (java.sql.Date)right;\n\t\t  \n\t\t  return l.compareTo(r);\n\t\t  \n\t  }\n\t  \n\t  \n    private static int compareStringForChinese(String s1, String s2) {\n        String m_s1 = null, m_s2 = null;\n        try {\n            //先将两字符串编码成GBK\n            m_s1 = new String(s1.getBytes(\"GB2312\"), \"GBK\");\n            m_s2 = new String(s2.getBytes(\"GB2312\"), \"GBK\");\n        } catch (Exception ex) {\n            LOGGER.error(\"compareStringForChineseError\", ex);\n            return s1.compareTo(s2);\n        }\n        int res = chineseCompareTo(m_s1, m_s2);\n        \n        //              System.out.println(\"比较：\" + s1 + \" | \" + s2 + \"==== Result: \" + res);\n        return res;\n    }\n \n\t//获取一个汉字/字母的Char值\n\tprivate static int getCharCode(String s){\n          if (s==null || s.length()==0) {\n\t\t\t  return -1;//保护代码\n\t\t  }\n          byte [] b = s.getBytes();\n          int value = 0;\n          //保证取第一个字符（汉字或者英文）\n          for (int i = 0; i < b.length && i <= 2; i ++){\n                 value = value*100+b[i];\n          }\n          if(value < 0){\n        \t  value += 100000;\n          }\n          \n          return value;\n   }\n\t \n\t//比较两个字符串\n\tprivate static int chineseCompareTo(String s1, String s2){\n\t\tint len1 = s1.length();\n\t\tint len2 = s2.length();\n \n\t\tint n = Math.min(len1, len2);\n \n\t\tfor (int i = 0; i < n; i ++){\n\t\t\tint s1_code = getCharCode(s1.charAt(i) + \"\");\n\t\t\tint s2_code = getCharCode(s2.charAt(i) + \"\");\n\t\t\tif (s1_code != s2_code){\n\t\t\t\treturn s1_code - s2_code;\n\t\t\t}\n     \t}\n        return len1 - len2;\n   }\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/CompressUtil.java",
    "content": "package io.mycat.util;\n\nimport com.google.common.collect.Lists;\n\nimport io.mycat.backend.mysql.BufferUtil;\nimport io.mycat.backend.mysql.MySQLMessage;\nimport io.mycat.net.AbstractConnection;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.zip.Deflater;\nimport java.util.zip.Inflater;\n\n\n/**\n * 压缩数据包协议\n * \n * http://dev.mysql.com/doc/internals/en/compressed-packet-header.html\n * \n * (包头)\n * 3 Bytes   压缩长度   \n * 1 Bytes   压缩序列号\n * 3 Bytes   压缩前的长度\n * \n * (包体)\n * n Bytes   压缩内容 或 未压缩内容\n *   \n * | -------------------------------------------------------------------------------------- |   \n * | comp-length  |  seq-id  | uncomp-len   |                Compressed Payload             | \n * | ------------------------------------------------ ------------------------------------- |  \t\n * |  22 00 00    |   00     |  32 00 00    | compress(\"\\x2e\\x00\\x00\\x00\\x03select ...\")    |\n * | -------------------------------------------------------------------------------------- | \t\n * \n * \t Q:为什么消息体是 压缩内容 或者未压缩内容?\n *   A:这是因为mysql内部有一个约定，如果查询语句payload小于50字节时， 对内容不压缩而保持原貌的方式，而mysql此举是为了减少CPU性能开销\n * \n */\npublic class CompressUtil {\n\n\tpublic static final int MINI_LENGTH_TO_COMPRESS = 50;\n\tpublic static final int NO_COMPRESS_PACKET_LENGTH =  MINI_LENGTH_TO_COMPRESS + 4;\n\n\t\n\t/**\n\t * 压缩数据包\n\t * @param input\n\t * @param con\n\t * @param compressUnfinishedDataQueue\n\t * @return\n\t */\n\tpublic static ByteBuffer compressMysqlPacket(ByteBuffer input, AbstractConnection con,\n\t\t\tConcurrentLinkedQueue<byte[]> compressUnfinishedDataQueue) {\n\t\t\n\t\tbyte[] byteArrayFromBuffer = getByteArrayFromBuffer(input);\n\t\tcon.recycle(input);\n\t\t\n\t\tbyteArrayFromBuffer = mergeBytes(byteArrayFromBuffer, compressUnfinishedDataQueue);\n\t\treturn compressMysqlPacket(byteArrayFromBuffer, con, compressUnfinishedDataQueue);\n\t}\n\n\t\n\t/**\n\t * 压缩数据包\n\t * @param data\n\t * @param con\n\t * @param compressUnfinishedDataQueue\n\t * @return\n\t */\n\tprivate static ByteBuffer compressMysqlPacket(byte[] data, AbstractConnection con,\n\t\t\tConcurrentLinkedQueue<byte[]> compressUnfinishedDataQueue) {\n\n\t\tByteBuffer byteBuf = con.allocate();\n\t\tbyteBuf = con.checkWriteBuffer(byteBuf, data.length, false); //TODO: 数据量大的时候, 此处是是性能的堵点\n\t\t\n\t\tMySQLMessage msg = new MySQLMessage(data);\n\t\twhile ( msg.hasRemaining() ) {\n\t\t\t\n\t\t\t//包体的长度\n\t\t\tint packetLength = 0;\n\t\t\t\n\t\t\t//可读的长度\n\t\t\tint readLength = msg.length() - msg.position();\n\t\t\tif ( readLength > 3 ) {\n\t\t\t\tpacketLength = msg.readUB3();\n\t\t\t\tmsg.move(-3);\n\t\t\t}\n\t\t\t\n\t\t\t//校验数据包完整性\n\t\t\tif ( readLength < packetLength + 4 ) {\n\t\t\t\tbyte[] packet = msg.readBytes(readLength);\n\t\t\t\tif (packet.length != 0) {\n\t\t\t\t\tcompressUnfinishedDataQueue.add(packet);\t\t//不完整的包\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t\n\t\t\t\tbyte[] packet = msg.readBytes(packetLength + 4);\n\t\t\t\tif ( packet.length != 0 ) {\n\t\t\t\t\t\n\t\t\t\t\tif ( packet.length <= NO_COMPRESS_PACKET_LENGTH ) {\n\t\t\t\t\t\tBufferUtil.writeUB3(byteBuf, packet.length);    //压缩长度\n\t\t\t\t\t\tbyteBuf.put(packet[3]);\t\t\t \t\t \t\t//压缩序号\n\t\t\t\t\t\tBufferUtil.writeUB3(byteBuf, 0);  \t\t\t\t//压缩前的长度设置为0\n\t\t\t\t\t\tbyteBuf.put(packet);\t\t\t\t\t\t\t//包体\n\n\t\t\t\t\t} else {\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\tbyte[] compress = compress(packet);\t\t\t\t//压缩\n\t\t\t\t\t\t\n\t\t\t\t\t\tBufferUtil.writeUB3(byteBuf, compress.length);\n\t\t\t\t\t\tbyteBuf.put(packet[3]);\n\t\t\t\t\t\tBufferUtil.writeUB3(byteBuf, packet.length);\n\t\t\t\t\t\tbyteBuf.put(compress);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn byteBuf;\n\t}\n\n\t/**\n\t * 解压数据包,同时做分包处理\n\t * \n\t * @param data\n\t * @param decompressUnfinishedDataQueue\n\t * @return\n\t */\n\tpublic static List<byte[]> decompressMysqlPacket(byte[] data,\n\t\t\tConcurrentLinkedQueue<byte[]> decompressUnfinishedDataQueue) {\n\t\t\n\t\tMySQLMessage msg = new MySQLMessage(data);\n\t\t\n\t\t//包头\n\t\t//-----------------------------------------\n\t\tint packetLength = msg.readUB3();  //压缩的包长\n\t\tbyte packetId = msg.read();\t\t   //压缩的包号\n\t\tint oldLen = msg.readUB3();\t\t   //压缩前的长度\n\t\t\n\t\t//未压缩, 直接返回\n\t\tif ( packetLength == data.length - 4 ) {\n\t\t\treturn Lists.newArrayList(data);\n\t\t\t\n\t\t//压缩不成功的, 直接返回\t\n\t\t} else if (oldLen == 0) {\t\t\t\n\t\t\tbyte[] readBytes = msg.readBytes();\n\t\t\treturn splitPack(readBytes, decompressUnfinishedDataQueue);\n\t\t\n\t\t//解压\n\t\t} else {\t\t\t\n\t\t\tbyte[] de = decompress(data, 7, data.length - 7);\n\t\t\treturn splitPack(de, decompressUnfinishedDataQueue);\n\t\t}\n\t}\n\n\t/**\n\t * 分包处理\n\t * \n\t * @param in\n\t * @param decompressUnfinishedDataQueue\n\t * @return\n\t */\n\tprivate static List<byte[]> splitPack(byte[] in, ConcurrentLinkedQueue<byte[]> decompressUnfinishedDataQueue) {\n\t\t\n\t\t//合并\n\t\tin = mergeBytes(in, decompressUnfinishedDataQueue);\n\n\t\tList<byte[]> smallPackList = new ArrayList<>();\n\t\t\n\t\tMySQLMessage msg = new MySQLMessage(in);\n\t\twhile ( msg.hasRemaining() ) {\n\t\t\t\n\t\t\tint readLength = msg.length() - msg.position();\n\t\t\tint packetLength = 0;\n\t\t\tif (readLength > 3) {\n\t\t\t\tpacketLength = msg.readUB3();\n\t\t\t\tmsg.move(-3);\n\t\t\t}\n\t\t\t\n\t\t\tif (readLength < packetLength + 4) {\t\t\t\t\n\t\t\t\tbyte[] packet = msg.readBytes(readLength);\n\t\t\t\tif ( packet.length != 0 ) {\n\t\t\t\t\tdecompressUnfinishedDataQueue.add(packet);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} else {\t\t\t\t\n\t\t\t\tbyte[] packet = msg.readBytes(packetLength + 4);\n\t\t\t\tif ( packet.length != 0 ) {\n\t\t\t\t\tsmallPackList.add(packet);\n\t\t\t\t}\t\t\t\t\n\t\t\t}\n\t\t}\n\n\t\treturn smallPackList;\n\t}\n\n\t/**\n\t * 合并 解压未完成的字节\n\t */\n\tprivate static byte[] mergeBytes(byte[] in, ConcurrentLinkedQueue<byte[]> decompressUnfinishedDataQueue) {\n\t\t\n\t\tif ( !decompressUnfinishedDataQueue.isEmpty() ) {\n\t\t\t\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\ttry {\n\t\t\t\twhile ( !decompressUnfinishedDataQueue.isEmpty() ) {\n\t\t\t\t\tout.write(decompressUnfinishedDataQueue.poll());\n\t\t\t\t}\n\t\t\t\tout.write(in);\n\t\t\t\tin = out.toByteArray();\n\t\t\t\t\n\t\t\t} catch (IOException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\tout.close();\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn in;\n\t}\n\n\tprivate static byte[] getByteArrayFromBuffer(ByteBuffer byteBuf) {\n\t\tbyteBuf.flip();\n\t\tbyte[] row = new byte[byteBuf.limit()];\n\t\tbyteBuf.get(row);\n\t\tbyteBuf.clear();\n\t\treturn row;\n\t}\n\n\tpublic static byte[] compress(ByteBuffer byteBuf) {\n\t\treturn compress(getByteArrayFromBuffer(byteBuf));\n\t}\n\n\t/**\n\t * 适用于mysql与客户端交互时zlib 压缩\n\t *  \n\t * @param data\n\t * @return\n\t */\t\n\tpublic static byte[] compress(byte[] data) {\n\t\t\n\t\tbyte[] output = null;\n\t\t\n\t\tDeflater compresser = new Deflater();\n\t\tcompresser.setInput(data);\n\t\tcompresser.finish();\n\t\t\n\t\tByteArrayOutputStream out = new ByteArrayOutputStream(data.length);\n\t\tbyte[] result = new byte[1024];\t\t\n\t\ttry {\t\t\t\n\t\t\twhile (!compresser.finished()) {\n\t\t\t\tint length = compresser.deflate(result);\n\t\t\t\tout.write(result, 0, length);\n\t\t\t}\t\n\t\t\toutput = out.toByteArray();\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tout.close();\n\t\t\t} catch (Exception e) {\n\t\t\t}\n\t\t\tcompresser.end();\n\t\t}\n\t\t\n\t\treturn output;\n\t}\n\n\t/**\n\t * 适用于mysql与客户端交互时zlib解压\n\t *\n\t * @param data  数据\n\t * @param off   偏移量\n\t * @param len   长度\n\t * @return\n\t */\n\tpublic static byte[] decompress(byte[] data, int off, int len) {\n\t\t\n\t\tbyte[] output = null;\n\t\t\n\t\tInflater decompresser = new Inflater();\n\t\tdecompresser.reset();\n\t\tdecompresser.setInput(data, off, len);\n\n\t\tByteArrayOutputStream out = new ByteArrayOutputStream(data.length);\n\t\ttry {\n\t\t\tbyte[] result = new byte[1024];\n\t\t\twhile (!decompresser.finished()) {\n\t\t\t\tint i = decompresser.inflate(result);\n\t\t\t\tout.write(result, 0, i);\n\t\t\t}\n\t\t\toutput = out.toByteArray();\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tout.close();\t\t\t\t\n\t\t\t} catch (Exception e) {\n\t\t\t}\t\t\t\n\t\t\tdecompresser.end();\n\t\t}\n\t\treturn output;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/DateUtil.java",
    "content": "package io.mycat.util;\n\nimport java.text.ParseException;\nimport java.util.Date;\n\nimport org.joda.time.DateTime;\nimport org.joda.time.format.DateTimeFormat;\n\n/**\n * 使用joda解析date,可以得到date的year,month,day等字段值\n * @author CrazyPig\n *\n */\npublic class DateUtil {\n\t\n\t\n\tpublic static final String DEFAULT_DATE_PATTERN = \"YYYY-MM-dd HH:mm:ss\";\n\tpublic static final String DATE_PATTERN_FULL = \"YYYY-MM-dd HH:mm:ss.SSSSSS\";\n\tpublic static final String DATE_PATTERN_ONLY_DATE = \"YYYY-MM-dd\";\n\tpublic static final String DEFAULT_TIME_PATTERN = \"HHH:mm:ss\";\n\tpublic static final String TIME_PATTERN_FULL = \"HHH:mm:ss.SSSSSS\";\n\t\n\t/**\n\t * 根据日期字符串解析得到date类型日期\n\t * @param dateStr\n\t * @return\n\t * @throws ParseException\n\t */\n\tpublic static Date parseDate(String dateStr) throws ParseException {\n\t\treturn parseDate(dateStr, DEFAULT_DATE_PATTERN);\n\t}\n\t\n\t/**\n\t * 根据日期字符串和日期格式解析得到date类型日期\n\t * @param dateStr\n\t * @param datePattern\n\t * @return\n\t * @throws ParseException\n\t */\n\tpublic static Date parseDate(String dateStr, String datePattern) throws ParseException {\n\t\tDateTime dt = DateTimeFormat.forPattern(datePattern).parseDateTime(dateStr);\n\t\treturn dt.toDate();\n\t}\n\t\n\t/**\n\t * 获取date对象年份\n\t * @param date\n\t * @return\n\t */\n\tpublic static int getYear(Date date) {\n\t\tDateTime dt = new DateTime(date);\n\t\treturn dt.getYear();\n\t}\n\t\n\t/**\n\t * 获取date对象月份\n\t * @param date\n\t * @return\n\t */\n\tpublic static int getMonth(Date date) {\n\t\tDateTime dt = new DateTime(date);\n\t\treturn dt.getMonthOfYear();\n\t}\n\t\n\t/**\n\t * 获取date对象天数\n\t * @param date\n\t * @return\n\t */\n\tpublic static int getDay(Date date) {\n\t\tDateTime dt = new DateTime(date);\n\t\treturn dt.getDayOfMonth();\n\t}\n\t\n\t/**\n\t * 获取date对象小时数\n\t * @param date\n\t * @return\n\t */\n\tpublic static int getHour(Date date) {\n\t\tDateTime dt = new DateTime(date);\n\t\treturn dt.getHourOfDay();\n\t}\n\t\n\t/**\n\t * 获取date对象分钟数\n\t * @param date\n\t * @return\n\t */\n\tpublic static int getMinute(Date date) {\n\t\tDateTime dt = new DateTime(date);\n\t\treturn dt.getMinuteOfHour();\n\t}\n\t\n\t/**\n\t * 获取date对象秒数\n\t * @param date\n\t * @return\n\t */\n\tpublic static int getSecond(Date date) {\n\t\tDateTime dt = new DateTime(date);\n\t\treturn dt.getSecondOfMinute();\n\t}\n\t\n\t/**\n\t * 获取date对象毫秒数\n\t * @param date\n\t * @return\n\t */\n\tpublic static int getMicroSecond(Date date) {\n\t\tDateTime dt = new DateTime(date);\n\t\treturn dt.getMillisOfSecond();\n\t}\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/DecryptUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport io.mycat.config.util.ConfigException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.crypto.Cipher;\nimport java.security.*;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.PKCS8EncodedKeySpec;\nimport java.security.spec.RSAPrivateKeySpec;\nimport java.security.spec.RSAPublicKeySpec;\nimport java.security.spec.X509EncodedKeySpec;\nimport java.util.Arrays;\n\n/**\n * @author songwie\n *\n */\npublic class DecryptUtil {\n\n\tprivate static final String DEFAULT_PRIVATE_KEY_STRING = \"MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAocbCrurZGbC5GArEHKlAfDSZi7gFBnd4yxOt0rwTqKBFzGyhtQLu5PRKjEiOXVa95aeIIBJ6OhC2f8FjqFUpawIDAQABAkAPejKaBYHrwUqUEEOe8lpnB6lBAsQIUFnQI/vXU4MV+MhIzW0BLVZCiarIQqUXeOhThVWXKFt8GxCykrrUsQ6BAiEA4vMVxEHBovz1di3aozzFvSMdsjTcYRRo82hS5Ru2/OECIQC2fAPoXixVTVY7bNMeuxCP4954ZkXp7fEPDINCjcQDywIgcc8XLkkPcs3Jxk7uYofaXaPbg39wuJpEmzPIxi3k0OECIGubmdpOnin3HuCP/bbjbJLNNoUdGiEmFL5hDI4UdwAdAiEAtcAwbm08bKN7pwwvyqaCBC//VnEWaq39DCzxr+Z2EIk=\";\n\tpublic static final String DEFAULT_PUBLIC_KEY_STRING = \"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKHGwq7q2RmwuRgKxBypQHw0mYu4BQZ3eMsTrdK8E6igRcxsobUC7uT0SoxIjl1WveWniCASejoQtn/BY6hVKWsCAwEAAQ==\";\n\n\tpublic static void main(String[] args) throws Exception {\n\t  System.out.println(\"其中 0:user:password是加密字符串,有两种格式\\n\"\n        + \"\\n\"\n        + \"dataHost加密格式\\n\"\n        + \"1:hostM1:root:123456\\n\"\n        + \"1代表是dataHost加密\\n\"\n        + \"hostM1是<writeHost host=\\\"hostM1\\\" \\n\"\n        + \"root是user=\\\"root\\\"\\n\"\n        + \"123456是 password=明文密码(123456)\\n\"\n        + \"\\n\"\n        + \"对应writeHost配置\\n\"\n        + \"\\t\\t<writeHost host=\\\"hostM1\\\" url=\\\"localhost:3306\\\" user=\\\"root\\\"\\n\"\n        + \"\\t\\t\\t\\t   password=\\\"BpkNIjF7LfzS1C76HT7B1bJgmGIDtPihqIvHBlC92L1IFqsMfoJEMk1EkxSzjasWB4GWoUcODYO4AaJstdAp5w==\\\" usingDecrypt=\\\"1\\\">\\n\"\n        + \"\\t\\t\\t<!-- can have multi read hosts -->\\n\"\n        + \"\\t\\t</writeHost>\\n\"\n        + \"\\n\"\n        + \"mycat用户登录密码加密格式\\n\"\n        + \"0:root:123456\\n\"\n        + \"0代表mycat用户登录密码加密\\n\"\n        + \"\\t<user name=\\\"root\\\" defaultAccount=\\\"true\\\">\\n\"\n        + \"\\t\\t<property name=\\\"usingDecrypt\\\">1</property>\\n\"\n        + \"\\t\\t<property name=\\\"password\\\">d6D+pOmkuUoY09p4/aivwMsScLa7zfjIwAxvkEhr3v7en06mEXoX9DTTjQNug5CfvGf7Wy9oLcthYI3yLMSjIg==</property>\\n\"\n        + \"\\t\\t<property name=\\\"schemas\\\">TESTDB</property>\");\n\t\tString password = args[0];\n\t\tSystem.out.println(encrypt(password));\n\t}\n\t\n\tpublic static String mycatDecrypt(String usingDecrypt,String user ,String passwrod){\n\t\tif(\"1\".equals(usingDecrypt)||\"true\".equalsIgnoreCase(usingDecrypt)){\n        \t//type:user:password\n        \t//0:test:test\n        \tboolean flag = false;\n        \ttry {\n        \t\tString passwrods[] = DecryptUtil.decrypt(passwrod).split(\":\");\n            \tif(\"0\".equals(passwrods[0])\n\t\t\t\t\t\t&& user.equals(passwrods[1])){\n                        flag = true;\n            \t\t\treturn passwrods[2];\n            \t}\n            \tif(flag==false){\n            \t\t throw new ConfigException(\"user \" + user + \" passwrod need to decrype ,but decrype password is wrong !\");\n            \t}\n        \t} catch (Exception e2) {\n       \t\t    throw new ConfigException(\"user \" + user + \" passwrod need to decrype ,but decrype password is wrong !\",e2);\n\t\t\t}\n\t\t}\n\t\treturn passwrod;\n\t}\n\tpublic static String DBHostDecrypt(String usingDecrypt,String host,String user ,String passwrod){\n\t\tif(\"1\".equals(usingDecrypt)||\"true\".equalsIgnoreCase(usingDecrypt)){\n\t\t\t//type:host:user:password\n        \t//1:myhost1:test:test\n        \tboolean flag = false;\n        \ttry {\n        \t\tString passwrods[] = DecryptUtil.decrypt(passwrod).split(\":\");\n            \tif(\"1\".equals(passwrods[0]) && host.equals(passwrods[1]) && user.equals(passwrods[2])){\n            \t\treturn passwrods[3];\n            \t}\n            \tif(flag==false){\n            \t\t throw new ConfigException(\"user \" + user + \" passwrod need to decrype ,but decrype password is wrong !\");\n            \t}\n        \t} catch (Exception e2) {\n       \t\t    throw new ConfigException(\"host \" + host + \",user \" + user + \" passwrod need to decrype ,but decrype password is wrong !\",e2);\n\t\t\t}\n\t\t}\n\t\treturn passwrod;\n\t}\n\t\n\n\tpublic static String decrypt(String cipherText) throws Exception {\n\t\treturn decrypt((String) null, cipherText);\n\t}\n\n\tpublic static String decrypt(String publicKeyText, String cipherText)\n\t\t\tthrows Exception {\n\t\tPublicKey publicKey = getPublicKey(publicKeyText);\n\n\t\treturn decrypt(publicKey, cipherText);\n\t}\n\n\tpublic static PublicKey getPublicKey(String publicKeyText) {\n\t\tif (publicKeyText == null || publicKeyText.length() == 0) {\n\t\t\tpublicKeyText = DecryptUtil.DEFAULT_PUBLIC_KEY_STRING;\n\t\t}\n\n\t\ttry {\n\t\t\tbyte[] publicKeyBytes = Base64.base64ToByteArray(publicKeyText);\n\t\t\tX509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(\n\t\t\t\t\tpublicKeyBytes);\n\n\t\t\tKeyFactory keyFactory = KeyFactory.getInstance(\"RSA\");\n\t\t\treturn keyFactory.generatePublic(x509KeySpec);\n\t\t} catch (Exception e) {\n\t\t\tthrow new IllegalArgumentException(\"Failed to get public key\", e);\n\t\t}\n\t}\n\n\n\tpublic static String decrypt(PublicKey publicKey, String cipherText)\n\t\t\tthrows Exception {\n\t\tCipher cipher = Cipher.getInstance(\"RSA\");\n\t\ttry {\n\t\t\tcipher.init(Cipher.DECRYPT_MODE, publicKey);\n\t\t} catch (InvalidKeyException e) {\n            // 因为 IBM JDK 不支持私钥加密, 公钥解密, 所以要反转公私钥\n            // 也就是说对于解密, 可以通过公钥的参数伪造一个私钥对象欺骗 IBM JDK\n            RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;\n            RSAPrivateKeySpec spec = new RSAPrivateKeySpec(rsaPublicKey.getModulus(), rsaPublicKey.getPublicExponent());\n            Key fakePrivateKey = KeyFactory.getInstance(\"RSA\").generatePrivate(spec);\n            cipher = Cipher.getInstance(\"RSA\"); //It is a stateful object. so we need to get new one.\n            cipher.init(Cipher.DECRYPT_MODE, fakePrivateKey);\n\t\t}\n\t\t\n\t\tif (cipherText == null || cipherText.length() == 0) {\n\t\t\treturn cipherText;\n\t\t}\n\n\t\tbyte[] cipherBytes = Base64.base64ToByteArray(cipherText);\n\t\tbyte[] plainBytes = cipher.doFinal(cipherBytes);\n\n\t\treturn new String(plainBytes);\n\t}\n\n\tpublic static String encrypt(String plainText) throws Exception {\n\t\treturn encrypt((String) null, plainText);\n\t}\n\n\tpublic static String encrypt(String key, String plainText) throws Exception {\n\t\tif (key == null) {\n\t\t\tkey = DEFAULT_PRIVATE_KEY_STRING;\n\t\t}\n\n\t\tbyte[] keyBytes = Base64.base64ToByteArray(key);\n\t\treturn encrypt(keyBytes, plainText);\n\t}\n\n\tpublic static String encrypt(byte[] keyBytes, String plainText)\n\t\t\tthrows Exception {\n\t\tPKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);\n\t\tKeyFactory factory = KeyFactory.getInstance(\"RSA\");\n\t\tPrivateKey privateKey = factory.generatePrivate(spec);\n\t\tCipher cipher = Cipher.getInstance(\"RSA\");\n        try {\n\t\t    cipher.init(Cipher.ENCRYPT_MODE, privateKey);\n        } catch (InvalidKeyException e) {\n            //For IBM JDK, 原因请看解密方法中的说明\n            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;\n            RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(rsaPrivateKey.getModulus(), rsaPrivateKey.getPrivateExponent());\n            Key fakePublicKey = KeyFactory.getInstance(\"RSA\").generatePublic(publicKeySpec);\n            cipher = Cipher.getInstance(\"RSA\");\n            cipher.init(Cipher.ENCRYPT_MODE, fakePublicKey);\n        }\n\n\t\tbyte[] encryptedBytes = cipher.doFinal(plainText.getBytes(\"UTF-8\"));\n\t\tString encryptedString = Base64.byteArrayToBase64(encryptedBytes);\n\n\t\treturn encryptedString;\n\t}\n\n\tpublic static byte[][] genKeyPairBytes(int keySize)\n\t\t\tthrows NoSuchAlgorithmException {\n\t\tbyte[][] keyPairBytes = new byte[2][];\n\n\t\tKeyPairGenerator gen = KeyPairGenerator.getInstance(\"RSA\");\n\t\tgen.initialize(keySize, new SecureRandom());\n\t\tKeyPair pair = gen.generateKeyPair();\n\n\t\tkeyPairBytes[0] = pair.getPrivate().getEncoded();\n\t\tkeyPairBytes[1] = pair.getPublic().getEncoded();\n\n\t\treturn keyPairBytes;\n\t}\n\n\tpublic static String[] genKeyPair(int keySize)\n\t\t\tthrows NoSuchAlgorithmException {\n\t\tbyte[][] keyPairBytes = genKeyPairBytes(keySize);\n\t\tString[] keyPairs = new String[2];\n\n\t\tkeyPairs[0] = Base64.byteArrayToBase64(keyPairBytes[0]);\n\t\tkeyPairs[1] = Base64.byteArrayToBase64(keyPairBytes[1]);\n\n\t\treturn keyPairs;\n\t}\n\t\n\tstatic class Base64 {\n\n\t    /**\n\t     * Translates the specified byte array into a Base64 string as per Preferences.put(byte[]).\n\t     */\n\t    public static String byteArrayToBase64(byte[] a) {\n\t        return byteArrayToBase64(a, false);\n\t    }\n\n\t    /**\n\t     * Translates the specified byte array into an \"alternate representation\" Base64 string. This non-standard variant\n\t     * uses an alphabet that does not contain the uppercase alphabetic characters, which makes it suitable for use in\n\t     * situations where case-folding occurs.\n\t     */\n\t    public static String byteArrayToAltBase64(byte[] a) {\n\t        return byteArrayToBase64(a, true);\n\t    }\n\n\t    private static String byteArrayToBase64(byte[] a, boolean alternate) {\n\t        int aLen = a.length;\n\t        int numFullGroups = aLen / 3;\n\t        int numBytesInPartialGroup = aLen - 3 * numFullGroups;\n\t        int resultLen = 4 * ((aLen + 2) / 3);\n\t        StringBuilder result = new StringBuilder(resultLen);\n\t        char[] intToAlpha = (alternate ? intToAltBase64 : intToBase64);\n\n\t        // Translate all full groups from byte array elements to Base64\n\t        int inCursor = 0;\n\t        for (int i = 0; i < numFullGroups; i++) {\n\t            int byte0 = a[inCursor++] & 0xff;\n\t            int byte1 = a[inCursor++] & 0xff;\n\t            int byte2 = a[inCursor++] & 0xff;\n\t            result.append(intToAlpha[byte0 >> 2]);\n\t            result.append(intToAlpha[(byte0 << 4) & 0x3f | (byte1 >> 4)]);\n\t            result.append(intToAlpha[(byte1 << 2) & 0x3f | (byte2 >> 6)]);\n\t            result.append(intToAlpha[byte2 & 0x3f]);\n\t        }\n\n\t        // Translate partial group if present\n\t        if (numBytesInPartialGroup != 0) {\n\t            int byte0 = a[inCursor++] & 0xff;\n\t            result.append(intToAlpha[byte0 >> 2]);\n\t            if (numBytesInPartialGroup == 1) {\n\t                result.append(intToAlpha[(byte0 << 4) & 0x3f]);\n\t                result.append(\"==\");\n\t            } else {\n\t                // assert numBytesInPartialGroup == 2;\n\t                int byte1 = a[inCursor++] & 0xff;\n\t                result.append(intToAlpha[(byte0 << 4) & 0x3f | (byte1 >> 4)]);\n\t                result.append(intToAlpha[(byte1 << 2) & 0x3f]);\n\t                result.append('=');\n\t            }\n\t        }\n\t        // assert inCursor == a.length;\n\t        // assert result.length() == resultLen;\n\t        return result.toString();\n\t    }\n\n\t    /**\n\t     * This array is a lookup table that translates 6-bit positive integer index values into their \"Base64 Alphabet\"\n\t     * equivalents as specified in Table 1 of RFC 2045.\n\t     */\n\t    private static final char intToBase64[]    = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',\n\t            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',\n\t            'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',\n\t            '3', '4', '5', '6', '7', '8', '9', '+', '/' };\n\n\t    /**\n\t     * This array is a lookup table that translates 6-bit positive integer index values into their\n\t     * \"Alternate Base64 Alphabet\" equivalents. This is NOT the real Base64 Alphabet as per in Table 1 of RFC 2045. This\n\t     * alternate alphabet does not use the capital letters. It is designed for use in environments where \"case folding\"\n\t     * occurs.\n\t     */\n\t    private static final char intToAltBase64[] = { '!', '\"', '#', '$', '%', '&', '\\'', '(', ')', ',', '-', '.', ':',\n\t            ';', '<', '>', '@', '[', ']', '^', '`', '_', '{', '|', '}', '~', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',\n\t            'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',\n\t            '3', '4', '5', '6', '7', '8', '9', '+', '?' };\n\n\t    /**\n\t     * Translates the specified Base64 string (as per Preferences.get(byte[])) into a byte array.\n\t     * \n\t     * @throw IllegalArgumentException if <tt>s</tt> is not a valid Base64 string.\n\t     */\n\t    public static byte[] base64ToByteArray(String s) {\n\t        return base64ToByteArray(s, false);\n\t    }\n\n\t    /**\n\t     * Translates the specified \"alternate representation\" Base64 string into a byte array.\n\t     * \n\t     * @throw IllegalArgumentException or ArrayOutOfBoundsException if <tt>s</tt> is not a valid alternate\n\t     * representation Base64 string.\n\t     */\n\t    public static byte[] altBase64ToByteArray(String s) {\n\t        return base64ToByteArray(s, true);\n\t    }\n\n\t    private static byte[] base64ToByteArray(String s, boolean alternate) {\n\t        byte[] alphaToInt = (alternate ? altBase64ToInt : base64ToInt);\n\t        int sLen = s.length();\n\t        int numGroups = sLen / 4;\n\t        if (4 * numGroups != sLen) {\n\t            throw new IllegalArgumentException(\"String length must be a multiple of four.\");\n\t        }\n\t        int missingBytesInLastGroup = 0;\n\t        int numFullGroups = numGroups;\n\t        if (sLen != 0) {\n\t            if (s.charAt(sLen - 1) == '=') {\n\t                missingBytesInLastGroup++;\n\t                numFullGroups--;\n\t            }\n\t            if (s.charAt(sLen - 2) == '=') {\n\t                missingBytesInLastGroup++;\n\t            }\n\t        }\n\t        byte[] result = new byte[3 * numGroups - missingBytesInLastGroup];\n\n\t        // Translate all full groups from base64 to byte array elements\n\t        int inCursor = 0, outCursor = 0;\n\t        for (int i = 0; i < numFullGroups; i++) {\n\t            int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt);\n\t            int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt);\n\t            int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt);\n\t            int ch3 = base64toInt(s.charAt(inCursor++), alphaToInt);\n\t            result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));\n\t            result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));\n\t            result[outCursor++] = (byte) ((ch2 << 6) | ch3);\n\t        }\n\n\t        // Translate partial group, if present\n\t        if (missingBytesInLastGroup != 0) {\n\t            int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt);\n\t            int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt);\n\t            result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));\n\n\t            if (missingBytesInLastGroup == 1) {\n\t                int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt);\n\t                result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));\n\t            }\n\t        }\n\t        // assert inCursor == s.length()-missingBytesInLastGroup;\n\t        // assert outCursor == result.length;\n\t        return result;\n\t    }\n\n\t    /**\n\t     * Translates the specified character, which is assumed to be in the \"Base 64 Alphabet\" into its equivalent 6-bit\n\t     * positive integer.\n\t     * \n\t     * @throw IllegalArgumentException or ArrayOutOfBoundsException if c is not in the Base64 Alphabet.\n\t     */\n\t    private static int base64toInt(char c, byte[] alphaToInt) {\n\t        int result = alphaToInt[c];\n\t        if (result < 0) {\n\t            throw new IllegalArgumentException(\"Illegal character \" + c);\n\t        }\n\t        return result;\n\t    }\n\n\t    /**\n\t     * This array is a lookup table that translates unicode characters drawn from the \"Base64 Alphabet\" (as specified in\n\t     * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64\n\t     * alphabet but fall within the bounds of the array are translated to -1.\n\t     */\n\t    private static final byte base64ToInt[]    = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n\t            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62,\n\t            -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7,\n\t            8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,\n\t            29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };\n\n\t    /**\n\t     * This array is the analogue of base64ToInt, but for the nonstandard variant that avoids the use of uppercase\n\t     * alphabetic characters.\n\t     */\n\t    private static final byte altBase64ToInt[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n\t            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, 62, 9, 10,\n\t            11, -1, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 12, 13, 14, -1, 15, 63, 16, -1, -1, -1, -1, -1, -1, -1, -1,\n\t            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, -1, 18, 19, 21, 20, 26, 27, 28,\n\t            29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 22, 23, 24, 25 };\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/ExecutorUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.util.concurrent.LinkedTransferQueue;\n\n/**\n * @author mycat\n */\npublic class ExecutorUtil {\n\n    public static final NameableExecutor create(String name, int size) {\n        return create(name, size, true);\n    }\n\n    private static final NameableExecutor create(String name, int size, boolean isDaemon) {\n        NameableThreadFactory factory = new NameableThreadFactory(name, isDaemon);\n        return new NameableExecutor(name, size, new LinkedTransferQueue<Runnable>(), factory);\n    }\n\n   \n}"
  },
  {
    "path": "src/main/java/io/mycat/util/FastByteOperations.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.mycat.util;\n\nimport java.lang.reflect.Field;\nimport java.nio.Buffer;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.security.AccessController;\nimport java.security.PrivilegedAction;\n\nimport sun.misc.Unsafe;\n\nimport com.google.common.primitives.Longs;\nimport com.google.common.primitives.UnsignedBytes;\nimport com.google.common.primitives.UnsignedLongs;\n\n/**\n * Utility code to do optimized byte-array comparison.\n * This is borrowed and slightly modified from Guava's {@link UnsignedBytes}\n * class to be able to compare arrays that start at non-zero offsets.\n */\npublic class FastByteOperations\n{\n\n    /**\n     * Lexicographically compare two byte arrays.\n     */\n    public static int compareUnsigned(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2)\n    {\n        return BestHolder.BEST.compare(b1, s1, l1, b2, s2, l2);\n    }\n\n    public static int compareUnsigned(ByteBuffer b1, byte[] b2, int s2, int l2)\n    {\n        return BestHolder.BEST.compare(b1, b2, s2, l2);\n    }\n\n    public static int compareUnsigned(byte[] b1, int s1, int l1, ByteBuffer b2)\n    {\n        return -BestHolder.BEST.compare(b2, b1, s1, l1);\n    }\n\n    public static int compareUnsigned(ByteBuffer b1, ByteBuffer b2)\n    {\n        return BestHolder.BEST.compare(b1, b2);\n    }\n\n    public static void copy(ByteBuffer src, int srcPosition, byte[] trg, int trgPosition, int length)\n    {\n        BestHolder.BEST.copy(src, srcPosition, trg, trgPosition, length);\n    }\n\n    public static void copy(ByteBuffer src, int srcPosition, ByteBuffer trg, int trgPosition, int length)\n    {\n        BestHolder.BEST.copy(src, srcPosition, trg, trgPosition, length);\n    }\n\n    public interface ByteOperations\n    {\n        abstract public int compare(byte[] buffer1, int offset1, int length1,\n                                    byte[] buffer2, int offset2, int length2);\n\n        abstract public int compare(ByteBuffer buffer1, byte[] buffer2, int offset2, int length2);\n\n        abstract public int compare(ByteBuffer buffer1, ByteBuffer buffer2);\n\n        abstract public void copy(ByteBuffer src, int srcPosition, byte[] trg, int trgPosition, int length);\n\n        abstract public void copy(ByteBuffer src, int srcPosition, ByteBuffer trg, int trgPosition, int length);\n    }\n\n    /**\n     * Provides a lexicographical comparer implementation; either a Java\n     * implementation or a faster implementation based on {@link Unsafe}.\n     * <p/>\n     * <p>Uses reflection to gracefully fall back to the Java implementation if\n     * {@code Unsafe} isn't available.\n     */\n    private static class BestHolder\n    {\n        static final String UNSAFE_COMPARER_NAME = FastByteOperations.class.getName() + \"$UnsafeOperations\";\n        static final ByteOperations BEST = getBest();\n\n        /**\n         * Returns the Unsafe-using Comparer, or falls back to the pure-Java\n         * implementation if unable to do so.\n         */\n        static ByteOperations getBest()\n        {\n            String arch = System.getProperty(\"os.arch\");\n            boolean unaligned = arch.equals(\"i386\") || arch.equals(\"x86\")\n                                || arch.equals(\"amd64\") || arch.equals(\"x86_64\");\n            if (!unaligned) {\n                return new PureJavaOperations();\n            }\n            try\n            {\n                Class<?> theClass = Class.forName(UNSAFE_COMPARER_NAME);\n\n                // yes, UnsafeComparer does implement Comparer<byte[]>\n                @SuppressWarnings(\"unchecked\")\n                ByteOperations comparer = (ByteOperations) theClass.getConstructor().newInstance();\n                return comparer;\n            }\n            catch (Throwable t)\n            {\n                //JVMStabilityInspector.inspectThrowable(t);\n                // ensure we really catch *everything*\n                return new PureJavaOperations();\n            }\n        }\n\n    }\n\n    @SuppressWarnings(\"unused\") // used via reflection\n    public static final class UnsafeOperations implements ByteOperations\n    {\n        static final Unsafe theUnsafe;\n        /**\n         * The offset to the first element in a byte array.\n         */\n        static final long BYTE_ARRAY_BASE_OFFSET;\n        static final long DIRECT_BUFFER_ADDRESS_OFFSET;\n\n        static\n        {\n            theUnsafe = (Unsafe) AccessController.doPrivileged(\n                      new PrivilegedAction<Object>()\n                      {\n                          @Override\n                          public Object run()\n                          {\n                              try\n                              {\n                                  Field f = Unsafe.class.getDeclaredField(\"theUnsafe\");\n                                  f.setAccessible(true);\n                                  return f.get(null);\n                              }\n                              catch (NoSuchFieldException e)\n                              {\n                                  // It doesn't matter what we throw;\n                                  // it's swallowed in getBest().\n                                  throw new Error();\n                              }\n                              catch (IllegalAccessException e)\n                              {\n                                  throw new Error();\n                              }\n                          }\n                      });\n\n            try\n            {\n                BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class);\n                DIRECT_BUFFER_ADDRESS_OFFSET = theUnsafe.objectFieldOffset(Buffer.class.getDeclaredField(\"address\"));\n            }\n            catch (Exception e)\n            {\n                throw new AssertionError(e);\n            }\n\n            // sanity check - this should never fail\n            if (theUnsafe.arrayIndexScale(byte[].class) != 1)\n            {\n                throw new AssertionError();\n            }\n        }\n\n        static final boolean BIG_ENDIAN = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN);\n\n        public int compare(byte[] buffer1, int offset1, int length1, byte[] buffer2, int offset2, int length2)\n        {\n            return compareTo(buffer1, BYTE_ARRAY_BASE_OFFSET + offset1, length1,\n                             buffer2, BYTE_ARRAY_BASE_OFFSET + offset2, length2);\n        }\n\n        public int compare(ByteBuffer buffer1, byte[] buffer2, int offset2, int length2)\n        {\n            Object obj1;\n            long offset1;\n            if (buffer1.hasArray())\n            {\n                obj1 = buffer1.array();\n                offset1 = BYTE_ARRAY_BASE_OFFSET + buffer1.arrayOffset();\n            }\n            else\n            {\n                obj1 = null;\n                offset1 = theUnsafe.getLong(buffer1, DIRECT_BUFFER_ADDRESS_OFFSET);\n            }\n            int length1;\n            {\n                int position = buffer1.position();\n                int limit = buffer1.limit();\n                length1 = limit - position;\n                offset1 += position;\n            }\n            return compareTo(obj1, offset1, length1, buffer2, BYTE_ARRAY_BASE_OFFSET + offset2, length2);\n        }\n\n        public int compare(ByteBuffer buffer1, ByteBuffer buffer2)\n        {\n            return compareTo(buffer1, buffer2);\n        }\n\n        public void copy(ByteBuffer src, int srcPosition, byte[] trg, int trgPosition, int length)\n        {\n            if (src.hasArray()) {\n                System.arraycopy(src.array(), src.arrayOffset() + srcPosition, trg, trgPosition, length);\n            }\n            else {\n                copy(null, srcPosition + theUnsafe.getLong(src, DIRECT_BUFFER_ADDRESS_OFFSET), trg, trgPosition, length);\n            }\n        }\n\n        public void copy(ByteBuffer srcBuf, int srcPosition, ByteBuffer trgBuf, int trgPosition, int length)\n        {\n            Object src;\n            long srcOffset;\n            if (srcBuf.hasArray())\n            {\n                src = srcBuf.array();\n                srcOffset = BYTE_ARRAY_BASE_OFFSET + srcBuf.arrayOffset();\n            }\n            else\n            {\n                src = null;\n                srcOffset = theUnsafe.getLong(srcBuf, DIRECT_BUFFER_ADDRESS_OFFSET);\n            }\n            copy(src, srcOffset + srcPosition, trgBuf, trgPosition, length);\n        }\n\n        public static void copy(Object src, long srcOffset, ByteBuffer trgBuf, int trgPosition, int length)\n        {\n            if (trgBuf.hasArray()) {\n                copy(src, srcOffset, trgBuf.array(), trgBuf.arrayOffset() + trgPosition, length);\n            }\n            else {\n                copy(src, srcOffset, null, trgPosition + theUnsafe.getLong(trgBuf, DIRECT_BUFFER_ADDRESS_OFFSET), length);\n            }\n        }\n\n        public static void copy(Object src, long srcOffset, byte[] trg, int trgPosition, int length)\n        {\n            if (length <= MIN_COPY_THRESHOLD)\n            {\n                for (int i = 0 ; i < length ; i++) {\n                    trg[trgPosition + i] = theUnsafe.getByte(src, srcOffset + i);\n                }\n            }\n            else\n            {\n                copy(src, srcOffset, trg, BYTE_ARRAY_BASE_OFFSET + trgPosition, length);\n            }\n        }\n\n        // 1M, copied from java.nio.Bits (unfortunately a package-private class)\n        private static final long UNSAFE_COPY_THRESHOLD = 1 << 20;\n        private static final long MIN_COPY_THRESHOLD = 6;\n\n        public static void copy(Object src, long srcOffset, Object dst, long dstOffset, long length)\n        {\n            while (length > 0) {\n                long size = (length > UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : length;\n                // if src or dst are null, the offsets are absolute base addresses:\n                theUnsafe.copyMemory(src, srcOffset, dst, dstOffset, size);\n                length -= size;\n                srcOffset += size;\n                dstOffset += size;\n            }\n        }\n\n\n        public static int compareTo(ByteBuffer buffer1, ByteBuffer buffer2)\n        {\n            Object obj1;\n            long offset1;\n            int length1;\n            if (buffer1.hasArray())\n            {\n                obj1 = buffer1.array();\n                offset1 = BYTE_ARRAY_BASE_OFFSET + buffer1.arrayOffset();\n            }\n            else\n            {\n                obj1 = null;\n                offset1 = theUnsafe.getLong(buffer1, DIRECT_BUFFER_ADDRESS_OFFSET);\n            }\n            offset1 += buffer1.position();\n            length1 = buffer1.remaining();\n            return compareTo(obj1, offset1, length1, buffer2);\n        }\n\n\n        public static int compareTo(Object buffer1, long offset1, int length1, ByteBuffer buffer)\n        {\n            Object obj2;\n            long offset2;\n\n            int position = buffer.position();\n            int limit = buffer.limit();\n            if (buffer.hasArray())\n            {\n                obj2 = buffer.array();\n                offset2 = BYTE_ARRAY_BASE_OFFSET + buffer.arrayOffset();\n            }\n            else\n            {\n                obj2 = null;\n                offset2 = theUnsafe.getLong(buffer, DIRECT_BUFFER_ADDRESS_OFFSET);\n            }\n            int length2 = limit - position;\n            offset2 += position;\n\n            return compareTo(buffer1, offset1, length1, obj2, offset2, length2);\n        }\n\n        /**\n         * Lexicographically compare two arrays.\n         *\n         * @param buffer1 left operand: a byte[] or null\n         * @param buffer2 right operand: a byte[] or null\n         * @param memoryOffset1 Where to start comparing in the left buffer (pure memory address if buffer1 is null, or relative otherwise)\n         * @param memoryOffset2 Where to start comparing in the right buffer (pure memory address if buffer1 is null, or relative otherwise)\n         * @param length1 How much to compare from the left buffer\n         * @param length2 How much to compare from the right buffer\n         * @return 0 if equal, < 0 if left is less than right, etc.\n         */\n\n        public static int compareTo(Object buffer1, long memoryOffset1, int length1,\n                             Object buffer2, long memoryOffset2, int length2)\n        {\n            int minLength = Math.min(length1, length2);\n\n            /*\n             * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a\n             * time is no slower than comparing 4 bytes at a time even on 32-bit.\n             * On the other hand, it is substantially faster on 64-bit.\n             */\n            int wordComparisons = minLength & ~7;\n            for (int i = 0; i < wordComparisons ; i += Longs.BYTES)\n            {\n                long lw = theUnsafe.getLong(buffer1, memoryOffset1 + (long) i);\n                long rw = theUnsafe.getLong(buffer2, memoryOffset2 + (long) i);\n\n                if (lw != rw)\n                {\n                    if (BIG_ENDIAN) {\n                        return UnsignedLongs.compare(lw, rw);\n                    }\n\n                    return UnsignedLongs.compare(Long.reverseBytes(lw), Long.reverseBytes(rw));\n                }\n            }\n\n            for (int i = wordComparisons ; i < minLength ; i++)\n            {\n                int b1 = theUnsafe.getByte(buffer1, memoryOffset1 + i) & 0xFF;\n                int b2 = theUnsafe.getByte(buffer2, memoryOffset2 + i) & 0xFF;\n                if (b1 != b2) {\n                    return b1 - b2;\n                }\n            }\n\n            return length1 - length2;\n        }\n\n    }\n\n    @SuppressWarnings(\"unused\")\n    public static final class PureJavaOperations implements ByteOperations\n    {\n        @Override\n        public int compare(byte[] buffer1, int offset1, int length1,\n                           byte[] buffer2, int offset2, int length2)\n        {\n            // Short circuit equal case\n            if (buffer1 == buffer2 && offset1 == offset2 && length1 == length2) {\n                return 0;\n            }\n\n            int end1 = offset1 + length1;\n            int end2 = offset2 + length2;\n            for (int i = offset1, j = offset2; i < end1 && j < end2; i++, j++)\n            {\n                int a = (buffer1[i] & 0xff);\n                int b = (buffer2[j] & 0xff);\n                if (a != b)\n                {\n                    return a - b;\n                }\n            }\n            return length1 - length2;\n        }\n\n        public int compare(ByteBuffer buffer1, byte[] buffer2, int offset2, int length2)\n        {\n            if (buffer1.hasArray()) {\n                return compare(buffer1.array(), buffer1.arrayOffset() + buffer1.position(), buffer1.remaining(),\n                        buffer2, offset2, length2);\n            }\n            return compare(buffer1, ByteBuffer.wrap(buffer2, offset2, length2));\n        }\n\n        public int compare(ByteBuffer buffer1, ByteBuffer buffer2)\n        {\n            int end1 = buffer1.limit();\n            int end2 = buffer2.limit();\n            for (int i = buffer1.position(), j = buffer2.position(); i < end1 && j < end2; i++, j++)\n            {\n                int a = (buffer1.get(i) & 0xff);\n                int b = (buffer2.get(j) & 0xff);\n                if (a != b)\n                {\n                    return a - b;\n                }\n            }\n            return buffer1.remaining() - buffer2.remaining();\n        }\n\n        public void copy(ByteBuffer src, int srcPosition, byte[] trg, int trgPosition, int length)\n        {\n            if (src.hasArray())\n            {\n                System.arraycopy(src.array(), src.arrayOffset() + srcPosition, trg, trgPosition, length);\n                return;\n            }\n            src = src.duplicate();\n            src.position(srcPosition);\n            src.get(trg, trgPosition, length);\n        }\n\n        public void copy(ByteBuffer src, int srcPosition, ByteBuffer trg, int trgPosition, int length)\n        {\n            if (src.hasArray() && trg.hasArray())\n            {\n                System.arraycopy(src.array(), src.arrayOffset() + srcPosition, trg.array(), trg.arrayOffset() + trgPosition, length);\n                return;\n            }\n            src = src.duplicate();\n            src.position(srcPosition).limit(srcPosition + length);\n            trg = trg.duplicate();\n            trg.position(trgPosition);\n            trg.put(src);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/FormatUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\n/**\n * 格式化工具\n * \n * @author mycat\n * @version 2008-11-24 下午12:58:17\n */\npublic final class FormatUtil {\n\n    // 右对齐格式化字符串\n    public static final int ALIGN_RIGHT = 0;\n\n    // 左对齐格式化字符串\n    public static final int ALIGN_LEFT = 1;\n\n    private static final char defaultSplitChar = ' ';\n\n    private static final String[] timeFormat = new String[] { \"d \", \"h \", \"m \", \"s \", \"ms\" };\n\n    /**\n     * 格式化后返回的字符串\n     * \n     * @param s\n     *            需要格式化的原始字符串，默认按左对齐。\n     * @param fillLength\n     *            填充长度\n     * @return String\n     */\n    public static String format(String s, int fillLength) {\n        return format(s, fillLength, defaultSplitChar, ALIGN_LEFT);\n    }\n\n    /**\n     * 格式化后返回的字符串\n     * \n     * @param i\n     *            需要格式化的数字类型，默认按右对齐。\n     * @param fillLength\n     *            填充长度\n     * @return String\n     */\n    public static String format(int i, int fillLength) {\n        return format(Integer.toString(i), fillLength, defaultSplitChar, ALIGN_RIGHT);\n    }\n\n    /**\n     * 格式化后返回的字符串\n     * \n     * @param l\n     *            需要格式化的数字类型，默认按右对齐。\n     * @param fillLength\n     *            填充长度\n     * @return String\n     */\n    public static String format(long l, int fillLength) {\n        return format(Long.toString(l), fillLength, defaultSplitChar, ALIGN_RIGHT);\n    }\n\n    /**\n     * @param s\n     *            需要格式化的原始字符串\n     * @param fillLength\n     *            填充长度\n     * @param fillChar\n     *            填充的字符\n     * @param align\n     *            填充方式（左边填充还是右边填充）\n     * @return String\n     */\n    public static String format(String s, int fillLength, char fillChar, int align) {\n        if (s == null) {\n            s = \"\";\n        } else {\n            s = s.trim();\n        }\n        int charLen = fillLength - s.length();\n        if (charLen > 0) {\n            char[] fills = new char[charLen];\n            for (int i = 0; i < charLen; i++) {\n                fills[i] = fillChar;\n            }\n            StringBuilder str = new StringBuilder(s);\n            switch (align) {\n            case ALIGN_RIGHT:\n                str.insert(0, fills);\n                break;\n            case ALIGN_LEFT:\n                str.append(fills);\n                break;\n            default:\n                str.append(fills);\n            }\n            return str.toString();\n        } else {\n            return s;\n        }\n    }\n\n    /**\n     * 格式化时间输出\n     * <p>\n     * 1d 15h 4m 15s 987ms\n     * </p>\n     */\n    public static String formatTime(long millis, int precision) {\n        long[] la = new long[5];\n        la[0] = (millis / 86400000);// days\n        la[1] = (millis / 3600000) % 24;// hours\n        la[2] = (millis / 60000) % 60;// minutes\n        la[3] = (millis / 1000) % 60;// seconds\n        la[4] = (millis % 1000);// ms\n\n        int index = 0;\n        for (int i = 0; i < la.length; i++) {\n            if (la[i] != 0) {\n                index = i;\n                break;\n            }\n        }\n\n        StringBuilder buf = new StringBuilder();\n        int validLength = la.length - index;\n        for (int i = 0; (i < validLength && i < precision); i++) {\n            buf.append(la[index]).append(timeFormat[index]);\n            index++;\n        }\n        return buf.toString();\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/HexFormatUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\n/**\n * @author mycat\n */\npublic final class HexFormatUtil {\n\t\n\tprivate final static char[] hexArray = \"0123456789ABCDEF\".toCharArray();\n\t\n\tpublic static String bytesToHexString(byte[] bytes) {\n\t    char[] hexChars = new char[bytes.length * 2];\n\t    for ( int j = 0; j < bytes.length; j++ ) {\n\t        int v = bytes[j] & 0xFF;\n\t        hexChars[j * 2] = hexArray[v >>> 4];\n\t        hexChars[j * 2 + 1] = hexArray[v & 0x0F];\n\t    }\n\t    return new String(hexChars);\n\t}\n\n    public static byte[] fromHex(String src) {\n        String[] hex = src.split(\" \");\n        byte[] b = new byte[hex.length];\n        for (int i = 0; i < hex.length; i++) {\n            b[i] = (byte) (Integer.parseInt(hex[i], 16) & 0xff);\n        }\n        return b;\n    }\n\n    public static String fromHex(String hexString, String charset) {\n        try {\n            byte[] b = fromHex(hexString);\n            if (charset == null) {\n                return new String(b);\n            }\n            return new String(b, charset);\n        } catch (Exception e) {\n            return null;\n        }\n    }\n\n    public static int fromHex2B(String src) {\n        byte[] b = fromHex(src);\n        int position = 0;\n        int i = (b[position++] & 0xff);\n        i |= (b[position++] & 0xff) << 8;\n        return i;\n    }\n\n    public static int fromHex4B(String src) {\n        byte[] b = fromHex(src);\n        int position = 0;\n        int i = (b[position++] & 0xff);\n        i |= (b[position++] & 0xff) << 8;\n        i |= (b[position++] & 0xff) << 16;\n        i |= (b[position++] & 0xff) << 24;\n        return i;\n    }\n\n    public static long fromHex8B(String src) {\n        byte[] b = fromHex(src);\n        int position = 0;\n        long l = (b[position++] & 0xff);\n        l |= (long) (b[position++] & 0xff) << 8;\n        l |= (long) (b[position++] & 0xff) << 16;\n        l |= (long) (b[position++] & 0xff) << 24;\n        l |= (long) (b[position++] & 0xff) << 32;\n        l |= (long) (b[position++] & 0xff) << 40;\n        l |= (long) (b[position++] & 0xff) << 48;\n        l |= (long) (b[position++] & 0xff) << 56;\n        return l;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/IntegerUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\n/**\n * @author mycat\n */\npublic final class IntegerUtil {\n\n    static final byte[] minValue = \"-2147483648\".getBytes();\n    static final int[] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE };\n    static final byte[] digitTens = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1',\n            '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3',\n            '3', '3', '3', '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5',\n            '5', '5', '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', '7',\n            '7', '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', '9', '9', '9', '9', '9', '9', '9', '9', '9', '9', };\n    static final byte[] digitOnes = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5',\n            '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',\n            '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7',\n            '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8',\n            '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', };\n    static final byte[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g',\n            'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };\n\n    public static byte[] toBytes(int i) {\n        if (i == Integer.MIN_VALUE) {\n            return minValue;\n        }\n        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);\n        byte[] buf = new byte[size];\n        getBytes(i, size, buf);\n        return buf;\n    }\n\n    static int stringSize(int x) {\n        for (int i = 0;; i++) {\n            if (x <= sizeTable[i]) {\n                return i + 1;\n            }\n        }\n    }\n\n    static void getBytes(int i, int index, byte[] buf) {\n        int q, r;\n        int charPos = index;\n        byte sign = 0;\n\n        if (i < 0) {\n            sign = '-';\n            i = -i;\n        }\n\n        // Generate two digits per iteration\n        while (i >= 65536) {\n            q = i / 100;\n            // really: r = i - (q * 100);\n            r = i - ((q << 6) + (q << 5) + (q << 2));\n            i = q;\n            buf[--charPos] = digitOnes[r];\n            buf[--charPos] = digitTens[r];\n        }\n\n        // Fall thru to fast mode for smaller numbers\n        // assert(i <= 65536, i);\n        for (;;) {\n            q = (i * 52429) >>> (16 + 3);\n            r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...\n            buf[--charPos] = digits[r];\n            i = q;\n            if (i == 0) {\n                break;\n            }\n        }\n        if (sign != 0) {\n            buf[--charPos] = sign;\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/LogUtil.java",
    "content": "package io.mycat.util;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.util.Locale;\n\nimport org.joda.time.DateTime;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.model.SystemConfig;\n\npublic class LogUtil {\n\tpublic static final Logger LOGGER = LoggerFactory.getLogger(LogUtil.class);\n\n\t/**\n\t *  將消息寫入到logs\\switch.log中\n\t *\n\t * @param\n\t * @param msg\n\t */\n\tpublic synchronized static void writeDataSourceLog(String msg) {\n\t\tFile file = new File(SystemConfig.getHomePath(), \"logs\" + File.separator + \"switch.log\");\n\t\tFileOutputStream fileOut = null;\n\t\ttry {\n\t\t\tFile parent = file.getParentFile();\n\t\t\tif (parent != null && !parent.exists()) {\n\t\t\t\tparent.mkdirs();\n\t\t\t}\n\t\t\tfileOut = new FileOutputStream(file, true);\n\t\t\tlong time = TimeUtil.currentTimeMillis();\n\t    \tDateTime dt2 = new DateTime(time);\n\t\t\tString m = String.format(\"%s: %s\\r\\n\",dt2.toString(DateUtil.DEFAULT_DATE_PATTERN,Locale.CHINESE) ,\n\t\t\t\t\tmsg);\n\t\t\tfileOut.write(m.getBytes());;\n\t\t\t\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.warn(\"write dataHost log err:\", e);\n\t\t} finally {\n\t\t\tif (fileOut != null) {\n\t\t\t\ttry {\n\t\t\t\t\tfileOut.close();\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/LongUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\n/**\n * @author mycat\n */\npublic final class LongUtil {\n\n    private static final byte[] minValue = \"-9223372036854775808\".getBytes();\n\n    public static byte[] toBytes(long i) {\n        if (i == Long.MIN_VALUE) {\n            return minValue;\n        }\n        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);\n        byte[] buf = new byte[size];\n        getBytes(i, size, buf);\n        return buf;\n    }\n\n    static int stringSize(long x) {\n        long p = 10;\n        for (int i = 1; i < 19; i++) {\n            if (x < p) {\n                return i;\n            }\n            p = 10 * p;\n        }\n        return 19;\n    }\n\n    static void getBytes(long i, int index, byte[] buf) {\n        long q;\n        int r;\n        int charPos = index;\n        byte sign = 0;\n\n        if (i < 0) {\n            sign = '-';\n            i = -i;\n        }\n\n        // Get 2 digits/iteration using longs until quotient fits into an int\n        while (i > Integer.MAX_VALUE) {\n            q = i / 100;\n            // really: r = i - (q * 100);\n            r = (int) (i - ((q << 6) + (q << 5) + (q << 2)));\n            i = q;\n            buf[--charPos] = IntegerUtil.digitOnes[r];\n            buf[--charPos] = IntegerUtil.digitTens[r];\n        }\n\n        // Get 2 digits/iteration using ints\n        int q2;\n        int i2 = (int) i;\n        while (i2 >= 65536) {\n            q2 = i2 / 100;\n            // really: r = i2 - (q * 100);\n            r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));\n            i2 = q2;\n            buf[--charPos] = IntegerUtil.digitOnes[r];\n            buf[--charPos] = IntegerUtil.digitTens[r];\n        }\n\n        // Fall thru to fast mode for smaller numbers\n        // assert(i2 <= 65536, i2);\n        for (;;) {\n            q2 = (i2 * 52429) >>> (16 + 3);\n            r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...\n            buf[--charPos] = IntegerUtil.digits[r];\n            i2 = q2;\n            if (i2 == 0) {\n                break;\n            }\n        }\n        if (sign != 0) {\n            buf[--charPos] = sign;\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/MysqlDefs.java",
    "content": "/*\n Copyright (C) 2002-2004 MySQL AB\n\n This program is free software; you can redistribute it and/or modify\n it under the terms of version 2 of the GNU AFFERO GENERAL PUBLIC LICENSE as \n published by the Free Software Foundation.\n\n There are special exceptions to the terms and conditions of the GPL \n as it is applied to this software. View the full text of the \n exception in file EXCEPTIONS-CONNECTOR-J in the directory of this \n software distribution.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU AFFERO GENERAL PUBLIC LICENSE for more details.\n\n You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE\n along with this program; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n\n\n */\n/*\n * \tThis program is free software; you can redistribute it and/or modify it under the terms of \n * the GNU AFFERO GENERAL PUBLIC LICENSE as published by the Free Software Foundation; either version 3 of the License, \n * or (at your option) any later version. \n * \n * \tThis program is distributed in the hope that it will be useful, \n * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n * See the GNU AFFERO GENERAL PUBLIC LICENSE for more details. \n * \tYou should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE along with this program; \n * if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n */\npackage io.mycat.util;\n\nimport java.sql.Types;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\n\n/**\n * copy from mysql-connector-j MysqlDefs contains many values that are needed\n * for communication with the MySQL server.\n * \n * @author Mark Matthews\n * @version $Id: MysqlDefs.java 4724 2005-12-20 23:27:01Z mmatthews $\n */\npublic final class MysqlDefs {\n\t// ~ Static fields/initializers\n\t// ---------------------------------------------\n\n\tpublic static final int COM_BINLOG_DUMP = 18;\n\n\tpublic static final int COM_CHANGE_USER = 17;\n\n\tpublic static final int COM_CLOSE_STATEMENT = 25;\n\n\tpublic static final int COM_CONNECT_OUT = 20;\n\n\tpublic static final int COM_END = 29;\n\n\tpublic static final int COM_EXECUTE = 23;\n\n\tpublic static final int COM_FETCH = 28;\n\n\tpublic static final int COM_LONG_DATA = 24;\n\n\tpublic static final int COM_PREPARE = 22;\n\n\tpublic static final int COM_REGISTER_SLAVE = 21;\n\n\tpublic static final int COM_RESET_STMT = 26;\n\n\tpublic static final int COM_SET_OPTION = 27;\n\n\tpublic static final int COM_TABLE_DUMP = 19;\n\n\tpublic static final int CONNECT = 11;\n\n\tpublic static final int CREATE_DB = 5;\n\n\tpublic static final int DEBUG = 13;\n\n\tpublic static final int DELAYED_INSERT = 16;\n\n\tpublic static final int DROP_DB = 6;\n\n\tpublic static final int FIELD_LIST = 4;\n\n\tpublic static final int FIELD_TYPE_BIT = 16;\n\n\tpublic static final int FIELD_TYPE_BLOB = 252;\n\n\tpublic static final int FIELD_TYPE_DATE = 10;\n\n\tpublic static final int FIELD_TYPE_DATETIME = 12;\n\n\t// Data Types\n\tpublic static final int FIELD_TYPE_DECIMAL = 0;\n\n\tpublic static final int FIELD_TYPE_DOUBLE = 5;\n\n\tpublic static final int FIELD_TYPE_ENUM = 247;\n\n\tpublic static final int FIELD_TYPE_FLOAT = 4;\n\n\tpublic static final int FIELD_TYPE_GEOMETRY = 255;\n\n\tpublic static final int FIELD_TYPE_INT24 = 9;\n\n\tpublic static final int FIELD_TYPE_LONG = 3;\n\n\tpublic static final int FIELD_TYPE_LONG_BLOB = 251;\n\n\tpublic static final int FIELD_TYPE_LONGLONG = 8;\n\n\tpublic static final int FIELD_TYPE_MEDIUM_BLOB = 250;\n\n\tpublic static final int FIELD_TYPE_NEW_DECIMAL = 246;\n\n\tpublic static final int FIELD_TYPE_NEWDATE = 14;\n\n\tpublic static final int FIELD_TYPE_NULL = 6;\n\n\tpublic static final int FIELD_TYPE_SET = 248;\n\n\tpublic static final int FIELD_TYPE_SHORT = 2;\n\n\tpublic static final int FIELD_TYPE_STRING = 254;\n\n\tpublic static final int FIELD_TYPE_TIME = 11;\n\n\tpublic static final int FIELD_TYPE_TIMESTAMP = 7;\n\n\tpublic static final int FIELD_TYPE_TINY = 1;\n\n\t// Older data types\n\tpublic static final int FIELD_TYPE_TINY_BLOB = 249;\n\n\tpublic static final int FIELD_TYPE_VAR_STRING = 253;\n\n\tpublic static final int FIELD_TYPE_VARCHAR = 15;\n\n\t// Newer data types\n\tpublic static final int FIELD_TYPE_YEAR = 13;\n\n\tpublic static final int INIT_DB = 2;\n\n\tpublic static final long LENGTH_BLOB = 65535;\n\n\tpublic static final long LENGTH_LONGBLOB = 4294967295L;\n\n\tpublic static final long LENGTH_MEDIUMBLOB = 16777215;\n\n\tpublic static final long LENGTH_TINYBLOB = 255;\n\n\t// Limitations\n\tpublic static final int MAX_ROWS = 50000000; // From the MySQL FAQ\n\n\t/**\n\t * Used to indicate that the server sent no field-level character set\n\t * information, so the driver should use the connection-level character\n\t * encoding instead.\n\t */\n\tpublic static final int NO_CHARSET_INFO = -1;\n\n\tpublic static final byte OPEN_CURSOR_FLAG = 1;\n\n\tpublic static final int PING = 14;\n\n\tpublic static final int PROCESS_INFO = 10;\n\n\tpublic static final int PROCESS_KILL = 12;\n\n\tpublic static final int QUERY = 3;\n\n\tpublic static final int QUIT = 1;\n\n\t// ~ Methods\n\t// ----------------------------------------------------------------\n\n\tpublic static final int RELOAD = 7;\n\n\tpublic static final int SHUTDOWN = 8;\n\n\t//\n\t// Constants defined from mysql\n\t//\n\t// DB Operations\n\tpublic static final int SLEEP = 0;\n\n\tpublic static final int STATISTICS = 9;\n\n\tpublic static final int TIME = 15;\n\n\t/**\n\t * Maps the given MySQL type to the correct JDBC type.\n\t */\n\tpublic static int mysqlToJavaType(int mysqlType) {\n\t\tint jdbcType;\n\n\t\tswitch (mysqlType) {\n\t\tcase MysqlDefs.FIELD_TYPE_NEW_DECIMAL:\n\t\tcase MysqlDefs.FIELD_TYPE_DECIMAL:\n\t\t\tjdbcType = Types.DECIMAL;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_TINY:\n\t\t\tjdbcType = Types.TINYINT;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_SHORT:\n\t\t\tjdbcType = Types.SMALLINT;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_LONG:\n\t\t\tjdbcType = Types.INTEGER;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_FLOAT:\n\t\t\tjdbcType = Types.REAL;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_DOUBLE:\n\t\t\tjdbcType = Types.DOUBLE;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_NULL:\n\t\t\tjdbcType = Types.NULL;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_TIMESTAMP:\n\t\t\tjdbcType = Types.TIMESTAMP;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_LONGLONG:\n\t\t\tjdbcType = Types.BIGINT;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_INT24:\n\t\t\tjdbcType = Types.INTEGER;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_DATE:\n\t\t\tjdbcType = Types.DATE;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_TIME:\n\t\t\tjdbcType = Types.TIME;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_DATETIME:\n\t\t\tjdbcType = Types.TIMESTAMP;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_YEAR:\n\t\t\tjdbcType = Types.DATE;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_NEWDATE:\n\t\t\tjdbcType = Types.DATE;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_ENUM:\n\t\t\tjdbcType = Types.CHAR;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_SET:\n\t\t\tjdbcType = Types.CHAR;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_TINY_BLOB:\n\t\t\tjdbcType = Types.VARBINARY;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:\n\t\t\tjdbcType = Types.LONGVARBINARY;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_LONG_BLOB:\n\t\t\tjdbcType = Types.LONGVARBINARY;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_BLOB:\n\t\t\tjdbcType = Types.LONGVARBINARY;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_VAR_STRING:\n\t\tcase MysqlDefs.FIELD_TYPE_VARCHAR:\n\t\t\tjdbcType = Types.VARCHAR;\n\n\t\t\tbreak;\n\n\t\tcase MysqlDefs.FIELD_TYPE_STRING:\n\t\t\tjdbcType = Types.CHAR;\n\n\t\t\tbreak;\n\t\tcase MysqlDefs.FIELD_TYPE_GEOMETRY:\n\t\t\tjdbcType = Types.BINARY;\n\n\t\t\tbreak;\n\t\tcase MysqlDefs.FIELD_TYPE_BIT:\n\t\t\tjdbcType = Types.BIT;\n\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tjdbcType = Types.VARCHAR;\n\t\t}\n\n\t\treturn jdbcType;\n\t}\n\n\tpublic static int javaTypeDetect(int javaType, int scale) {\n\t\tswitch (javaType) {\n\t\tcase Types.NUMERIC: {\n\t\t\tif (scale > 0) {\n\t\t\t\treturn Types.DECIMAL;\n\t\t\t}else{\n\t\t\t\treturn javaType;\n\t\t\t}\n\t\t}\n\t\tdefault: {\n\t\t\treturn javaType;\n\t\t}\n\t\t}\n\n\t}\n\n\tpublic static int javaTypeMysql(int javaType) {\n\n\t\tswitch (javaType) {\n\t\tcase Types.NUMERIC:\n\t\t\treturn MysqlDefs.FIELD_TYPE_DECIMAL;\n\n\t\tcase Types.DECIMAL:\n\t\t\treturn MysqlDefs.FIELD_TYPE_NEW_DECIMAL;\n\n\t\tcase Types.TINYINT:\n\t\t\treturn MysqlDefs.FIELD_TYPE_TINY;\n\n\t\tcase Types.SMALLINT:\n\t\t\treturn MysqlDefs.FIELD_TYPE_SHORT;\n\n\t\tcase Types.INTEGER:\n\t\t\treturn MysqlDefs.FIELD_TYPE_LONG;\n\n\t\tcase Types.REAL:\n\t\t\treturn MysqlDefs.FIELD_TYPE_FLOAT;\n\n\t\tcase Types.DOUBLE:\n\t\t\treturn MysqlDefs.FIELD_TYPE_DOUBLE;\n\n\t\tcase Types.NULL:\n\t\t\treturn MysqlDefs.FIELD_TYPE_NULL;\n\n\t\tcase Types.TIMESTAMP:\n\t\t\treturn MysqlDefs.FIELD_TYPE_TIMESTAMP;\n\n\t\tcase Types.BIGINT:\n\t\t\treturn MysqlDefs.FIELD_TYPE_LONGLONG;\n\n\t\tcase Types.DATE:\n\t\t\treturn MysqlDefs.FIELD_TYPE_DATE;\n\n\t\tcase Types.TIME:\n\t\t\treturn MysqlDefs.FIELD_TYPE_TIME;\n\n\t\tcase Types.VARBINARY:\n\t\t\treturn MysqlDefs.FIELD_TYPE_TINY_BLOB;\n\n\t\tcase Types.LONGVARBINARY:\n\t\t\treturn MysqlDefs.FIELD_TYPE_BLOB;\n            //对应sqlserver的image类型\n            case 27:\n                return MysqlDefs.FIELD_TYPE_BLOB;\n\n\t\tcase Types.VARCHAR:\n\t\t\treturn MysqlDefs.FIELD_TYPE_VAR_STRING;\n\n\t\tcase Types.CHAR:\n\t\t\treturn MysqlDefs.FIELD_TYPE_STRING;\n\n\t\tcase Types.BINARY:\n\t\t\treturn MysqlDefs.FIELD_TYPE_GEOMETRY;\n\n\t\tcase Types.BIT:\n\t\t\treturn MysqlDefs.FIELD_TYPE_BIT;\n\t\tcase Types.CLOB:\n\t\t\treturn MysqlDefs.FIELD_TYPE_VAR_STRING;\n\t\tcase Types.BLOB:\n\t\t\treturn MysqlDefs.FIELD_TYPE_BLOB;\n\n\t\t//修改by     magicdoom@gmail.com\n\t\t// 当jdbc连接非mysql的数据库时，需要把对应类型映射为mysql的类型，否则应用端会出错\n\t\t\tcase Types.NVARCHAR:\n\t\t\t\treturn MysqlDefs.FIELD_TYPE_VAR_STRING;\n\t\t\tcase Types.NCHAR:\n\t\t\t\treturn MysqlDefs.FIELD_TYPE_STRING;\n\t\t\tcase Types.NCLOB:\n\t\t\t\treturn MysqlDefs.FIELD_TYPE_VAR_STRING;\n\t\t\tcase Types.LONGNVARCHAR:\n\t\t\t\treturn MysqlDefs.FIELD_TYPE_VAR_STRING;\n\n\t\tdefault:\n\t\t\treturn MysqlDefs.FIELD_TYPE_VAR_STRING;   //其他未知类型返回字符类型\n\t\t//\treturn Types.VARCHAR;\n\t\t}\n\n\t}\n\n\t/**\n\t * Maps the given MySQL type to the correct JDBC type.\n\t */\n\tstatic int mysqlToJavaType(String mysqlType) {\n\t\tif (mysqlType.equalsIgnoreCase(\"BIT\")) {\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_BIT);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"TINYINT\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_TINY);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"SMALLINT\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_SHORT);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"MEDIUMINT\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_INT24);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"INT\") || mysqlType.equalsIgnoreCase(\"INTEGER\")) { //$NON-NLS-1$ //$NON-NLS-2$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_LONG);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"BIGINT\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_LONGLONG);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"INT24\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_INT24);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"REAL\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_DOUBLE);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"FLOAT\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_FLOAT);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"DECIMAL\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_DECIMAL);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"NUMERIC\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_DECIMAL);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"DOUBLE\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_DOUBLE);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"CHAR\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_STRING);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"VARCHAR\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_VAR_STRING);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"DATE\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_DATE);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"TIME\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_TIME);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"YEAR\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_YEAR);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"TIMESTAMP\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_TIMESTAMP);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"DATETIME\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_DATETIME);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"TINYBLOB\")) { //$NON-NLS-1$\n\t\t\treturn java.sql.Types.BINARY;\n\t\t} else if (mysqlType.equalsIgnoreCase(\"BLOB\")) { //$NON-NLS-1$\n\t\t\treturn java.sql.Types.LONGVARBINARY;\n\t\t} else if (mysqlType.equalsIgnoreCase(\"MEDIUMBLOB\")) { //$NON-NLS-1$\n\t\t\treturn java.sql.Types.LONGVARBINARY;\n\t\t} else if (mysqlType.equalsIgnoreCase(\"LONGBLOB\")) { //$NON-NLS-1$\n\t\t\treturn java.sql.Types.LONGVARBINARY;\n\t\t} else if (mysqlType.equalsIgnoreCase(\"TINYTEXT\")) { //$NON-NLS-1$\n\t\t\treturn java.sql.Types.VARCHAR;\n\t\t} else if (mysqlType.equalsIgnoreCase(\"TEXT\")) { //$NON-NLS-1$\n\t\t\treturn java.sql.Types.LONGVARCHAR;\n\t\t} else if (mysqlType.equalsIgnoreCase(\"MEDIUMTEXT\")) { //$NON-NLS-1$\n\t\t\treturn java.sql.Types.LONGVARCHAR;\n\t\t} else if (mysqlType.equalsIgnoreCase(\"LONGTEXT\")) { //$NON-NLS-1$\n\t\t\treturn java.sql.Types.LONGVARCHAR;\n\t\t} else if (mysqlType.equalsIgnoreCase(\"ENUM\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_ENUM);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"SET\")) { //$NON-NLS-1$\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_SET);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"GEOMETRY\")) {\n\t\t\treturn mysqlToJavaType(FIELD_TYPE_GEOMETRY);\n\t\t} else if (mysqlType.equalsIgnoreCase(\"BINARY\")) {\n\t\t\treturn Types.BINARY; // no concrete type on the wire\n\t\t} else if (mysqlType.equalsIgnoreCase(\"VARBINARY\")) {\n\t\t\treturn Types.VARBINARY; // no concrete type on the wire\n\t\t} \n\n\t\t// Punt\n\t\treturn java.sql.Types.OTHER;\n\t}\n\n\t/**\n\t * @param mysqlType\n\t * @return\n\t */\n\tpublic static String typeToName(int mysqlType) {\n\t\tswitch (mysqlType) {\n\t\tcase MysqlDefs.FIELD_TYPE_DECIMAL:\n\t\t\treturn \"FIELD_TYPE_DECIMAL\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_TINY:\n\t\t\treturn \"FIELD_TYPE_TINY\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_SHORT:\n\t\t\treturn \"FIELD_TYPE_SHORT\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_LONG:\n\t\t\treturn \"FIELD_TYPE_LONG\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_FLOAT:\n\t\t\treturn \"FIELD_TYPE_FLOAT\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_DOUBLE:\n\t\t\treturn \"FIELD_TYPE_DOUBLE\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_NULL:\n\t\t\treturn \"FIELD_TYPE_NULL\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_TIMESTAMP:\n\t\t\treturn \"FIELD_TYPE_TIMESTAMP\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_LONGLONG:\n\t\t\treturn \"FIELD_TYPE_LONGLONG\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_INT24:\n\t\t\treturn \"FIELD_TYPE_INT24\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_DATE:\n\t\t\treturn \"FIELD_TYPE_DATE\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_TIME:\n\t\t\treturn \"FIELD_TYPE_TIME\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_DATETIME:\n\t\t\treturn \"FIELD_TYPE_DATETIME\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_YEAR:\n\t\t\treturn \"FIELD_TYPE_YEAR\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_NEWDATE:\n\t\t\treturn \"FIELD_TYPE_NEWDATE\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_ENUM:\n\t\t\treturn \"FIELD_TYPE_ENUM\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_SET:\n\t\t\treturn \"FIELD_TYPE_SET\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_TINY_BLOB:\n\t\t\treturn \"FIELD_TYPE_TINY_BLOB\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:\n\t\t\treturn \"FIELD_TYPE_MEDIUM_BLOB\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_LONG_BLOB:\n\t\t\treturn \"FIELD_TYPE_LONG_BLOB\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_BLOB:\n\t\t\treturn \"FIELD_TYPE_BLOB\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_VAR_STRING:\n\t\t\treturn \"FIELD_TYPE_VAR_STRING\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_STRING:\n\t\t\treturn \"FIELD_TYPE_STRING\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_VARCHAR:\n\t\t\treturn \"FIELD_TYPE_VARCHAR\";\n\n\t\tcase MysqlDefs.FIELD_TYPE_GEOMETRY:\n\t\t\treturn \"FIELD_TYPE_GEOMETRY\";\n\n\t\tdefault:\n\t\t\treturn \" Unknown MySQL Type # \" + mysqlType;\n\t\t}\n\t}\n\n\tpublic static boolean isBianry(byte mysqlType) {\n\t\tint type = mysqlType;\n\t\tif(type < 0) {\n\t\t\ttype += 256;\n\t\t}\n\n\t\tif(type == MysqlDefs.FIELD_TYPE_BLOB || type == MysqlDefs.FIELD_TYPE_TINY_BLOB ||\n\t\t\t\ttype == MysqlDefs.FIELD_TYPE_MEDIUM_BLOB || type == MysqlDefs.FIELD_TYPE_LONG_BLOB) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate static Map<String, Integer> mysqlToJdbcTypesMap = new HashMap<String, Integer>();\n\n\tstatic {\n\t\tmysqlToJdbcTypesMap.put(\"BIT\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_BIT)));\n\n\t\tmysqlToJdbcTypesMap.put(\"TINYINT\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_TINY)));\n\t\tmysqlToJdbcTypesMap.put(\"SMALLINT\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_SHORT)));\n\t\tmysqlToJdbcTypesMap.put(\"MEDIUMINT\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_INT24)));\n\t\tmysqlToJdbcTypesMap.put(\"INT\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_LONG)));\n\t\tmysqlToJdbcTypesMap.put(\"INTEGER\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_LONG)));\n\t\tmysqlToJdbcTypesMap.put(\"BIGINT\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_LONGLONG)));\n\t\tmysqlToJdbcTypesMap.put(\"INT24\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_INT24)));\n\t\tmysqlToJdbcTypesMap.put(\"REAL\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_DOUBLE)));\n\t\tmysqlToJdbcTypesMap.put(\"FLOAT\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_FLOAT)));\n\t\tmysqlToJdbcTypesMap.put(\"DECIMAL\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_DECIMAL)));\n\t\tmysqlToJdbcTypesMap.put(\"NUMERIC\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_DECIMAL)));\n\t\tmysqlToJdbcTypesMap.put(\"DOUBLE\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_DOUBLE)));\n\t\tmysqlToJdbcTypesMap.put(\"CHAR\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_STRING)));\n\t\tmysqlToJdbcTypesMap.put(\"VARCHAR\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_VAR_STRING)));\n\t\tmysqlToJdbcTypesMap.put(\"DATE\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_DATE)));\n\t\tmysqlToJdbcTypesMap.put(\"TIME\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_TIME)));\n\t\tmysqlToJdbcTypesMap.put(\"YEAR\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_YEAR)));\n\t\tmysqlToJdbcTypesMap.put(\"TIMESTAMP\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_TIMESTAMP)));\n\t\tmysqlToJdbcTypesMap.put(\"DATETIME\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_DATETIME)));\n\t\tmysqlToJdbcTypesMap.put(\"TINYBLOB\", new Integer(java.sql.Types.BINARY));\n\t\tmysqlToJdbcTypesMap.put(\"BLOB\", new Integer(\n\t\t\t\tjava.sql.Types.LONGVARBINARY));\n\t\tmysqlToJdbcTypesMap.put(\"MEDIUMBLOB\", new Integer(\n\t\t\t\tjava.sql.Types.LONGVARBINARY));\n\t\tmysqlToJdbcTypesMap.put(\"LONGBLOB\", new Integer(\n\t\t\t\tjava.sql.Types.LONGVARBINARY));\n\t\tmysqlToJdbcTypesMap\n\t\t\t\t.put(\"TINYTEXT\", new Integer(java.sql.Types.VARCHAR));\n\t\tmysqlToJdbcTypesMap\n\t\t\t\t.put(\"TEXT\", new Integer(java.sql.Types.LONGVARCHAR));\n\t\tmysqlToJdbcTypesMap.put(\"MEDIUMTEXT\", new Integer(\n\t\t\t\tjava.sql.Types.LONGVARCHAR));\n\t\tmysqlToJdbcTypesMap.put(\"LONGTEXT\", new Integer(\n\t\t\t\tjava.sql.Types.LONGVARCHAR));\n\t\tmysqlToJdbcTypesMap.put(\"ENUM\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_ENUM)));\n\t\tmysqlToJdbcTypesMap.put(\"SET\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_SET)));\n\t\tmysqlToJdbcTypesMap.put(\"GEOMETRY\", new Integer(\n\t\t\t\tmysqlToJavaType(FIELD_TYPE_GEOMETRY)));\n\t}\n\n\tstatic final void appendJdbcTypeMappingQuery(StringBuffer buf,\n\t\t\tString mysqlTypeColumnName) {\n\n\t\tbuf.append(\"CASE \");\n\t\tMap<String, Integer> typesMap = new HashMap<String, Integer>();\n\t\ttypesMap.putAll(mysqlToJdbcTypesMap);\n\t\ttypesMap.put(\"BINARY\", new Integer(Types.BINARY));\n\t\ttypesMap.put(\"VARBINARY\", new Integer(Types.VARBINARY));\n\n\t\tIterator<String> mysqlTypes = typesMap.keySet().iterator();\n\n\t\twhile (mysqlTypes.hasNext()) {\n\t\t\tString mysqlTypeName = (String) mysqlTypes.next();\n\t\t\tbuf.append(\" WHEN \");\n\t\t\tbuf.append(mysqlTypeColumnName);\n\t\t\tbuf.append(\"='\");\n\t\t\tbuf.append(mysqlTypeName);\n\t\t\tbuf.append(\"' THEN \");\n\t\t\tbuf.append(typesMap.get(mysqlTypeName));\n\n\t\t\tif (mysqlTypeName.equalsIgnoreCase(\"DOUBLE\")\n\t\t\t\t\t|| mysqlTypeName.equalsIgnoreCase(\"FLOAT\")\n\t\t\t\t\t|| mysqlTypeName.equalsIgnoreCase(\"DECIMAL\")\n\t\t\t\t\t|| mysqlTypeName.equalsIgnoreCase(\"NUMERIC\")) {\n\t\t\t\tbuf.append(\" WHEN \");\n\t\t\t\tbuf.append(mysqlTypeColumnName);\n\t\t\t\tbuf.append(\"='\");\n\t\t\t\tbuf.append(mysqlTypeName);\n\t\t\t\tbuf.append(\" unsigned' THEN \");\n\t\t\t\tbuf.append(typesMap.get(mysqlTypeName));\n\t\t\t}\n\t\t}\n\n\t\tbuf.append(\" ELSE \");\n\t\tbuf.append(Types.OTHER);\n\t\tbuf.append(\" END \");\n\n\t}\n\n\tpublic static final String SQL_STATE_BASE_TABLE_NOT_FOUND = \"S0002\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_BASE_TABLE_OR_VIEW_ALREADY_EXISTS = \"S0001\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND = \"42S02\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_COLUMN_ALREADY_EXISTS = \"S0021\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_COLUMN_NOT_FOUND = \"S0022\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_COMMUNICATION_LINK_FAILURE = \"08S01\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_CONNECTION_FAIL_DURING_TX = \"08007\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_CONNECTION_IN_USE = \"08002\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_CONNECTION_NOT_OPEN = \"08003\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_CONNECTION_REJECTED = \"08004\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_DATE_TRUNCATED = \"01004\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_DATETIME_FIELD_OVERFLOW = \"22008\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_DEADLOCK = \"41000\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_DISCONNECT_ERROR = \"01002\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_DIVISION_BY_ZERO = \"22012\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_DRIVER_NOT_CAPABLE = \"S1C00\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_ERROR_IN_ROW = \"01S01\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_GENERAL_ERROR = \"S1000\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_ILLEGAL_ARGUMENT = \"S1009\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_INDEX_ALREADY_EXISTS = \"S0011\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_INDEX_NOT_FOUND = \"S0012\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST = \"21S01\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_INVALID_AUTH_SPEC = \"28000\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST = \"22018\"; // $NON_NLS\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// -\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// $\n\n\tpublic static final String SQL_STATE_INVALID_COLUMN_NUMBER = \"S1002\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_INVALID_CONNECTION_ATTRIBUTE = \"01S00\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_MEMORY_ALLOCATION_FAILURE = \"S1001\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_MORE_THAN_ONE_ROW_UPDATED_OR_DELETED = \"01S04\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_NO_DEFAULT_FOR_COLUMN = \"S0023\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_NO_ROWS_UPDATED_OR_DELETED = \"01S03\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE = \"22003\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_PRIVILEGE_NOT_REVOKED = \"01006\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_SYNTAX_ERROR = \"42000\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_TIMEOUT_EXPIRED = \"S1T00\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN = \"08007\"; // $NON_NLS\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// -\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// $\n\n\tpublic static final String SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE = \"08001\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_WRONG_NO_OF_PARAMETERS = \"07001\"; //$NON-NLS-1$\n\n\tpublic static final String SQL_STATE_INVALID_TRANSACTION_TERMINATION = \"2D000\"; // $NON_NLS\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// -\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// $\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/NameableExecutor.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * @author mycat\n */\npublic class NameableExecutor extends ThreadPoolExecutor {\n\n    protected String name;\n\n    public NameableExecutor(String name, int size, BlockingQueue<Runnable> queue, ThreadFactory factory) {\n        super(size, size, Long.MAX_VALUE, TimeUnit.NANOSECONDS, queue, factory);\n        this.name = name;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/NameableThreadFactory.java",
    "content": "package io.mycat.util;\n\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class NameableThreadFactory implements ThreadFactory {\n\tprivate final ThreadGroup group;\n\tprivate final String namePrefix;\n\tprivate final AtomicInteger threadId;\n\tprivate final boolean isDaemon;\n\n\tpublic NameableThreadFactory(String name, boolean isDaemon) {\n\t\tSecurityManager s = System.getSecurityManager();\n\t\tthis.group = (s != null) ? s.getThreadGroup() : Thread.currentThread()\n\t\t\t\t.getThreadGroup();\n\t\tthis.namePrefix = name;\n\t\tthis.threadId = new AtomicInteger(0);\n\t\tthis.isDaemon = isDaemon;\n\t}\n\n\tpublic Thread newThread(Runnable r) {\n\t\tThread t = new Thread(group, r, namePrefix + threadId.getAndIncrement());\n\t\tt.setDaemon(isDaemon);\n\t\treturn t;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/ObjectUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.beans.BeanInfo;\nimport java.beans.IntrospectionException;\nimport java.beans.Introspector;\nimport java.beans.PropertyDescriptor;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author mycat\n */\npublic final class ObjectUtil {\n    private static final Logger LOGGER = LoggerFactory.getLogger(ObjectUtil.class);\n\n\n    public static Object getStaticFieldValue(String className,String fieldName)\n    {\n        Class clazz = null;\n        try\n        {\n            clazz = Class.forName(className);\n           Field field = clazz.getField(fieldName);\n             if(field!=null) {\n                 return field.get(null);\n             }\n        } catch (ClassNotFoundException e)\n        {\n            //LOGGER.error(\"getStaticFieldValue\", e);\n        } catch (NoSuchFieldException e)\n        {\n           // LOGGER.error(\"getStaticFieldValue\", e);\n        } catch (IllegalAccessException e)\n        {\n          //  LOGGER.error(\"getStaticFieldValue\", e);\n        }\n        return null;\n    }\n\n    \n\tpublic static Object copyObject(Object object) {\n\t\tByteArrayOutputStream b = new ByteArrayOutputStream();\n\t\tObjectOutputStream s = null;\n\t\ttry {\n\t\t\ts = new ObjectOutputStream(b);\n\t\t\ts.writeObject(object);\n\t\t\tObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(b.toByteArray()));\n\t\t\treturn ois.readObject();\n\t\t} catch (IOException e) {\n            throw new RuntimeException(e);\n\t\t} catch (ClassNotFoundException e) {\n            throw new RuntimeException(e);\n\t\t}\n\n\t}\n\t\n    /**\n     * 递归地比较两个数组是否相同，支持多维数组。\n     * <p>\n     * 如果比较的对象不是数组，则此方法的结果同<code>ObjectUtil.equals</code>。\n     * </p>\n     * \n     * @param array1\n     *            数组1\n     * @param array2\n     *            数组2\n     * @return 如果相等, 则返回<code>true</code>\n     */\n    public static boolean equals(Object array1, Object array2) {\n        if (array1 == array2) {\n            return true;\n        }\n\n        if ((array1 == null) || (array2 == null)) {\n            return false;\n        }\n\n        Class<? extends Object> clazz = array1.getClass();\n\n        if (!clazz.equals(array2.getClass())) {\n            return false;\n        }\n\n        if (!clazz.isArray()) {\n            return array1.equals(array2);\n        }\n\n        // array1和array2为同类型的数组\n        if (array1 instanceof long[]) {\n            long[] longArray1 = (long[]) array1;\n            long[] longArray2 = (long[]) array2;\n\n            if (longArray1.length != longArray2.length) {\n                return false;\n            }\n\n            for (int i = 0; i < longArray1.length; i++) {\n                if (longArray1[i] != longArray2[i]) {\n                    return false;\n                }\n            }\n\n            return true;\n        } else if (array1 instanceof int[]) {\n            int[] intArray1 = (int[]) array1;\n            int[] intArray2 = (int[]) array2;\n\n            if (intArray1.length != intArray2.length) {\n                return false;\n            }\n\n            for (int i = 0; i < intArray1.length; i++) {\n                if (intArray1[i] != intArray2[i]) {\n                    return false;\n                }\n            }\n\n            return true;\n        } else if (array1 instanceof short[]) {\n            short[] shortArray1 = (short[]) array1;\n            short[] shortArray2 = (short[]) array2;\n\n            if (shortArray1.length != shortArray2.length) {\n                return false;\n            }\n\n            for (int i = 0; i < shortArray1.length; i++) {\n                if (shortArray1[i] != shortArray2[i]) {\n                    return false;\n                }\n            }\n\n            return true;\n        } else if (array1 instanceof byte[]) {\n            byte[] byteArray1 = (byte[]) array1;\n            byte[] byteArray2 = (byte[]) array2;\n\n            if (byteArray1.length != byteArray2.length) {\n                return false;\n            }\n\n            for (int i = 0; i < byteArray1.length; i++) {\n                if (byteArray1[i] != byteArray2[i]) {\n                    return false;\n                }\n            }\n\n            return true;\n        } else if (array1 instanceof double[]) {\n            double[] doubleArray1 = (double[]) array1;\n            double[] doubleArray2 = (double[]) array2;\n\n            if (doubleArray1.length != doubleArray2.length) {\n                return false;\n            }\n\n            for (int i = 0; i < doubleArray1.length; i++) {\n                if (Double.doubleToLongBits(doubleArray1[i]) != Double.doubleToLongBits(doubleArray2[i])) {\n                    return false;\n                }\n            }\n\n            return true;\n        } else if (array1 instanceof float[]) {\n            float[] floatArray1 = (float[]) array1;\n            float[] floatArray2 = (float[]) array2;\n\n            if (floatArray1.length != floatArray2.length) {\n                return false;\n            }\n\n            for (int i = 0; i < floatArray1.length; i++) {\n                if (Float.floatToIntBits(floatArray1[i]) != Float.floatToIntBits(floatArray2[i])) {\n                    return false;\n                }\n            }\n\n            return true;\n        } else if (array1 instanceof boolean[]) {\n            boolean[] booleanArray1 = (boolean[]) array1;\n            boolean[] booleanArray2 = (boolean[]) array2;\n\n            if (booleanArray1.length != booleanArray2.length) {\n                return false;\n            }\n\n            for (int i = 0; i < booleanArray1.length; i++) {\n                if (booleanArray1[i] != booleanArray2[i]) {\n                    return false;\n                }\n            }\n\n            return true;\n        } else if (array1 instanceof char[]) {\n            char[] charArray1 = (char[]) array1;\n            char[] charArray2 = (char[]) array2;\n\n            if (charArray1.length != charArray2.length) {\n                return false;\n            }\n\n            for (int i = 0; i < charArray1.length; i++) {\n                if (charArray1[i] != charArray2[i]) {\n                    return false;\n                }\n            }\n\n            return true;\n        } else {\n            Object[] objectArray1 = (Object[]) array1;\n            Object[] objectArray2 = (Object[]) array2;\n\n            if (objectArray1.length != objectArray2.length) {\n                return false;\n            }\n\n            for (int i = 0; i < objectArray1.length; i++) {\n                if (!equals(objectArray1[i], objectArray2[i])) {\n                    return false;\n                }\n            }\n\n            return true;\n        }\n    }\n\n\n    public static void copyProperties(Object fromObj, Object toObj) {\n        Class<? extends Object> fromClass = fromObj.getClass();\n        Class<? extends Object> toClass = toObj.getClass();\n\n        try {\n            BeanInfo fromBean = Introspector.getBeanInfo(fromClass);\n            BeanInfo toBean = Introspector.getBeanInfo(toClass);\n\n            PropertyDescriptor[] toPd = toBean.getPropertyDescriptors();\n            List<PropertyDescriptor> fromPd = Arrays.asList(fromBean\n                    .getPropertyDescriptors());\n\n            for (PropertyDescriptor propertyDescriptor : toPd) {\n                propertyDescriptor.getDisplayName();\n                PropertyDescriptor pd = fromPd.get(fromPd\n                        .indexOf(propertyDescriptor));\n                if (pd.getDisplayName().equals(\n                        propertyDescriptor.getDisplayName())\n                        && !pd.getDisplayName().equals(\"class\")\n                        && propertyDescriptor.getWriteMethod() != null) {\n                        propertyDescriptor.getWriteMethod().invoke(toObj, pd.getReadMethod().invoke(fromObj, null));\n                }\n\n            }\n        } catch (IntrospectionException e) {\n          throw  new RuntimeException(e);\n        } catch (IllegalArgumentException e) {\n            throw  new RuntimeException(e);\n        } catch (IllegalAccessException e) {\n            throw  new RuntimeException(e);\n        } catch (InvocationTargetException e) {\n            throw  new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/ProcessUtil.java",
    "content": "package io.mycat.util;\n\nimport io.mycat.migrate.MigrateUtils;\nimport io.mycat.util.dataMigrator.DataMigratorUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class ProcessUtil\n{\n    private static Logger LOGGER = LoggerFactory.getLogger((ProcessUtil.class));\n\n\n\n    public static int exec(String cmd) {\n        Process process = null;\n        try {\n            Runtime runtime = Runtime.getRuntime();\n            process = runtime.exec(cmd);\n            new StreamGobble(process.getInputStream(), \"INFO\").start();\n            new StreamGobble(process.getErrorStream(), \"ERROR\").start();\n            return process.waitFor();\n        } catch (Throwable t) {\n           LOGGER.error(t.getMessage());\n        } finally {\n            if (process != null)\n                process.destroy();\n\n        }\n        return 0;\n    }\n    public static String execReturnString(List<String> cmd) {\n        Process process = null;\n        try {\n            //            Runtime runtime = Runtime.getRuntime();\n            //            process = runtime.exec(cmd);\n            ProcessBuilder pb = new ProcessBuilder(cmd);\n            pb.redirectErrorStream(true);\n            process=pb.start();\n            StreamGobble inputGobble = new StreamGobble(process.getInputStream(), \"INFO\");\n            inputGobble.start();\n            new StreamGobble(process.getErrorStream(), \"ERROR\").start();\n            process.waitFor();\n            return inputGobble.getResult();\n        } catch (Throwable t) {\n            LOGGER.error(t.getMessage());\n        } finally {\n            if (process != null)\n                process.destroy();\n\n        }\n        return null;\n    }\n    public static String execReturnString(String cmd) {\n        Process process = null;\n        try {\n           Runtime runtime = Runtime.getRuntime();\n          process = runtime.exec(cmd);\n            StreamGobble inputGobble = new StreamGobble(process.getInputStream(), \"INFO\");\n            inputGobble.start();\n            new StreamGobble(process.getErrorStream(), \"ERROR\").start();\n             process.waitFor();\n            return inputGobble.getResult();\n        } catch (Throwable t) {\n            LOGGER.error(t.getMessage());\n        } finally {\n            if (process != null)\n                process.destroy();\n\n        }\n        return null;\n    }\n    public static int exec(String cmd,File dir) {\n        Process process = null;\n        try {\n            Runtime runtime = Runtime.getRuntime();\n            process = runtime.exec(cmd,null,dir);\n            new StreamGobble(process.getInputStream(), \"INFO\").start();\n            new StreamGobble(process.getErrorStream(), \"ERROR\").start();\n            return process.waitFor();\n\n        } catch (Throwable t) {\n            LOGGER.error(t.getMessage());\n        } finally {\n            if (process != null)\n                process.destroy();\n\n        }\n        return 0;\n    }\n\n    public static void main(String[] args) {\n\n\n//        List<String> argss= Arrays.asList(\"mysqldump\", \"-h127.0.0.1\", \"-P3301\", \"-uczn\",\n//                \"-p123\", \"base1\",\"test\", \"--single-transaction\",\"-q\",\"--default-character-set=utf8mb4\",\"--hex-blob\",\"--where=(_slot>=100 and _slot<=1000) or (_slot>=2000 and _slot <=100000)\", \"--master-data=1\",\"-Tc:\\\\999\"\n//        ,\"--fields-enclosed-by=\\\\\\\"\",\"--fields-terminated-by=,\", \"--lines-terminated-by=\\\\n\",  \"--fields-escaped-by=\\\\\\\\\");\n//        String result=  ProcessUtil.execReturnString(argss);\n//        System.out.println(result);\n\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/RandomUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\n/**\n * @author mycat\n */\npublic class RandomUtil {\n    private static final byte[] bytes = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'q', 'w', 'e', 'r', 't',\n            'y', 'u', 'i', 'o', 'p', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n', 'm',\n            'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Z', 'X',\n            'C', 'V', 'B', 'N', 'M' };\n    private static final long multiplier = 0x5DEECE66DL;\n    private static final long addend = 0xBL;\n    private static final long mask = (1L << 48) - 1;\n    private static final long integerMask = (1L << 33) - 1;\n    private static final long seedUniquifier = 8682522807148012L;\n\n    private static long seed;\n    static {\n        long s = seedUniquifier + System.nanoTime();\n        s = (s ^ multiplier) & mask;\n        seed = s;\n    }\n\n    public static final byte[] randomBytes(int size) {\n        byte[] bb = bytes;\n        byte[] ab = new byte[size];\n        for (int i = 0; i < size; i++) {\n            ab[i] = randomByte(bb);\n        }\n        return ab;\n    }\n\n    private static byte randomByte(byte[] b) {\n        int ran = (int) ((next() & integerMask) >>> 16);\n        return b[ran % b.length];\n    }\n\n    private static long next() {\n        long oldSeed = seed;\n        long nextSeed = 0L;\n        do {\n            nextSeed = (oldSeed * multiplier + addend) & mask;\n        } while (oldSeed == nextSeed);\n        seed = nextSeed;\n        return nextSeed;\n    }\n\n    /**\n     * 随机指定范围内N个不重复的数\n     * 最简单最基本的方法\n     * @param min 指定范围最小值（包含）\n     * @param max 指定范围最大值(不包含)\n     * @param n 随机数个数\n     */\n    public static int[] getNRandom(int min, int max, int n){\n        if (n > (max - min + 1) || max < min) {\n            return null;\n        }\n        int[] result = new int[n];\n        for(int i = 0 ; i < n ; i++){\n            result[i] = -9999;\n        }\n        int count = 0;\n        while(count < n) {\n            int num = (int) ((Math.random() * (max - min)) + min);\n            boolean flag = true;\n            for (int j = 0; j < n; j++) {\n                if(num == result[j]){\n                    flag = false;\n                    break;\n                }\n            }\n            if(flag){\n                result[count] = num;\n                count++;\n            }\n        }\n        return result;\n    }\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/ResultSetUtil.java",
    "content": "package io.mycat.util;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport io.mycat.net.mysql.FieldPacket;\nimport io.mycat.net.mysql.RowDataPacket;\n\n/**\n * \n * @author struct\n * \n */\npublic class ResultSetUtil {\n\n\tpublic static int toFlag(ResultSetMetaData metaData, int column)\n\t\t\tthrows SQLException {\n\n\t\tint flags = 0;\n\t\tif (metaData.isNullable(column) == 1) {\n\t\t\tflags |= 1;\n\t\t}\n\n\t\tif (metaData.isSigned(column)) {\n\t\t\tflags |= 16;\n\t\t}\n\n\t\tif (metaData.isAutoIncrement(column)) {\n\t\t\tflags |= 128;\n\t\t}\n\n\t\treturn flags;\n\t}\n\n\tpublic static void resultSetToFieldPacket(String charset,\n\t\t\t\t\t\t\t\t\t\t\t  List<FieldPacket> fieldPks, ResultSet rs,\n\t\t\t\t\t\t\t\t\t\t\t  boolean isSpark) throws SQLException {\n\t\tResultSetMetaData metaData = rs.getMetaData();\n\t\tint colunmCount = metaData.getColumnCount();\n\t\tif (colunmCount > 0) {\n\t\t\t//String values=\"\";\n\t\t\tfor (int i = 0; i < colunmCount; i++) {\n\t\t\t\tint j = i + 1;\n\t\t\t\tFieldPacket fieldPacket = new FieldPacket();\n\t\t\t\tfieldPacket.orgName = StringUtil.encode(metaData.getColumnName(j),charset);\n\t\t\t\tfieldPacket.name = StringUtil.encode(metaData.getColumnLabel(j), charset);\n\t\t\t\tif (! isSpark){\n\t\t\t\t  fieldPacket.orgTable = StringUtil.encode(metaData.getTableName(j), charset);\n\t\t\t\t  fieldPacket.table = StringUtil.encode(metaData.getTableName(j),\tcharset);\n\t\t\t\t  fieldPacket.db = StringUtil.encode(metaData.getSchemaName(j),charset);\n\t\t\t\t  fieldPacket.flags = toFlag(metaData, j);\n\t\t\t\t}\n\t\t\t\tfieldPacket.length = metaData.getColumnDisplaySize(j);\n\t\t\t\t\n\t\t\t\tfieldPacket.decimals = (byte) metaData.getScale(j);\n\t\t\t\tint javaType = MysqlDefs.javaTypeDetect(\n\t\t\t\t\t\tmetaData.getColumnType(j), fieldPacket.decimals);\n\t\t\t\tfieldPacket.type = (byte) (MysqlDefs.javaTypeMysql(javaType) & 0xff);\n\t\t\t\tif(MysqlDefs.isBianry((byte) fieldPacket.type)) {\n\t\t\t\t\t// 63 represent binary character set\n\t\t\t\t\tfieldPacket.charsetIndex = 63;\n\t\t\t\t}\n\t\t\t\tfieldPks.add(fieldPacket);\n\t\t\t\t//values+=metaData.getColumnLabel(j)+\"|\"+metaData.getColumnName(j)+\"  \";\n\t\t\t}\n\t\t\t// System.out.println(values);\n\t\t}\n\n\n\t}\n\n\tpublic static RowDataPacket parseRowData(byte[] row,\n\t\t\tList<byte[]> fieldValues) {\n\t\tRowDataPacket rowDataPkg = new RowDataPacket(fieldValues.size());\n\t\trowDataPkg.read(row);\n\t\treturn rowDataPkg;\n\t}\n\n\tpublic static String getColumnValAsString(byte[] row,\n\t\t\tList<byte[]> fieldValues, int columnIndex) {\n\t\tRowDataPacket rowDataPkg = new RowDataPacket(fieldValues.size());\n\t\trowDataPkg.read(row);\n\t\tbyte[] columnData = rowDataPkg.fieldValues.get(columnIndex);\n\t\t//columnData 为空时,直接返回null\n\t\treturn columnData==null?null:new String(columnData);\n\t}\n\n\tpublic static byte[] getColumnVal(byte[] row, List<byte[]> fieldValues,\n\t\t\tint columnIndex) {\n\t\tRowDataPacket rowDataPkg = new RowDataPacket(fieldValues.size());\n\t\trowDataPkg.read(row);\n\t\tbyte[] columnData = rowDataPkg.fieldValues.get(columnIndex);\n\t\treturn columnData;\n\t}\n\n\tpublic static byte[] fromHex(String hexString) {\n\t\tString[] hex = hexString.split(\" \");\n\t\tbyte[] b = new byte[hex.length];\n\t\tfor (int i = 0; i < hex.length; i++) {\n\t\t\tb[i] = (byte) (Integer.parseInt(hex[i], 16) & 0xff);\n\t\t}\n\n\t\treturn b;\n\t}\n\n\tpublic static void main(String[] args) throws Exception {\n\t\t// byte[] byt =\n\t\t// fromHex(\"20 00 00 02 03 64 65 66 00 00 00 0A 40 40 73 71 6C 5F 6D 6F 64 65 00 0C 21 00 BA 00 00 00 FD 01 00 1F 00 00\");\n\t\t// MysqlPacketBuffer buffer = new MysqlPacketBuffer(byt);\n\t\t// /*\n\t\t// * ResultSetHeaderPacket packet = new ResultSetHeaderPacket();\n\t\t// * packet.init(buffer);\n\t\t// */\n\t\t// FieldPacket[] fields = new FieldPacket[(int) 1];\n\t\t// for (int i = 0; i < 1; i++) {\n\t\t// fields[i] = new FieldPacket();\n\t\t// fields[i].init(buffer);\n\t\t// }\n\t\t// System.out.println(1 | 0200);\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/SelectorUtil.java",
    "content": "package io.mycat.util;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOException;\nimport java.nio.channels.SelectableChannel;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\nimport java.util.ConcurrentModificationException;\n\n/**\n * Selector工具类\n * Created by Hash Zhang on 2017/7/24.\n */\npublic class SelectorUtil {\n    private static final Logger logger = LoggerFactory.getLogger(SelectorUtil.class);\n\n    public static final int REBUILD_COUNT_THRESHOLD = 512;\n\n    public static final long MIN_SELECT_TIME_IN_NANO_SECONDS = 500000L;\n\n    public static Selector rebuildSelector(final Selector oldSelector) throws IOException {\n        final Selector newSelector;\n        try {\n            newSelector = Selector.open();\n        } catch (Exception e) {\n            logger.warn(\"Failed to create a new Selector.\", e);\n            return null;\n        }\n\n        int nChannels = 0;\n        for (;;) {\n            try {\n                for (SelectionKey key: oldSelector.keys()) {\n                    Object a = key.attachment();\n                    try {\n                        if (!key.isValid() || key.channel().keyFor(newSelector) != null) {\n                            continue;\n                        }\n                        int interestOps = key.interestOps();\n                        key.cancel();\n                        key.channel().register(newSelector, interestOps, a);\n                        nChannels ++;\n                    } catch (Exception e) {\n                        logger.warn(\"Failed to re-register a Channel to the new Selector.\", e);\n                    }\n                }\n            } catch (ConcurrentModificationException e) {\n                // Probably due to concurrent modification of the key set.\n                continue;\n            }\n            break;\n        }\n        oldSelector.close();\n        return newSelector;\n    }\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/SetIgnoreUtil.java",
    "content": "package io.mycat.util;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.regex.Matcher;\r\nimport java.util.regex.Pattern;\r\n\r\n/**\r\n * 忽略部分SET 指令\r\n * \r\n * 实际使用中PHP用户经常会操作多个SET指令组成一个Stmt , 所以该指令检测功能独立出来\r\n * \r\n * @author zhuam\r\n *\r\n */\r\npublic class SetIgnoreUtil {\r\n\t\r\n\tprivate static List<Pattern> ptrnIgnoreList = new ArrayList<Pattern>();\r\n\t\r\n\tstatic  {\r\n\t\t\r\n\t\t//TODO: 忽略部分 SET 指令, 避免WARN 不断的刷日志\r\n\t\tString[] ignores = new String[] {\r\n\t\t\t\"(?i)set (sql_mode)\",\r\n\t\t\t\"(?i)set (interactive_timeout|wait_timeout|net_read_timeout|net_write_timeout|lock_wait_timeout|slave_net_timeout)\",\r\n\t\t\t\"(?i)set (connect_timeout|delayed_insert_timeout|innodb_lock_wait_timeout|innodb_rollback_on_timeout)\",\r\n\t\t\t\"(?i)set (profiling|profiling_history_size)\"\r\n\t\t};\r\n\t\t\r\n\t\tfor (int i = 0; i < ignores.length; ++i) {\r\n            ptrnIgnoreList.add(Pattern.compile(ignores[i]));\r\n        }\r\n\t}\r\n\t\r\n\tpublic static boolean isIgnoreStmt(String stmt) {\r\n\t\tboolean ignore = false;\r\n        Matcher matcherIgnore;\r\n        for (Pattern ptrnIgnore : ptrnIgnoreList) {\r\n            matcherIgnore = ptrnIgnore.matcher( stmt );\r\n            if (matcherIgnore.find()) {\r\n                ignore = true;\r\n                break;\r\n            }\r\n        }\t\t\r\n        return ignore;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/util/SmallSet.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.io.Serializable;\nimport java.util.AbstractSet;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.NoSuchElementException;\nimport java.util.Set;\n\n/**\n * usually one element\n * \n * @author mycat\n */\npublic final class SmallSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable {\n\n    private static final long serialVersionUID = 2037649294658559180L;\n\n    private final int initSize;\n    private ArrayList<E> list;\n    private E single;\n    private int size;\n\n    public SmallSet() {\n        this(2);\n    }\n\n    public SmallSet(int initSize) {\n        this.initSize = initSize;\n    }\n\n    @Override\n    public boolean add(E e) {\n        switch (size) {\n        case 0:\n            ++size;\n            single = e;\n            return true;\n        case 1:\n            if (isEquals(e, single)) {\n                return false;\n            }\n            list = new ArrayList<E>(initSize);\n            list.add(single);\n            list.add(e);\n            ++size;\n            return true;\n        default:\n            for (int i = 0; i < list.size(); ++i) {\n                E e1 = list.get(i);\n                if (isEquals(e1, e)) {\n                    return false;\n                }\n            }\n            list.add(e);\n            ++size;\n            return true;\n        }\n    }\n\n    private boolean isEquals(E e1, E e2) {\n        if (e1 == null) {\n            return e2 == null;\n        }\n        return e1.equals(e2);\n    }\n\n    @Override\n    protected Object clone() throws CloneNotSupportedException {\n        return super.clone();\n    }\n\n    @Override\n    public Iterator<E> iterator() {\n        return new Iterator<E>() {\n            private int i;\n            private boolean next;\n\n            @Override\n            public boolean hasNext() {\n                return i < size;\n            }\n\n            @Override\n            public E next() {\n                next = true;\n                switch (size) {\n                case 0:\n                    throw new NoSuchElementException();\n                case 1:\n                    switch (i) {\n                    case 0:\n                        ++i;\n                        return single;\n                    default:\n                        throw new NoSuchElementException();\n                    }\n                default:\n                    try {\n                        E e = list.get(i);\n                        ++i;\n                        return e;\n                    } catch (IndexOutOfBoundsException e) {\n                        throw new NoSuchElementException(e.getMessage());\n                    }\n                }\n            }\n\n            @Override\n            public void remove() {\n                if (!next) {\n                    throw new IllegalStateException();\n                }\n                switch (size) {\n                case 0:\n                    throw new IllegalStateException();\n                case 1:\n                    size = i = 0;\n                    single = null;\n                    if (list != null && !list.isEmpty()) {\n                        list.remove(0);\n                    }\n                    break;\n                default:\n                    list.remove(--i);\n                    if (--size == 1) {\n                        single = list.get(0);\n                    }\n                    break;\n                }\n                next = false;\n            }\n        };\n    }\n\n    @Override\n    public int size() {\n        return size;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/SplitUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * @author mycat\n */\npublic class SplitUtil {\n    private static final String[] EMPTY_STRING_ARRAY = new String[0];\n\n    /**\n     * 解析字符串<br>\n     * 比如:c1='$',c2='-' 输入字符串：mysql_db$0-2<br>\n     * 输出array:mysql_db[0],mysql_db[1],mysql_db[2]\n     */\n    public static String[] split2(String src, char c1, char c2) {\n        if (src == null) {\n            return null;\n        }\n        int length = src.length();\n        if (length == 0) {\n            return EMPTY_STRING_ARRAY;\n        }\n        List<String> list = new LinkedList<String>();\n        String[] p = split(src, c1, true);\n        if (p.length > 1) {\n            String[] scope = split(p[1], c2, true);\n            int min = Integer.parseInt(scope[0]);\n            int max = Integer.parseInt(scope[scope.length - 1]);\n            for (int x = min; x <= max; x++) {\n                list.add(new StringBuilder(p[0]).append('[').append(x).append(']').toString());\n            }\n        } else {\n            list.add(p[0]);\n        }\n        return list.toArray(new String[list.size()]);\n    }\n\n    public static String[] split(String src) {\n        return split(src, null, -1);\n    }\n\n    public static String[] split(String src, char separatorChar) {\n        if (src == null) {\n            return null;\n        }\n        int length = src.length();\n        if (length == 0) {\n            return EMPTY_STRING_ARRAY;\n        }\n        List<String> list = new LinkedList<String>();\n        int i = 0;\n        int start = 0;\n        boolean match = false;\n        while (i < length) {\n            if (src.charAt(i) == separatorChar) {\n                if (match) {\n                    list.add(src.substring(start, i));\n                    match = false;\n                }\n                start = ++i;\n                continue;\n            }\n            match = true;\n            i++;\n        }\n        if (match) {\n            list.add(src.substring(start, i));\n        }\n        return list.toArray(new String[list.size()]);\n    }\n\n    public static String[] split(String src, char separatorChar, boolean trim) {\n        if (src == null) {\n            return null;\n        }\n        int length = src.length();\n        if (length == 0) {\n            return EMPTY_STRING_ARRAY;\n        }\n        List<String> list = new LinkedList<String>();\n        int i = 0;\n        int start = 0;\n        boolean match = false;\n        while (i < length) {\n            if (src.charAt(i) == separatorChar) {\n                if (match) {\n                    if (trim) {\n                        list.add(src.substring(start, i).trim());\n                    } else {\n                        list.add(src.substring(start, i));\n                    }\n                    match = false;\n                }\n                start = ++i;\n                continue;\n            }\n            match = true;\n            i++;\n        }\n        if (match) {\n            if (trim) {\n                list.add(src.substring(start, i).trim());\n            } else {\n                list.add(src.substring(start, i));\n            }\n        }\n        return list.toArray(new String[list.size()]);\n    }\n\n    public static String[] split(String str, String separatorChars) {\n        return split(str, separatorChars, -1);\n    }\n\n    public static String[] split(String src, String separatorChars, int max) {\n        if (src == null) {\n            return null;\n        }\n        int length = src.length();\n        if (length == 0) {\n            return EMPTY_STRING_ARRAY;\n        }\n        List<String> list = new LinkedList<String>();\n        int sizePlus1 = 1;\n        int i = 0;\n        int start = 0;\n        boolean match = false;\n        if (separatorChars == null) {// null表示使用空白作为分隔符\n            while (i < length) {\n                if (Character.isWhitespace(src.charAt(i))) {\n                    if (match) {\n                        if (sizePlus1++ == max) {\n                            i = length;\n                        }\n                        list.add(src.substring(start, i));\n                        match = false;\n                    }\n                    start = ++i;\n                    continue;\n                }\n                match = true;\n                i++;\n            }\n        } else if (separatorChars.length() == 1) {// 优化分隔符长度为1的情形\n            char sep = separatorChars.charAt(0);\n            while (i < length) {\n                if (src.charAt(i) == sep) {\n                    if (match) {\n                        if (sizePlus1++ == max) {\n                            i = length;\n                        }\n                        list.add(src.substring(start, i));\n                        match = false;\n                    }\n                    start = ++i;\n                    continue;\n                }\n                match = true;\n                i++;\n            }\n        } else {// 一般情形\n            while (i < length) {\n                if (separatorChars.indexOf(src.charAt(i)) >= 0) {\n                    if (match) {\n                        if (sizePlus1++ == max) {\n                            i = length;\n                        }\n                        list.add(src.substring(start, i));\n                        match = false;\n                    }\n                    start = ++i;\n                    continue;\n                }\n                match = true;\n                i++;\n            }\n        }\n        if (match) {\n            list.add(src.substring(start, i));\n        }\n        return list.toArray(new String[list.size()]);\n    }\n\n    /**\n     * 解析字符串，比如: <br>\n     * 1. c1='$',c2='-',c3='[',c4=']' 输入字符串：mysql_db$0-2<br>\n     * 输出mysql_db[0],mysql_db[1],mysql_db[2]<br>\n     * 2. c1='$',c2='-',c3='#',c4='0' 输入字符串：mysql_db$0-2<br>\n     * 输出mysql_db#0,mysql_db#1,mysql_db#2<br>\n     * 3. c1='$',c2='-',c3='0',c4='0' 输入字符串：mysql_db$0-2<br>\n     * 输出mysql_db0,mysql_db1,mysql_db2<br>\n     */\n    public static String[] split(String src, char c1, char c2, char c3, char c4) {\n        if (src == null) {\n            return null;\n        }\n        int length = src.length();\n        if (length == 0) {\n            return EMPTY_STRING_ARRAY;\n        }\n        List<String> list = new LinkedList<String>();\n        if (src.indexOf(c1) == -1) {\n            list.add(src.trim());\n        } else {\n            String[] s = split(src, c1, true);\n            String[] scope = split(s[1], c2, true);\n            int min = Integer.parseInt(scope[0]);\n            int max = Integer.parseInt(scope[scope.length - 1]);\n            if (c3 == '0') {\n                for (int x = min; x <= max; x++) {\n                    list.add(new StringBuilder(s[0]).append(x).toString());\n                }\n            } else if (c4 == '0') {\n                for (int x = min; x <= max; x++) {\n                    list.add(new StringBuilder(s[0]).append(c3).append(x).toString());\n                }\n            } else {\n                for (int x = min; x <= max; x++) {\n                    list.add(new StringBuilder(s[0]).append(c3).append(x).append(c4).toString());\n                }\n            }\n        }\n        return list.toArray(new String[list.size()]);\n    }\n\n    public static String[] split(String src, char fi, char se, char th) {\n        return split(src, fi, se, th, '0', '0');\n    }\n\n    public static String[] split(String src, char fi, char se, char th, char left, char right) {\n        List<String> list = new LinkedList<String>();\n        String[] pools = split(src, fi, true);\n        for (int i = 0; i < pools.length; i++) {\n            if (pools[i].indexOf(se) == -1) {\n                list.add(pools[i]);\n                continue;\n            }\n            String[] s = split(pools[i], se, th, left, right);\n            for (int j = 0; j < s.length; j++) {\n                list.add(s[j]);\n            }\n        }\n        return list.toArray(new String[list.size()]);\n    }\n\n    public static String[] splitByByteSize(String string, int size) {\n        if (size < 2)\n        {\n         return    new String[]{string};\n        }\n        byte[] bytes = string.getBytes();\n        if (bytes.length <= size) {\n            return new String[]{string};\n        }\n        // 分成的条数不确定(整除的情况下也许会多出一条),所以先用list再转化为array\n        List list = new ArrayList();\n        int offset = 0;// 偏移量,也就是截取的字符串的首字节的位置\n        int length = 0;// 截取的字符串的长度,可能是size,可能是size-1\n        int position = 0;// 可能的截取点,根据具体情况判断是不是在此截取\n        while (position < bytes.length) {\n            position = offset + size;\n            if (position > bytes.length) {\n                // 最后一条\n                String s = new String(bytes, offset, bytes.length - offset);\n                list.add(s);\n                break;\n            }\n            if (bytes[position - 1] > 0\n                    || (bytes[position - 1] < 0 && bytes[position - 2] < 0)){\n                // 截断点是字母,或者是汉字\n                length = size;\n            } else {\n                // 截断点在汉字中间\n                length = size - 1;\n            }\n            String s = new String(bytes, offset, length);\n            list.add(s);\n            offset += length;\n        }\n        String[] array = new String[list.size()];\n        for (int i = 0; i < array.length; i++) {\n            array[i] = (String) list.get(i);\n        }\n        return array;\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/StreamGobble.java",
    "content": "package io.mycat.util;\n\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\n\npublic class StreamGobble extends Thread {\n    InputStream is;\n    String type;\n   private StringBuffer result=new StringBuffer();\n\n    public String getResult() {\n        return result.toString();\n    }\n\n    private static Logger LOG = LoggerFactory.getLogger((StreamGobble.class));\n\n    StreamGobble(InputStream is, String type) {\n        this.is = is;\n        this.type = type;\n    }\n\n    public void run() {\n        try {\n            InputStreamReader isr = new InputStreamReader(is);\n            BufferedReader br = new BufferedReader(isr);\n            String line = null;\n            while ((line = br.readLine()) != null) {\n                result.append(line).append(\"\\n\");\n                LOG.info(line);\n            }\n        } catch (IOException ioe) {\n            LOG.error(ioe.getMessage());\n        }\n    }\n}\n\n\n"
  },
  {
    "path": "src/main/java/io/mycat/util/StringUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport io.mycat.MycatServer;\nimport io.mycat.sqlengine.mpp.LoadData;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Random;\n\n/**\n * @author mycat\n */\npublic class StringUtil {\n\tpublic static final String TABLE_COLUMN_SEPARATOR = \".\";\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(StringUtil.class);\n\tprivate static final byte[] EMPTY_BYTE_ARRAY = new byte[0];\n\tprivate static final Random RANDOM = new Random();\n\tprivate static final char[] CHARS = { '1', '2', '3', '4', '5', '6', '7',\n\t\t\t'8', '9', '0', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',\n\t\t\t'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v',\n\t\t\t'b', 'n', 'm', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P',\n\t\t\t'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Z', 'X', 'C', 'V',\n\t\t\t'B', 'N', 'M' };\n\n\t/**\n\t * 字符串hash算法：s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] <br>\n\t * 其中s[]为字符串的字符数组，换算成程序的表达式为：<br>\n\t * h = 31*h + s.charAt(i); => h = (h << 5) - h + s.charAt(i); <br>\n\t *\n\t * @param start\n\t *            hash for s.substring(start, end)\n\t * @param end\n\t *            hash for s.substring(start, end)\n\t */\n\tpublic static long hash(String s, int start, int end) {\n\t\tif (start < 0) {\n\t\t\tstart = 0;\n\t\t}\n\t\tif (end > s.length()) {\n\t\t\tend = s.length();\n\t\t}\n\t\tlong h = 0;\n\t\tfor (int i = start; i < end; ++i) {\n\t\t\th = (h << 5) - h + s.charAt(i);\n\t\t}\n\t\treturn h;\n\t}\n\n\tpublic static byte[] encode(String src, String charset) {\n\t\tif (src == null) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\treturn src.getBytes(charset);\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\treturn src.getBytes();\n\t\t}\n\t}\n\n\tpublic static String decode(byte[] src, String charset) {\n\t\treturn decode(src, 0, src.length, charset);\n\t}\n\n\tpublic static String decode(byte[] src, int offset, int length,\n\t\t\tString charset) {\n\t\ttry {\n\t\t\treturn new String(src, offset, length, charset);\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\treturn new String(src, offset, length);\n\t\t}\n\t}\n\n\tpublic static String getRandomString(int size) {\n\t\tStringBuilder s = new StringBuilder(size);\n\t\tint len = CHARS.length;\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tint x = RANDOM.nextInt();\n\t\t\ts.append(CHARS[(x < 0 ? -x : x) % len]);\n\t\t}\n\t\treturn s.toString();\n\t}\n\n\tpublic static String safeToString(Object object) {\n\t\ttry {\n\t\t\treturn object.toString();\n\t\t} catch (Exception t) {\n\t\t    LOGGER.error(\"safeToStringError\", t);\n\t\t\treturn \"<toString() failure: \" + t + \">\";\n\t\t}\n\t}\n\n\tpublic static boolean isEmpty(String str) {\n\t\treturn ((str == null) || (str.length() == 0));\n\t}\n\n\tpublic static byte[] hexString2Bytes(char[] hexString, int offset,\n\t\t\tint length) {\n\t\tif (hexString == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (length == 0) {\n\t\t\treturn EMPTY_BYTE_ARRAY;\n\t\t}\n\t\tboolean odd = length << 31 == Integer.MIN_VALUE;\n\t\tbyte[] bs = new byte[odd ? (length + 1) >> 1 : length >> 1];\n\t\tfor (int i = offset, limit = offset + length; i < limit; ++i) {\n\t\t\tchar high, low;\n\t\t\tif (i == offset && odd) {\n\t\t\t\thigh = '0';\n\t\t\t\tlow = hexString[i];\n\t\t\t} else {\n\t\t\t\thigh = hexString[i];\n\t\t\t\tlow = hexString[++i];\n\t\t\t}\n\t\t\tint b;\n\t\t\tswitch (high) {\n\t\t\tcase '0':\n\t\t\t\tb = 0;\n\t\t\t\tbreak;\n\t\t\tcase '1':\n\t\t\t\tb = 0x10;\n\t\t\t\tbreak;\n\t\t\tcase '2':\n\t\t\t\tb = 0x20;\n\t\t\t\tbreak;\n\t\t\tcase '3':\n\t\t\t\tb = 0x30;\n\t\t\t\tbreak;\n\t\t\tcase '4':\n\t\t\t\tb = 0x40;\n\t\t\t\tbreak;\n\t\t\tcase '5':\n\t\t\t\tb = 0x50;\n\t\t\t\tbreak;\n\t\t\tcase '6':\n\t\t\t\tb = 0x60;\n\t\t\t\tbreak;\n\t\t\tcase '7':\n\t\t\t\tb = 0x70;\n\t\t\t\tbreak;\n\t\t\tcase '8':\n\t\t\t\tb = 0x80;\n\t\t\t\tbreak;\n\t\t\tcase '9':\n\t\t\t\tb = 0x90;\n\t\t\t\tbreak;\n\t\t\tcase 'a':\n\t\t\tcase 'A':\n\t\t\t\tb = 0xa0;\n\t\t\t\tbreak;\n\t\t\tcase 'b':\n\t\t\tcase 'B':\n\t\t\t\tb = 0xb0;\n\t\t\t\tbreak;\n\t\t\tcase 'c':\n\t\t\tcase 'C':\n\t\t\t\tb = 0xc0;\n\t\t\t\tbreak;\n\t\t\tcase 'd':\n\t\t\tcase 'D':\n\t\t\t\tb = 0xd0;\n\t\t\t\tbreak;\n\t\t\tcase 'e':\n\t\t\tcase 'E':\n\t\t\t\tb = 0xe0;\n\t\t\t\tbreak;\n\t\t\tcase 'f':\n\t\t\tcase 'F':\n\t\t\t\tb = 0xf0;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"illegal hex-string: \"\n\t\t\t\t\t\t+ new String(hexString, offset, length));\n\t\t\t}\n\t\t\tswitch (low) {\n\t\t\tcase '0':\n\t\t\t\tbreak;\n\t\t\tcase '1':\n\t\t\t\tb += 1;\n\t\t\t\tbreak;\n\t\t\tcase '2':\n\t\t\t\tb += 2;\n\t\t\t\tbreak;\n\t\t\tcase '3':\n\t\t\t\tb += 3;\n\t\t\t\tbreak;\n\t\t\tcase '4':\n\t\t\t\tb += 4;\n\t\t\t\tbreak;\n\t\t\tcase '5':\n\t\t\t\tb += 5;\n\t\t\t\tbreak;\n\t\t\tcase '6':\n\t\t\t\tb += 6;\n\t\t\t\tbreak;\n\t\t\tcase '7':\n\t\t\t\tb += 7;\n\t\t\t\tbreak;\n\t\t\tcase '8':\n\t\t\t\tb += 8;\n\t\t\t\tbreak;\n\t\t\tcase '9':\n\t\t\t\tb += 9;\n\t\t\t\tbreak;\n\t\t\tcase 'a':\n\t\t\tcase 'A':\n\t\t\t\tb += 10;\n\t\t\t\tbreak;\n\t\t\tcase 'b':\n\t\t\tcase 'B':\n\t\t\t\tb += 11;\n\t\t\t\tbreak;\n\t\t\tcase 'c':\n\t\t\tcase 'C':\n\t\t\t\tb += 12;\n\t\t\t\tbreak;\n\t\t\tcase 'd':\n\t\t\tcase 'D':\n\t\t\t\tb += 13;\n\t\t\t\tbreak;\n\t\t\tcase 'e':\n\t\t\tcase 'E':\n\t\t\t\tb += 14;\n\t\t\t\tbreak;\n\t\t\tcase 'f':\n\t\t\tcase 'F':\n\t\t\t\tb += 15;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"illegal hex-string: \"\n\t\t\t\t\t\t+ new String(hexString, offset, length));\n\t\t\t}\n\t\t\tbs[(i - offset) >> 1] = (byte) b;\n\t\t}\n\t\treturn bs;\n\t}\n\n\tpublic static String dumpAsHex(byte[] src, int length) {\n\t\tStringBuilder out = new StringBuilder(length * 4);\n\t\tint p = 0;\n\t\tint rows = length / 8;\n\t\tfor (int i = 0; (i < rows) && (p < length); i++) {\n\t\t\tint ptemp = p;\n\t\t\tfor (int j = 0; j < 8; j++) {\n\t\t\t\tString hexVal = Integer.toHexString(src[ptemp] & 0xff);\n\t\t\t\tif (hexVal.length() == 1) {\n\t\t\t\t\tout.append('0');\n\t\t\t\t}\n\t\t\t\tout.append(hexVal).append(' ');\n\t\t\t\tptemp++;\n\t\t\t}\n\t\t\tout.append(\"    \");\n\t\t\tfor (int j = 0; j < 8; j++) {\n\t\t\t\tint b = 0xff & src[p];\n\t\t\t\tif (b > 32 && b < 127) {\n\t\t\t\t\tout.append((char) b).append(' ');\n\t\t\t\t} else {\n\t\t\t\t\tout.append(\". \");\n\t\t\t\t}\n\t\t\t\tp++;\n\t\t\t}\n\t\t\tout.append('\\n');\n\t\t}\n\t\tint n = 0;\n\t\tfor (int i = p; i < length; i++) {\n\t\t\tString hexVal = Integer.toHexString(src[i] & 0xff);\n\t\t\tif (hexVal.length() == 1) {\n\t\t\t\tout.append('0');\n\t\t\t}\n\t\t\tout.append(hexVal).append(' ');\n\t\t\tn++;\n\t\t}\n\t\tfor (int i = n; i < 8; i++) {\n\t\t\tout.append(\"   \");\n\t\t}\n\t\tout.append(\"    \");\n\t\tfor (int i = p; i < length; i++) {\n\t\t\tint b = 0xff & src[i];\n\t\t\tif (b > 32 && b < 127) {\n\t\t\t\tout.append((char) b).append(' ');\n\t\t\t} else {\n\t\t\t\tout.append(\". \");\n\t\t\t}\n\t\t}\n\t\tout.append('\\n');\n\t\treturn out.toString();\n\t}\n\n\tpublic static byte[] escapeEasternUnicodeByteStream(byte[] src,\n\t\t\tString srcString, int offset, int length) {\n\t\tif ((src == null) || (src.length == 0)) {\n\t\t\treturn src;\n\t\t}\n\t\tint bytesLen = src.length;\n\t\tint bufIndex = 0;\n\t\tint strIndex = 0;\n\t\tByteArrayOutputStream out = new ByteArrayOutputStream(bytesLen);\n\t\twhile (true) {\n\t\t\tif (srcString.charAt(strIndex) == '\\\\') {// write it out as-is\n\t\t\t\tout.write(src[bufIndex++]);\n\t\t\t} else {// Grab the first byte\n\t\t\t\tint loByte = src[bufIndex];\n\t\t\t\tif (loByte < 0) {\n\t\t\t\t\tloByte += 256; // adjust for signedness/wrap-around\n\t\t\t\t}\n\t\t\t\tout.write(loByte);// We always write the first byte\n\t\t\t\tif (loByte >= 0x80) {\n\t\t\t\t\tif (bufIndex < (bytesLen - 1)) {\n\t\t\t\t\t\tint hiByte = src[bufIndex + 1];\n\t\t\t\t\t\tif (hiByte < 0) {\n\t\t\t\t\t\t\thiByte += 256; // adjust for signedness/wrap-around\n\t\t\t\t\t\t}\n\t\t\t\t\t\tout.write(hiByte);// write the high byte here, and\n\t\t\t\t\t\t\t\t\t\t\t// increment the index for the high\n\t\t\t\t\t\t\t\t\t\t\t// byte\n\t\t\t\t\t\tbufIndex++;\n\t\t\t\t\t\tif (hiByte == 0x5C) {\n\t\t\t\t\t\t\tout.write(hiByte);// escape 0x5c if necessary\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (loByte == 0x5c\n\t\t\t\t\t\t&& bufIndex < (bytesLen - 1)) {\n\t\t\t\t\t\tint hiByte = src[bufIndex + 1];\n\t\t\t\t\t\tif (hiByte < 0) {\n\t\t\t\t\t\t\thiByte += 256; // adjust for signedness/wrap-around\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (hiByte == 0x62) {// we need to escape the 0x5c\n\t\t\t\t\t\t\tout.write(0x5c);\n\t\t\t\t\t\t\tout.write(0x62);\n\t\t\t\t\t\t\tbufIndex++;\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbufIndex++;\n\t\t\t}\n\t\t\tif (bufIndex >= bytesLen) {\n\t\t\t\tbreak;// we're done\n\t\t\t}\n\t\t\tstrIndex++;\n\t\t}\n\t\treturn out.toByteArray();\n\t}\n\n\tpublic static String toString(byte[] bytes) {\n\t\tif (bytes == null || bytes.length == 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\tStringBuffer buffer = new StringBuffer();\n\t\tfor (byte byt : bytes) {\n\t\t\tbuffer.append((char) byt);\n\t\t}\n\t\treturn buffer.toString();\n\t}\n\n\tpublic static boolean equalsIgnoreCase(String str1, String str2) {\n\t\tif (str1 == null) {\n\t\t\treturn str2 == null;\n\t\t}\n\t\treturn str1.equalsIgnoreCase(str2);\n\t}\n\n\tpublic static int countChar(String str, char c) {\n\t\tif (str == null || str.isEmpty()) {\n\t\t\treturn 0;\n\t\t}\n\t\tfinal int len = str.length();\n\t\tint cnt = 0;\n\t\tfor (int i = 0; i < len; ++i) {\n\t\t\tif (c == str.charAt(i)) {\n\t\t\t\t++cnt;\n\t\t\t}\n\t\t}\n\t\treturn cnt;\n\t}\n\n\tpublic static String replaceOnce(String text, String repl, String with) {\n\t\treturn replace(text, repl, with, 1);\n\t}\n\n\tpublic static String replace(String text, String repl, String with) {\n\t\treturn replace(text, repl, with, -1);\n\t}\n\n\tpublic static String replace(String text, String repl, String with, int max) {\n\t\tif ((text == null) || (repl == null) || (with == null)\n\t\t\t\t|| (repl.length() == 0) || (max == 0)) {\n\t\t\treturn text;\n\t\t}\n\t\tStringBuffer buf = new StringBuffer(text.length());\n\t\tint start = 0;\n\t\tint end = 0;\n\t\twhile ((end = text.indexOf(repl, start)) != -1) {\n\t\t\tbuf.append(text.substring(start, end)).append(with);\n\t\t\tstart = end + repl.length();\n\t\t\tif (--max == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tbuf.append(text.substring(start));\n\t\treturn buf.toString();\n\t}\n\n\tpublic static String replaceChars(String str, String searchChars,\n\t\t\t\t\t\t\t\t\t  String replaceChars) {\n\t\treturn replaceChars(str,searchChars,replaceChars,false);\n\t}\n\tpublic static String replaceChars(String str, String searchChars,\n\t\t\tString replaceChars,boolean force) {\n\t\tif(!force){\n\t\t\treturn str;\n\t\t}\n\t\tif ((str == null) || (str.length() == 0) || (searchChars == null)\n\t\t\t\t|| (searchChars.length() == 0)) {\n\t\t\treturn str;\n\t\t}\n\t\tchar[] chars = str.toCharArray();\n\t\tint len = chars.length;\n\t\tboolean modified = false;\n\t\tfor (int i = 0, isize = searchChars.length(); i < isize; i++) {\n\t\t\tchar searchChar = searchChars.charAt(i);\n\t\t\tif ((replaceChars == null) || (i >= replaceChars.length())) {// 删除\n\t\t\t\tint pos = 0;\n\t\t\t\tfor (int j = 0; j < len; j++) {\n\t\t\t\t\tif (chars[j] != searchChar) {\n\t\t\t\t\t\tchars[pos++] = chars[j];\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmodified = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlen = pos;\n\t\t\t} else {// 替换\n\t\t\t\tfor (int j = 0; j < len; j++) {\n\t\t\t\t\tif (chars[j] == searchChar) {\n\t\t\t\t\t\tchars[j] = replaceChars.charAt(i);\n\t\t\t\t\t\tmodified = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!modified) {\n\t\t\treturn str;\n\t\t}\n\t\treturn new String(chars, 0, len);\n\t}\n\n\t/**\n\t * insert into tablexxx\n\t *\n\t * @param oriSql\n\t * @return\n\t */\n\tpublic static String getTableName(String oriSql) {\n        //此处应该优化为去掉sql中的注释，或兼容注释\n        String sql=null;\n        if(oriSql.startsWith(LoadData.loadDataHint))\n        {\n           sql=oriSql.substring(LoadData.loadDataHint.length()) ;\n        } else\n        {\n            sql=oriSql;\n        }\n\t\tint pos = 0;\n\t\tboolean insertFound = false;\n\t\tboolean intoFound = false;\n\t\tint tableStartIndx = -1;\n\t\tint tableEndIndex = -1;\n\t\twhile (pos < sql.length()) {\n\t\t\tchar ch = sql.charAt(pos);\n\t\t\t// 忽略处理注释 /* */ BEN\n\t\t\tif(ch == '/' &&  pos+4 < sql.length() && sql.charAt(pos+1) == '*') {\n\t\t\t\tif(sql.substring(pos+2).indexOf(\"*/\") != -1) {\n\t\t\t\t\tpos += sql.substring(pos+2).indexOf(\"*/\")+4;\n\t\t\t\t\tcontinue;\n\t\t\t\t} else {\n\t\t\t\t\t// 不应该发生这类情况。\n\t\t\t\t\tthrow new IllegalArgumentException(\"sql 注释 语法错误\");\n\t\t\t\t}\n\t\t\t} else if (ch <= ' ' || ch == '(' || ch=='`') {//\n\t\t\t\tif (tableStartIndx > 0) {\n\t\t\t\t\ttableEndIndex = pos;\n\t\t\t\t\tbreak;\n\t\t\t\t} else {\n\t\t\t\t\tpos++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} else if (ch == 'i' || ch == 'I') {\n\t\t\t\tif (intoFound) {\n\t\t\t\t\tif (tableStartIndx == -1 && ch!='`') {\n\t\t\t\t\t\ttableStartIndx = pos;\n\t\t\t\t\t}\n\t\t\t\t\tpos++;\n\t\t\t\t} else if (insertFound) {// into start\n\t\t\t\t\t// 必须全部都为INTO才认为是into\n\t\t\t\t\tif(pos+5 < sql.length() && (sql.charAt(pos+1) == 'n' || sql.charAt(pos+1) == 'N') && (sql.charAt(pos+2) == 't' || sql.charAt(pos+2) == 'T') && (sql.charAt(pos+3) == 'o' || sql.charAt(pos+3) == 'O') && (sql.charAt(pos+4) <= ' ')) {\n\t\t\t\t\t\tpos = pos + 5;\n\t\t\t\t\t\tintoFound = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpos++;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// 矫正必须全部都为 INSERT才认为是insert\n\t\t\t\t\t// insert start\n\t\t\t\t\tif(pos+7 < sql.length() && (sql.charAt(pos+1) == 'n' || sql.charAt(pos+1) == 'N') && (sql.charAt(pos+2) == 's' || sql.charAt(pos+2) == 'S')  && (sql.charAt(pos+3) == 'e' || sql.charAt(pos+3) == 'E') && (sql.charAt(pos+4) == 'r' || sql.charAt(pos+4) == 'R')  && (sql.charAt(pos+5) == 't' || sql.charAt(pos+5) == 'T') && (sql.charAt(pos+6) <= ' ')) {\n\t\t\t\t\t\tpos = pos + 7;\n\t\t\t\t\t\tinsertFound = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpos++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (tableStartIndx == -1) {\n\t\t\t\t\ttableStartIndx = pos;\n\t\t\t\t}\n\t\t\t\tpos++;\n\t\t\t}\n\n\t\t}\n\t\treturn sql.substring(tableStartIndx, tableEndIndex);\n\t}\n\n\t/**\n\t * 移除`符号\n\t * @param str\n\t * @return\n\t */\n\tpublic static String removeBackquote(String str) {\n\t\tif (str == null){\n\t\t\treturn str;\n\t\t}\n\t\tstr = str.trim();\n\t\t//删除名字中的`tablename`和'value'\n\t\tif (str.length() >= 2) {\n\t\t\tchar firstChar = str.charAt(0);\n\t\t\tint lastIndex = str.length() - 1;\n\t\t\tchar tailChar = str.charAt(lastIndex);\n\t\t\tif ((firstChar == '`' && tailChar == '`') || (firstChar == '\\'' && tailChar == '\\'')) {\n\t\t\t\treturn str.substring(1, lastIndex);\n\t\t\t}\n\t\t}\n\t\treturn str;\n\t}\n\n\tpublic static String makeString(Object... args) {\n\t\tStringBuilder stringBuilder = new StringBuilder();\n\t\tfor (Object arg : args) {\n\t\t\tstringBuilder.append(arg);\n\t\t}\n\t\treturn stringBuilder.toString();\n\t}\n\n\tpublic static boolean isNull(String src) {\n\t\tif (src == null || src.trim().equals(\"\") || src.trim().equalsIgnoreCase(\"undefined\")) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static String sha1(String data) throws NoSuchAlgorithmException {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA1\");\n\t\tmd.update(data.getBytes());\n\t\tStringBuffer buf = new StringBuffer();\n\t\tbyte[] bits = md.digest();\n\t\tfor (int i = 0; i < bits.length; i++) {\n\t\t\tint a = bits[i];\n\t\t\tif (a < 0) a += 256;\n\t\t\tif (a < 16) buf.append(\"0\");\n\t\t\tbuf.append(Integer.toHexString(a));\n\t\t}\n\t\treturn buf.toString();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tString s;\n\t\ts = removeBackquote(\"`\");\n\t\tassert (\"`\".equals(s));\n\t\ts = removeBackquote(\"```\");\n\t\tassert (\"`\".equals(s));\n\t\ts = removeBackquote(\"'`'\");\n\t\tassert (\"`\".equals(s));\n\t\ts = removeBackquote(\"``\");\n\t\tassert (\"\".equals(s));\n\t\ts = removeBackquote(\"`qqqqq\");\n\t\tassert (\"`qqqqq\".equals(s));\n\t\ts = removeBackquote(\"'qqqqq\");\n\t\tassert (\"'qqqqq\".equals(s));\n\t\ts = removeBackquote(\"qqqqq`\");\n\t\tassert (\"qqqqq`\".equals(s));\n\t\ts = removeBackquote(\"qqqqq\\'\");\n\t\tassert (\"qqqqq\\'\".equals(s));\n\t\ts = removeBackquote(\"\\'qqqqq\\'\");\n\t\tassert (\"qqqqq\".equals(s));\n\t\ts = removeBackquote(\"`qqqqq'\");\n\t\tassert (\"`qqqqq'\".equals(s));\n\t\ts = removeBackquote(\"'qqqqq`\");\n\t\tassert (\"'qqqqq`\".equals(s));\n\t\tSystem.out.println(getTableName(\"insert into ssd  (id) values (s)\"));\n\t\tSystem.out.println(getTableName(\"insert into    ssd(id) values (s)\"));\n\t\tSystem.out.println(getTableName(\"  insert  into    ssd(id) values (s)\"));\n\t\tSystem.out.println(getTableName(\"  insert  into    isd(id) values (s)\"));\n\t\tSystem.out.println(getTableName(\"INSERT INTO test_activity_input  (id,vip_no\"));\n\t\tSystem.out.println(getTableName(\"/* ApplicationName=DBeaver 3.3.1 - Main connection */ insert into employee(id,name,sharding_id) values(4,’myhome’,10011)\"));\n        System.out.println(countChar(\"insert into ssd  (id) values (s) ,(s),(7);\",'('));\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/TimeUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\n/**\n * 弱精度的计时器，考虑性能不使用同步策略。\n * \n * @author mycat\n */\npublic class TimeUtil {\n    private static volatile long CURRENT_TIME = System.currentTimeMillis();\n\n    public static final long currentTimeMillis() {\n        return CURRENT_TIME;\n    }\n    public static final long currentTimeNanos() {\n        return System.nanoTime();\n    }\n\n    public static final void update() {\n        CURRENT_TIME = System.currentTimeMillis();\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/ZKUtils.java",
    "content": "package io.mycat.util;\n\nimport io.mycat.MycatServer;\nimport io.mycat.config.loader.zkprocess.comm.ZkConfig;\nimport io.mycat.config.loader.zkprocess.comm.ZkParamCfg;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.CuratorFrameworkFactory;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCache;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;\nimport org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;\nimport org.apache.curator.framework.recipes.locks.InterProcessMutex;\nimport org.apache.curator.framework.state.ConnectionState;\nimport org.apache.curator.framework.state.ConnectionStateListener;\nimport org.apache.curator.retry.ExponentialBackoffRetry;\nimport org.apache.curator.retry.RetryForever;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.io.Files;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.concurrent.*;\n\npublic class ZKUtils {\n    private static final Logger LOGGER = LoggerFactory.getLogger(ZKUtils.class);\n    static CuratorFramework curatorFramework = null;\n    static ConcurrentMap<String, PathChildrenCache> watchMap = new ConcurrentHashMap<>();\n\n    static {\n        curatorFramework = createConnection();\n        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {\n            @Override\n            public void run() {\n                if (curatorFramework != null)\n                    curatorFramework.close();\n                watchMap.clear();\n            }\n        }));\n    }\n\n    public static String getZKBasePath() {\n        String clasterID = ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_CLUSTERID);\n\n        return \"/mycat/\" + clasterID + \"/\";\n    }\n\n    public static CuratorFramework getConnection() {\n        return curatorFramework;\n    }\n\n    private static CuratorFramework createConnection() {\n        String url = ZkConfig.getInstance().getZkURL();\n\n        CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(url, new ExponentialBackoffRetry(100, 6));\n\n        // start connection\n        curatorFramework.start();\n        // wait 3 second to establish connect\n        try {\n            curatorFramework.blockUntilConnected(3, TimeUnit.SECONDS);\n            if (curatorFramework.getZookeeperClient().isConnected()) {\n                return curatorFramework;\n            }\n        } catch (InterruptedException ignored) {\n            Thread.currentThread().interrupt();\n        }\n\n        // fail situation\n        curatorFramework.close();\n        throw new RuntimeException(\"failed to connect to zookeeper service : \" + url);\n    }\n\n    public static void closeWatch(List<String> watchs) {\n        for (String watch : watchs) {\n            closeWatch(watch);\n        }\n    }\n\n    public static void closeWatch(String path) {\n        PathChildrenCache childrenCache = watchMap.get(path);\n        if (childrenCache != null) {\n            try {\n                childrenCache.close();\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n        }\n    }\n\n    public static void addChildPathCache(String path, PathChildrenCacheListener listener) {\n        NameableExecutor businessExecutor = MycatServer.getInstance().getBusinessExecutor();\n        ExecutorService executor = businessExecutor == null ? Executors.newFixedThreadPool(5) : businessExecutor;\n\n        try {\n            /**\n             * 监听子节点的变化情况\n             */\n            final PathChildrenCache childrenCache = new PathChildrenCache(getConnection(), path, true);\n            childrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);\n            childrenCache.getListenable().addListener(listener, executor);\n            watchMap.put(path, childrenCache);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n    \n\n  //写数据到某个路径底下\n  \tpublic static boolean writeProperty( String path, Map<String, String> propertyMap) throws Exception {\n  \t\t// save to  zk\n  \t\t//try {\n  \t\t\tCuratorFramework client = ZKUtils.getConnection();\n  \t\t\t//lock.acquire(30,TimeUnit.SECONDS)   ;\t\t\t\n  \t\t\tProperties properties=new Properties();\n  \t\t\tByteArrayOutputStream out=new ByteArrayOutputStream();\n  \t\n  \t\t\tif(client.checkExists().forPath(path)==null) {\n  \t\t\t\tfor(String key : propertyMap.keySet()){\n  \t\t\t\t\tproperties.setProperty(key, propertyMap.get(key));\n  \t\t\t\t}\n  \t\t\t\tproperties.store(out, \"add\");\t\t\t\t\n  \t\t\t\tclient.create().creatingParentsIfNeeded().forPath(path,out.toByteArray());\n  \t\t\t\treturn true;\n  \t\t\t} else{\n  \t\t\t\tbyte[] data = client.getData().forPath(path);\n  \t\t\t\tproperties.load(new ByteArrayInputStream(data));\n  \t\t\t\tboolean isUpdate = false;\n  \t\t\t\tfor(String key : propertyMap.keySet()){\n  \t\t\t\t\tString value = propertyMap.get(key);\n  \t\t\t\t\tif(!String.valueOf(value).equals(properties.getProperty(key))) {\n  \t\t\t\t\t\t properties.setProperty(key, String.valueOf(value));\n  \t\t\t\t\t\t isUpdate =  true;\n  \t\t\t\t\t }\n  \t\t\t\t}\n  \t\t\t\t properties.store(out, \"update\");\n  \t\n  \t\t\t\t//数据有进行更新\n  \t\t\t\tif(isUpdate){\n  \t\t\t\t\t client.setData().forPath(path, out.toByteArray());\n  \t\t\t\t\t return true;\n  \t\t\t\t}\n  \t\t\t\treturn false;\n  \t\t\t\t \n  \t\t\t}\n  \t\n  \t\t//}finally {\n  \t\t//\tlock.release();\n  \t\t//}\n  \t}\n\tpublic static void createPath(String path, String data) {\n\t\t//这边应该将结果写入到 dnindex.properties\n\t\tCuratorFramework client = ZKUtils.getConnection();\n\n\t\ttry {\n\t\t\tif(client.checkExists().forPath(path) == null) {\n\t\t\t\tclient.create().creatingParentsIfNeeded().forPath(path, data.getBytes());\n\t\t\t} else {\n\t\t\t\tclient.setData().forPath(path, data.getBytes());\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tSystem.out.println(\"放置数据失败\");\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n     public static String getDnIndexPath(){    \t\n    \t return ZKUtils.getZKBasePath() + \"bindata/dnindex.properties\";\n     } \n\n\n    \n}"
  },
  {
    "path": "src/main/java/io/mycat/util/cmd/CmdArgs.java",
    "content": "package io.mycat.util.cmd;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * -host=192.168.1.1:8080\n * -\n * @author me\n *\n */\npublic class CmdArgs {\n\tprivate static final CmdArgs cmdArgs=new CmdArgs();\n\t\n\tprivate Map<String,String> args;\n\t\n\tprivate CmdArgs(){\n\t\targs=new HashMap<>();\n\t}\n\t\n\t\n\tpublic static CmdArgs getInstance(String[] args){\n\t\tMap<String,String> cmdArgs=CmdArgs.cmdArgs.args;\n\t\tfor(int i=0,l=args.length;i<l;i++){\n\t\t\tString arg=args[i].trim();\n\t\t\tint split=arg.indexOf('=');\n\t\t\tcmdArgs.put(arg.substring(1,split), arg.substring(split+1));\n\t\t}\n\t\treturn CmdArgs.cmdArgs;\n\t}\n\t\n\tpublic String getString(String name){\n\t\treturn args.get(name);\n\t}\n\tpublic int getInt(String name){\n\t\treturn Integer.parseInt(getString(name));\n\t}\n\tpublic long getLong(String name){\n\t\treturn Long.parseLong(getString(name));\n\t}\n\tpublic boolean getBoolean(String name){\n\t\treturn Boolean.parseBoolean(getString(name));\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/ConfigComparer.java",
    "content": "package io.mycat.util.dataMigrator;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Properties;\nimport java.util.Set;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.DBHostConfig;\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.config.model.DataNodeConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.config.model.rule.RuleConfig;\nimport io.mycat.config.util.ConfigException;\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\n\n/**\n * 数据迁移新旧配置文件加载、对比\n * @author haonan108\n *\n */\npublic class ConfigComparer {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(ConfigComparer.class);\n\t/*\n\t *指定需要进行数据迁移的表及对应schema\n\t * 配置文件格式\n\t * schema1=tb1,tb2,...\n\t * schema2=all\n\t * ...\n\t */\n\tprivate final static String TABLES_FILE = \"/migrateTables.properties\";  \n    private final static String NEW_SCHEMA = \"/newSchema.xml\";\n\tprivate final static String NEW_RULE = \"/newRule.xml\";\n\tprivate final static String DN_INDEX_FILE = \"/dnindex.properties\";\n\t\n\tprivate SchemaLoader oldLoader;\n\tprivate SchemaLoader newLoader;\n\t\n\tprivate  Map<String, DataHostConfig>  oldDataHosts;\n\tprivate  Map<String, DataNodeConfig>  oldDataNodes;\n\tprivate  Map<String, SchemaConfig>  oldSchemas; \n\t\n\tprivate  Map<String, DataHostConfig> newDataHosts;\n\tprivate  Map<String, DataNodeConfig> newDataNodes;\n\tprivate  Map<String, SchemaConfig> newSchemas;\n\t\n\t//即使发生主备切换也使用主数据源\n\tprivate boolean isAwaysUseMaster;\n\tprivate Properties dnIndexProps;\n\t\n\t//此类主要目的是通过加载新旧配置文件来获取表迁移信息，migratorTables就是最终要获取的迁移信息集合\n\tprivate List<TableMigrateInfo> migratorTables = new ArrayList<TableMigrateInfo>();\n\t\n\tpublic ConfigComparer(boolean isAwaysUseMaster) throws Exception{\n\t\tthis.isAwaysUseMaster = isAwaysUseMaster;\n\t\tloadOldConfig();\n\t\tloadNewConfig();\n\t\tloadTablesFile();\n\t}\n\t\n\tpublic List<TableMigrateInfo> getMigratorTables(){\n\t\treturn migratorTables;\n\t}\n\t\n\tprivate void loadOldConfig(){\n\t\ttry{\n\t\t\toldLoader = new XMLSchemaLoader();\n\t\t\toldDataHosts = oldLoader.getDataHosts();\n\t\t\toldDataNodes = oldLoader.getDataNodes();\n\t\t\toldSchemas = oldLoader.getSchemas();\n\t\t}catch(Exception e){\n\t\t\tthrow new ConfigException(\" old config for migrate read fail!please check schema.xml or  rule.xml  \"+e);\n\t\t}\n\t\t\n\t}\n\t\n\tprivate void loadNewConfig(){\n\t\ttry{\n\t\t\tnewLoader = new XMLSchemaLoader(NEW_SCHEMA, NEW_RULE);\n\t\t\tnewDataHosts = newLoader.getDataHosts();\n\t\t\tnewDataNodes = newLoader.getDataNodes();\n\t\t\tnewSchemas = newLoader.getSchemas();\n\t\t}catch(Exception e){\n\t\t\tthrow new ConfigException(\" new config for migrate read fail!please check newSchema.xml or  newRule.xml  \"+e);\n\t\t}\n\t\t\n\t}\n\t\n\t\n\tprivate void loadTablesFile() throws Exception{\n\t\tProperties pro = new Properties();\n\t\tif(!isAwaysUseMaster){\n\t\t\tdnIndexProps = loadDnIndexProps();\n\t\t}\n\t\ttry{\n\t\t\tpro.load(ConfigComparer.class.getResourceAsStream(TABLES_FILE));\n\t\t}catch(Exception e){\n\t\t\tthrow new ConfigException(\"tablesFile.properties read fail!\");\n\t\t}\n\t\tIterator<Entry<Object, Object>> it = pro.entrySet().iterator();\n\t\twhile(it.hasNext()){\n\t\t\tEntry<Object, Object> entry  = it.next();\n\t\t\tString schemaName = entry.getKey().toString();\n\t\t\tString tables = entry.getValue().toString();\n\t\t\tloadMigratorTables(schemaName,getTables(tables));\n\t\t}\n\t}\n\t\n\tprivate String[] getTables(String tables){\n\t\tif(tables.equalsIgnoreCase(\"all\") || tables.isEmpty()){\n\t\t\treturn new String[]{};\n\t\t}else{\n\t\t\treturn tables.split(\",\");\n\t\t}\n\t}\n\t\n\t/*\n\t * 加载迁移表信息，tables大小为0表示迁移schema下所有表\n\t */\n\tprivate void loadMigratorTables(String schemaName,String[] tables){\n\t\tif(!DataMigratorUtil.isKeyExistIgnoreCase(oldSchemas, schemaName)){\n\t\t\tthrow new ConfigException(\"oldSchema:\"+schemaName+\" is not exists!\");\n\t\t}\n\t\tif(!DataMigratorUtil.isKeyExistIgnoreCase(newSchemas,schemaName)){\n\t\t\tthrow new ConfigException(\"newSchema:\"+schemaName+\" is not exists!\");\n\t\t}\n\t\tMap<String, TableConfig> oldTables =  DataMigratorUtil.getValueIgnoreCase(oldSchemas, schemaName).getTables();\n\t\tMap<String, TableConfig> newTables = DataMigratorUtil.getValueIgnoreCase(newSchemas, schemaName).getTables();\n\t\tif(tables.length>0){\n\t\t\t//指定schema下的表进行迁移\n\t\t\tfor(int i =0;i<tables.length;i++){\n\t\t\t\tTableConfig oldTable =  DataMigratorUtil.getValueIgnoreCase(oldTables,tables[i]);\n\t\t\t\tTableConfig newTable = DataMigratorUtil.getValueIgnoreCase(newTables,tables[i]);\n\t\t\t\tloadMigratorTable(oldTable, newTable,schemaName,tables[i]);\n\t\t\t}\n\t\t}else{\n\t\t\t//迁移schema下所有的表\n\t\t\t//校验新旧schema中的table配置是否一致\n\t\t\tSet<String> oldSet = oldTables.keySet();\n\t\t\tSet<String> newSet = newTables.keySet();\n\t\t\tif(!oldSet.equals(newSet)){\n\t\t\t\tthrow new ConfigException(\"new & old table config is not equal!\");\n\t\t\t}\n\t\t\tfor(String tableName:oldSet){\n\t\t\t\tTableConfig oldTable = oldTables.get(tableName);\n\t\t\t\tTableConfig newTable = newTables.get(tableName);\n\t\t\t\tloadMigratorTable(oldTable, newTable,schemaName,tableName);\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\t\n\t\n\t\n\tprivate void loadMigratorTable(TableConfig oldTable,TableConfig newTable,String schemaName,String tableName){\n\t\t//禁止配置非拆分表\n\t\tif(oldTable == null || newTable == null){\n\t\t\tthrow new ConfigException(\"please check tableFile.properties,make sure \"+schemaName+\":\"+tableName+\" is sharding table \");\n\t\t}\n\t\t//忽略全局表\n\t\tif(oldTable.isGlobalTable()||newTable.isGlobalTable()){\n\t\t\tString message = \"global table: \"+schemaName+\":\"+tableName+\" is ignore!\";\n\t\t\tSystem.out.println(\"Warn: \"+message);\n\t\t\tLOGGER.warn(message);\n\t\t}else{\n\t\t\tList<DataNode > oldDN = getDataNodes(oldTable,oldDataNodes,oldDataHosts);\n\t\t\tList<DataNode > newDN = getDataNodes(newTable,newDataNodes,newDataHosts);\n\t\t\t//忽略数据节点分布没有发生变化的表\n\t\t\tif(isNeedMigrate(oldDN,newDN)){\n\t\t\t\tLOGGER.info(\"old table:{}\",oldTable);\n\t\t\t\tLOGGER.info(\"new table:{}\",newTable);\n\t\t\t\tif (oldTable.getRule()== null){\n\t\t\t\t\tLOGGER.info(\"beacuse {} rule is null,skip it,check it is er child table\",oldTable);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (newTable.getRule()==null){\n\t\t\t\t\tLOGGER.info(\"beacuse {} rule is null,skip it,check it is er child table.\",newTable);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcheckRuleConfig(oldTable.getRule(), newTable.getRule(),schemaName,tableName);\n\t\t\t\tRuleConfig newRC=newTable.getRule();\n\t\t\t\tTableMigrateInfo tmi = new TableMigrateInfo(schemaName, tableName, oldDN, newDN, newRC.getRuleAlgorithm(), newRC.getColumn());\n\t\t\t\tmigratorTables.add(tmi);\n\t\t\t}else{\n\t\t\t\tString message = schemaName+\":\"+tableName+\" is ignore,no need to migrate!\";\n\t\t\t\tLOGGER.warn(message);\n\t\t\t\tSystem.out.println(\"Warn: \"+message);\n\t\t\t}\n\t\t\t\n\t\t}\n\t}\n\t\n\t//对比前后表数据节点分布是否一致\n\tprivate boolean isNeedMigrate(List<DataNode> oldDN,List<DataNode> newDN){\n\t\tif(oldDN.size() != newDN.size()){\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t//获取拆分表对应节点列表,具体到实例地址、库\n\tprivate List<DataNode> getDataNodes(TableConfig tableConfig,Map<String, DataNodeConfig> dnConfig,Map<String, DataHostConfig> dhConfig){\n\t\tList<DataNode> dataNodes = new ArrayList<DataNode>();\n\t\t//TO-DO\n\t\tArrayList<String> dataNodeNames = tableConfig.getDataNodes();\n\t\tint i = 0;\n\t\tfor(String name:dataNodeNames){\n\t\t\tDataNodeConfig config = dnConfig.get(name);\n\t\t\tString db = config.getDatabase();\n\t\t\tString dataHost = config.getDataHost();\n\t\t\tDataHostConfig dh = dhConfig.get(dataHost);\n\t\t\tString dbType = dh.getDbType();\n\t\t\tDBHostConfig[]  writeHosts = dh.getWriteHosts();\n\t\t\tDBHostConfig currentWriteHost;\n\t\t\tif(isAwaysUseMaster){\n\t\t\t\tcurrentWriteHost = writeHosts[0];\n\t\t\t}else{\n\t\t\t    //迁移数据发生在当前切换后的数据源\n\t\t\t\tcurrentWriteHost = writeHosts[Integer.valueOf(dnIndexProps.getProperty(dh.getName()))];\n\t\t\t}\n\t\t\tDataNode dn = new DataNode(name,currentWriteHost.getIp(), currentWriteHost.getPort(), currentWriteHost.getUser(), currentWriteHost.getPassword(), db, dbType,i++);\n\t\t\tdataNodes.add(dn);\n\t\t}\n\t\t\n\t\treturn dataNodes;\n\t}\n\t\n\t//校验前后路由规则是否一致\n\tprivate void checkRuleConfig(RuleConfig oldRC,RuleConfig newRC,String schemaName,String tableName){\n\t\tif(!oldRC.getColumn().equalsIgnoreCase(newRC.getColumn())){\n\t\t\tthrow new ConfigException(schemaName+\":\"+tableName+\" old & new partition column is not same!\");\n\t\t}\n\t\tAbstractPartitionAlgorithm oldAlg = oldRC.getRuleAlgorithm();\n\t\tAbstractPartitionAlgorithm newAlg = newRC.getRuleAlgorithm();\n\t\t//判断路由算法前后是否一致\n\t\tif(!oldAlg.getClass().isAssignableFrom(newAlg.getClass())){\n\t\t\tthrow new ConfigException(schemaName+\":\"+tableName+\" old & new rule Algorithm is not same!\");\n\t\t}\n\t}\n\t\n\tprivate Properties loadDnIndexProps() {\n\t\tProperties prop = new Properties();\n\t\tInputStream is = null;\n\t\ttry {\n\t\t\tis = ConfigComparer.class.getResourceAsStream(DN_INDEX_FILE);\n\t\t\tprop.load(is);\n\t\t} catch (Exception e) {\n\t\t\tthrow new ConfigException(\"please check file \\\"dnindex.properties\\\" \"+e.getMessage());\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tif(is !=null){\n\t\t\t\t\tis.close();\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\tthrow new ConfigException(e.getMessage());\n\t\t\t}\n\t\t}\n\t\treturn prop;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/DataClearRunner.java",
    "content": "package io.mycat.util.dataMigrator;\r\n\r\nimport java.io.File;\r\nimport java.io.IOException;\r\nimport java.sql.Connection;\r\nimport java.sql.SQLException;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport com.alibaba.druid.util.JdbcUtils;\r\n\r\n/**\r\n * 清理数据扩容缩容后的冗余数据\r\n * @author haonan108\r\n *\r\n */\r\npublic class DataClearRunner implements Runnable{\r\n\r\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(DataClearRunner.class);\r\n\tprivate DataNode srcDn;\r\n\tprivate File tempFile;\r\n\tprivate TableMigrateInfo tableInfo;\r\n\t\r\n\tpublic DataClearRunner(TableMigrateInfo tableInfo,DataNode srcDn,File tempFile){\r\n\t\tthis.tableInfo = tableInfo;\r\n\t\tthis.srcDn = srcDn;\r\n\t\tthis.tempFile = tempFile;\r\n\t}\r\n\t@Override\r\n\tpublic void run() {\r\n\t\tString data = \"\";\r\n\t\tlong offset = 0;\r\n\t\tConnection con = null;\r\n\t\ttry {\r\n\t\t\tlong start = System.currentTimeMillis();\r\n\t\t\tcon = DataMigratorUtil.getMysqlConnection(srcDn);\r\n\t\t\tif(tableInfo.isExpantion()){\r\n\t\t\t\tdeleteDataDependFile(data, offset, con);\r\n\t\t\t}else{\r\n\t\t\t\t//缩容，移除的节点直接truncate删除数据，非移除的节点按照临时文件的中值进行删除操作\r\n\t\t\t\tList<DataNode> list = tableInfo.getRemovedDataNodes();\r\n\t\t\t\tboolean isRemovedDn = false;\r\n\t\t\t\tfor(DataNode dn:list){\r\n\t\t\t\t\tif(srcDn.equals(dn)){\r\n\t\t\t\t\t\tisRemovedDn = true;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif(isRemovedDn){\r\n\t\t\t\t\tString sql = \"truncate \"+tableInfo.getTableName();\r\n\t\t\t\t\tJdbcUtils.execute(con, sql, new ArrayList<>());\r\n\t\t\t\t}else{\r\n\t\t\t\t\tdeleteDataDependFile(data, offset, con);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tlong end = System.currentTimeMillis();\r\n\t\t\tSystem.out.println(tableInfo.getSchemaAndTableName()+\" clean dataNode \"+srcDn.getName()+\" completed in \"+(end-start)+\"ms\");\r\n\t\t\t\r\n\t\t} catch (Exception e) {\r\n\t\t\tString errMessage = srcDn.toString()+\":\"+\"clean data error!\";\r\n\t\t\tLOGGER.error(errMessage, e);\r\n\t\t\ttableInfo.setError(true);\r\n\t\t\ttableInfo.getErrMessage().append(errMessage+\"\\n\");\r\n\t\t} finally{\r\n\t\t\tJdbcUtils.close(con);\r\n\t\t}\r\n\t}\r\n\t\r\n\tprivate void deleteDataDependFile(String data,long offset,Connection con) throws IOException, SQLException{\r\n\t\twhile((data=DataMigratorUtil.readData(tempFile,offset,DataMigrator.margs.getQueryPageSize())).length()>0){\r\n\t\t\toffset += data.getBytes().length;\r\n\t\t\tif(data.startsWith(\",\")){\r\n\t\t\t\tdata = data.substring(1, data.length());\r\n\t\t\t}\r\n\t\t\tif(data.endsWith(\",\")){\r\n\t\t\t\tdata = data.substring(0,data.length()-1);\r\n\t\t\t}\r\n\t\t\tString sql = \"delete from \"+tableInfo.getTableName()+\" where \"+tableInfo.getColumn()+\" in (\"+data+\")\";\r\n\t\t\tJdbcUtils.execute(con, sql, new ArrayList<>());\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/DataIO.java",
    "content": "package io.mycat.util.dataMigrator;\r\n\r\nimport java.io.File;\r\nimport java.io.IOException;\r\n\r\n/**\r\n * 数据导入导出接口，mysql、oracle等数据库通过实现此接口提供具体的数据导入导出功能\r\n * @author haonan108\r\n *\r\n */\r\npublic interface DataIO {\r\n \r\n\t/**\r\n\t * 导入数据\r\n\t * @param dn 导入到具体的数据库\r\n\t * @param file 导入的文件\r\n\t * @throws IOException \r\n\t * @throws InterruptedException \r\n\t */\r\n\t\r\n    void importData(TableMigrateInfo table,DataNode dn,String tableName,File file) throws IOException, InterruptedException;\r\n    \r\n    /**\r\n     * 根据条件导出迁移数据\r\n     * @param dn 导出哪个具体的数据库\r\n     * @param tableName 导出的表名称\r\n     * @param export 文件导出到哪里\r\n     * @param condion 导出文件依赖的具体条件\r\n     * @return \r\n     * @throws IOException \r\n     * @throws InterruptedException \r\n     */\r\n    File exportData(TableMigrateInfo table,DataNode dn,String tableName,File exportPath,File condion) throws IOException, InterruptedException;\r\n\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/DataIOFactory.java",
    "content": "package io.mycat.util.dataMigrator;\r\n\r\nimport io.mycat.util.dataMigrator.dataIOImpl.MysqlDataIO;\r\nimport io.mycat.util.exception.DataMigratorException;\r\n\r\npublic class DataIOFactory {\r\n\r\n\tpublic static final String MYSQL = \"mysql\";\r\n\tpublic static final String ORACLE = \"oracle\";\r\n\t\r\n\tpublic static DataIO createDataIO(String dbType){\r\n\t\tswitch (dbType) {\r\n\t\tcase MYSQL:\r\n\t\t\treturn new MysqlDataIO();\r\n\t\tdefault:\r\n\t\t\tthrow new DataMigratorException(\"dbType:\"+dbType+\" is not support for the moment!\");\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/DataMigrateRunner.java",
    "content": "package io.mycat.util.dataMigrator;\r\n\r\nimport java.io.File;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\n\r\n/**\r\n * 数据迁移过程类\r\n * @author haonan108\r\n *\r\n */\r\npublic  class DataMigrateRunner implements Runnable{\r\n\r\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(DataMigrateRunner.class);\r\n\tprivate DataNode src;\r\n\tprivate DataNode target;\r\n\tprivate String tableName;\r\n\tprivate DataIO dataIO;\r\n\tprivate File conditionFile;\r\n\tprivate TableMigrateInfo table;\r\n\t\r\n\t\r\n\t\r\n\tpublic DataMigrateRunner(TableMigrateInfo table, DataNode src,DataNode target,String tableName,File conditionFile){\r\n\t\tthis.tableName = tableName;\r\n\t\tthis.conditionFile= conditionFile;\r\n\t\tthis.src = src;\r\n\t\tthis.target = target;\r\n\t\tthis.table = table;\r\n\t\tdataIO = DataIOFactory.createDataIO(src.getDbType());\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void run() {\r\n\t\tif(table.isError()) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\ttry {\r\n\t\t\tlong start = System.currentTimeMillis();\r\n\t\t\tFile loadFile = dataIO.exportData(table,src, tableName, conditionFile.getParentFile(), conditionFile);\r\n\t\t\tdataIO.importData(table,target,tableName, loadFile);\r\n\t\t\tlong end = System.currentTimeMillis();\r\n\t\t\tSystem.out.println(table.getSchemaAndTableName()+\" \"+src.getName()+\"->\"+target.getName()+\" completed in \"+(end-start)+\"ms\");\r\n\t\t} catch (Exception e) {\r\n\t\t\tString errMessage = table.getSchemaAndTableName()+\" \"+src.getName()+\"->\"+target.getName()+\" migrate err! \"+e.getMessage();\r\n\t\t\tLOGGER.error(errMessage, e);\r\n\t\t\ttable.setError(true);\r\n\t\t\ttable.getErrMessage().append(errMessage);\r\n\t\t}\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/DataMigrator.java",
    "content": "package io.mycat.util.dataMigrator;\n\nimport java.io.File;\nimport java.sql.SQLException;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n\n/**\n * 数据迁移统一调度类，支持扩容缩容\n * 原理：读取需要迁移的数据节点表所有拆分字段数据，按照扩容或缩容后的配置对拆分字段重新计算路由节点，\n * 将需要迁移的数据导出，然后导入到扩容或缩容后对应的数据节点\n * @author haonan108\n *\n */\npublic class DataMigrator {\n \n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(DataMigrator.class);\n\t\n\tpublic static  DataMigratorArgs margs;\n\t\n\tprivate List<TableMigrateInfo> migrateTables;\n\t\n\tprivate ExecutorService executor;\n\t\n\tprivate List<DataNodeClearGroup> clearGroup = new ArrayList<>();\n\t\n\tpublic DataMigrator(String[] args){\n\t\tmargs = new DataMigratorArgs(args);\n\t\texecutor = new ThreadPoolExecutor(margs.getThreadCount(), margs.getThreadCount(),\n                0L, TimeUnit.MILLISECONDS,\n                new LinkedBlockingQueue<Runnable>(),new ThreadPoolExecutor.CallerRunsPolicy());\n\t\t\n\t\t\n\t\ttry {\n\t\t\tcreateTempParentDir(margs.getTempFileDir());\n\t\t\tConfigComparer loader = new ConfigComparer(margs.isAwaysUseMaster());\n\t\t\tmigrateTables = loader.getMigratorTables();\n\t\t\t//建表\n\t\t\tfor(TableMigrateInfo table:migrateTables){\n\t\t\t\ttable.setTableStructure();\n\t\t\t\ttable.createTableToNewDataNodes();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(),e);\n\t\t\tSystem.out.println(e.getMessage());\n\t\t\t//配置错误退出迁移程序\n\t\t\tSystem.exit(-1);\n\t\t}\n\t}\n\t\n\tpublic static void main(String[] args) throws SQLException {\n\t\tlong start = System.currentTimeMillis();\n\t\tDateFormat format = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss:SSS\");\n\t\tSystem.out.println(\"\\n\"+format.format(new Date())+\" [1]-> creating migrator schedule and temp files for migrate...\");\n\t\t//初始化配置\n\t\tDataMigrator migrator = new DataMigrator(args);\n\t\t\n\t\t//生成中间文件\n\t\tmigrator.createTempFiles();\n\t\tmigrator.changeSize();\n\t\tmigrator.printInfo();\n\n\t\t//迁移数据\n\t\tSystem.out.println(\"\\n\"+format.format(new Date())+\" [2]-> start migrate data...\");\n\t\tmigrator.migrateData();\n\t\t\n\t\t//清除中间临时文件、清除被迁移掉的冗余数据\n\t\tSystem.out.println(\"\\n\"+format.format(new Date())+\" [3]-> cleaning redundant data...\");\n\t\tmigrator.clear();\n\t\t\n\t\t//校验数据是否迁移成功\n\t\tSystem.out.println(\"\\n\"+format.format(new Date())+\" [4]-> validating tables migrate result...\");\n\t\tmigrator.validate();\n\t\tmigrator.clearTempFiles();\n\t\tlong end = System.currentTimeMillis();\n\t\tSystem.out.println(\"\\n\"+format.format(new Date())+\" migrate data complete in \"+(end-start)+\"ms\");\n\t}\n\t\n\t//打印各个表的迁移数据信息\n\tprivate void printInfo() {\n\t\tfor(TableMigrateInfo table:migrateTables){\n\t\t\ttable.printMigrateInfo();\n\t\t\ttable.printMigrateSchedule();\n\t\t}\n\t}\n\n\t//删除临时文件\n\tprivate void clearTempFiles() {\n\t\tFile tempFileDir = new File(margs.getTempFileDir());\n\t\tif(tempFileDir.exists() && margs.isDeleteTempDir()){\n\t\t\tDataMigratorUtil.deleteDir(tempFileDir);\n\t\t}\n\t}\n\n\t//生成需要进行迁移的数据依赖的拆分字段值文件\n\tprivate void createTempFiles(){\n\t\tfor(TableMigrateInfo table:migrateTables){\n\t\t\t//创建具体拆分表中间临时文件\n\t\t\tcreateTableTempFiles(table);\n\t\t}\n\t\texecutor.shutdown();\n\t\twhile(true){\n\t\t\tif(executor.isTerminated()){\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tThread.sleep(200);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tLOGGER.error(\"error\",e);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate void migrateData() throws SQLException{\n\t\texecutor =  new ThreadPoolExecutor(margs.getThreadCount(), margs.getThreadCount(),\n                0L, TimeUnit.MILLISECONDS,\n                new LinkedBlockingQueue<Runnable>(),new ThreadPoolExecutor.CallerRunsPolicy());\n\t\tfor(TableMigrateInfo table:migrateTables){\n\t\t\tif(!table.isError()){ //忽略已出错的拆分表\n\t\t\t\tList<DataNodeMigrateInfo> detailList = table.getDataNodesDetail();\n\t\t\t\tfor(DataNodeMigrateInfo info:detailList){\n\t\t\t\t\tLOGGER.info(\"{}\",info);\n\t\t\t\t\texecutor.execute(new DataMigrateRunner(table, info.getSrc(), info.getTarget(), table.getTableName(), info.getTempFile()));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\texecutor.shutdown();\n\t\twhile(true){\n\t\t\tif(executor.isTerminated()){\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tThread.sleep(200);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tLOGGER.error(\"error\",e);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t//缩容需要重新计算表大小\n\tprivate void changeSize() throws SQLException {\n\t\tfor(TableMigrateInfo table:migrateTables){\n\t\t\tif(!table.isExpantion()){\n\t\t\t\tList<DataNode> oldDn = table.getOldDataNodes();\n\t\t\t\tlong size = 0L;\n\t\t\t\tfor(DataNode dn:oldDn){\n\t\t\t\t\tsize+=DataMigratorUtil.querySize(dn, table.getTableName());\n\t\t\t\t}\n\t\t\t\ttable.setSize(size);\n\t\t\t}\n\t\t}\n\t}\n\n\t//校验迁移计划中数据迁移情况同数据实际落盘是否一致\n\tprivate void validate() throws SQLException {\n\t\tfor(TableMigrateInfo table:migrateTables){\n\t\t\tif (table.isError()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlong size = table.getSize().get();\n\t\t\tlong factSize = 0L;\n\t\t\tfor(DataNode dn:table.getNewDataNodes()){\n\t\t\t\tfactSize+=DataMigratorUtil.querySize(dn, table.getTableName());\n\t\t\t}\n\t\t\tif(factSize != size){\n\t\t\t\tString message = \"migrate error!after migrate should be:\"+size+\" but fact is:\"+factSize;\n\t\t\t\ttable.setError(true);\n\t\t\t\ttable.setErrMessage(message);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//打印最终迁移结果信息\n\t\tString title = \"migrate result\";\n\t\tMap<String,String> result = new HashMap<String, String>();\n\t\tfor(TableMigrateInfo table:migrateTables){\n\t\t\tString resultMessage = table.isError()?\"fail! reason: \"+table.getErrMessage():\"success\";\n\t\t\tresult.put(table.getSchemaAndTableName(), resultMessage);\n\t\t}\n\t\tString info = DataMigratorUtil.printMigrateInfo(title, result, \"->\");\n\t\tSystem.out.println(info);\n\t}\n\t\n\t//清除中间临时文件、导出的迁移数据文件、已被迁移的原始节点冗余数据\n\tprivate void clear(){\n\t\tfor(TableMigrateInfo table:migrateTables){\n\t\t\tmakeClearDataGroup(table);\n\t\t}\n\t\tfor(DataNodeClearGroup group:clearGroup){\n\t\t\tclearData(group.getTempFiles(), group.getTableInfo());\n\t\t}\n\t}\n\t\n\t//同一主机上的mysql执行按where条件删除数据并发多了性能反而下降很快\n\t//按照主机ip进行分组，每个主机ip分配一个线程池，线程池大小可配置，默认为当前主机环境cpu核数的一半\n\tprivate void makeClearDataGroup(TableMigrateInfo table){\n\t\tList<DataNodeMigrateInfo>  list = table.getDataNodesDetail();\n\t\t //将数据节点按主机ip分组，每组分配一个线程池\n\t\tfor(DataNodeMigrateInfo dnInfo:list){\n\t\t\tDataNode src = dnInfo.getSrc();\n\t\t\tString ip  =src.getIp();\n\t\t\tFile f = dnInfo.getTempFile();\n\t\t\tDataNodeClearGroup group = getDataNodeClearGroup(ip,table);\n\t\t\tif(group == null){\n\t\t\t\tgroup = new DataNodeClearGroup(ip, table);\n\t\t\t\tclearGroup.add(group);\n\t\t\t}\n\t\t\tgroup.getTempFiles().put(f, src);\n\t\t}\n\t}\n\t\n\tprivate DataNodeClearGroup getDataNodeClearGroup(String ip, TableMigrateInfo table){\n\t\tDataNodeClearGroup result = null;\n\t\tfor(DataNodeClearGroup group:clearGroup){\n\t\t\tif(group.getIp().equals(ip) && group.getTableInfo().equals(table)){\n\t\t\t\tresult = group;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\t\n\tprivate void clearData(Map<File,DataNode> map,TableMigrateInfo table){\n\t\tif(table.isError()) {\n\t\t\treturn;\n\t\t}\n\t\tExecutorService executor  =  new ThreadPoolExecutor(margs.getThreadCount(), margs.getThreadCount(),\n                0L, TimeUnit.MILLISECONDS,\n                new LinkedBlockingQueue<Runnable>(),new ThreadPoolExecutor.CallerRunsPolicy());\n\t\tIterator<Entry<File,DataNode>>  it = map.entrySet().iterator();\n\t\twhile(it.hasNext()){\n\t\t\tEntry<File,DataNode> et = it.next();\n\t\t\tFile f =et.getKey();\n\t\t\tDataNode srcDn  =  et.getValue();\n\t\t\texecutor.execute(new DataClearRunner(table, srcDn, f));\n\t\t}\n\t\texecutor.shutdown();\n\t\twhile(true){\n\t\t\tif(executor.isTerminated()){\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tThread.sleep(200);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tLOGGER.error(\"error\",e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void createTempParentDir(String dir){\n\t\tFile outputDir = new File(dir);\n\t\tif(outputDir.exists()){\n\t\t\tDataMigratorUtil.deleteDir(outputDir);\n\t\t}\n\t\toutputDir.mkdirs();\n\t\toutputDir.setWritable(true);\n\t}\n\t\n\tprivate void createTableTempFiles(TableMigrateInfo table) {\n\t\tList<DataNode> oldDn = table.getOldDataNodes();\n\t\t//生成迁移中间文件，并生成迁移执行计划\n\t\tfor(DataNode dn:oldDn){\n\t\t\texecutor.execute(new MigratorConditonFilesMaker(table,dn,margs.getTempFileDir(),margs.getQueryPageSize()));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/DataMigratorArgs.java",
    "content": "package io.mycat.util.dataMigrator;\r\n\r\nimport java.io.File;\r\n\r\nimport io.mycat.config.model.SystemConfig;\r\nimport io.mycat.util.cmd.CmdArgs;\r\n\r\n\r\n\r\n\r\n/**\r\n * 数据迁移工具依赖参数\r\n * @author haonan108\r\n *\r\n */\r\npublic class DataMigratorArgs {\r\n\r\n\t/** 并行线程数*/\r\n\tpublic static final String THREAD_COUNT = \"threadCount\";\r\n\t\r\n\t/** mysqldump命令所在路径 */\r\n\tpublic static final String MYSQL_BIN = \"mysqlBin\";\r\n\t\r\n\t/** 数据迁移生成的中间文件指定存放目录*/\r\n\tpublic static final String TEMP_FILE_DIR = \"tempFileDir\";\r\n\t\r\n\t/** 使用主数据源还是当前数据源(如果发生主备切换存在数据源选择问题)*/\r\n\tpublic static final String IS_AWAYS_USE_MASTER = \"isAwaysUseMaster\";\r\n\t\r\n\t/**生成中间临时文件一次加载的数据量*/\r\n\tpublic static final String QUERY_PAGE_SIZE = \"queryPageSize\";\r\n\t\r\n\tpublic static final String DEL_THRAD_COUNT = \"delThreadCount\";\r\n\t\r\n\t/** mysqldump导出中间文件命令操作系统限制长度 */\r\n\tpublic static final String MYSQL_DUMP_CMD_LENGTH = \"cmdLength\";\r\n\r\n\tpublic static final String CHARSET = \"charset\";\r\n\r\n\tpublic static final String GTID_PURGED = \"gtidPurged\";\r\n\t\r\n\t/**完成扩容缩容后清除临时文件 默认为true*/\r\n\tpublic static final String DELETE_TEMP_FILE_DIR = \"deleteTempFileDir\";\r\n\t\r\n\t\r\n\t\r\n\tprivate static final int DEFAULT_THREAD_COUNT = Runtime.getRuntime().availableProcessors()*2;\r\n\t\r\n\tprivate static final int DEFAULT_DEL_THRAD_COUNT  = Runtime.getRuntime().availableProcessors()/2;\r\n\t\r\n\tprivate static final int DEFAULT_CMD_LENGTH = 110*1024;//操作系统命令行限制长度 110KB\r\n\t\r\n\tprivate static final int DEFAULT_PAGE_SIZE = 100000;//默认一次读取10w条数据\r\n\t\r\n\tprivate static final String DEFAULT_CHARSET = \"utf8\";\r\n\t\r\n\tprivate CmdArgs cmdArgs;\r\n\t\r\n\tpublic DataMigratorArgs(String[] args){\r\n\t\tcmdArgs = CmdArgs.getInstance(args);\r\n\t}\r\n\t\r\n\tpublic String getString(String name){\r\n\t\treturn cmdArgs.getString(name);\r\n\t}\r\n\t\r\n\tpublic String getMysqlBin(){\r\n\t\tString result = getString(MYSQL_BIN);\r\n\t\tif(result ==null) {\r\n\t\t\treturn \"\";\r\n\t\t}\r\n\t\tif(!result.isEmpty() &&!result.endsWith(\"/\")){\r\n\t\t\tresult +=\"/\";\r\n\t\t}\r\n\t\treturn result;\r\n\t}\r\n\t\r\n\tpublic String getTempFileDir(){\r\n\t\tString path = getString(TEMP_FILE_DIR);\r\n\t\tif(null == path || path.trim().isEmpty()){\r\n\t\t\treturn SystemConfig.getHomePath()+File.separator+\"temp\";\r\n\t\t}\r\n\t\treturn path;\r\n\t}\r\n\t\r\n\tpublic int getThreadCount(){\r\n\t\tString count =getString(THREAD_COUNT);\r\n\t\tif(null == count||count.isEmpty()|| count.equals(\"0\") ){\r\n\t\t\treturn DEFAULT_THREAD_COUNT;\r\n\t\t}\r\n\t\treturn Integer.valueOf(count);\r\n\t}\r\n\t\r\n\tpublic int getDelThreadCount(){\r\n\t\tString count =getString(DEL_THRAD_COUNT);\r\n\t\tif(null == count||count.isEmpty()|| count.equals(\"0\") ){\r\n\t\t\treturn DEFAULT_DEL_THRAD_COUNT;\r\n\t\t}\r\n\t\treturn Integer.valueOf(count);\r\n\t}\r\n\t\r\n\tpublic boolean isAwaysUseMaster(){\r\n\t\tString result = getString(IS_AWAYS_USE_MASTER);\r\n\t\tif(null == result||result.isEmpty()||result.equals(\"true\")){\r\n\t\t\treturn true;\r\n\t\t}\r\n\t    return false;\r\n\t}\r\n\t\r\n\tpublic int getCmdLength(){\r\n\t\tString result = getString(MYSQL_DUMP_CMD_LENGTH);\r\n\t\tif(null  == result||result.isEmpty()){\r\n\t\t\treturn DEFAULT_CMD_LENGTH;\r\n\t\t}\r\n\t\tif(result.contains(\"*\")){\r\n\t\t\tString[] arr = result.split(\"\\\\*\");\r\n\t\t\tint j = 1;\r\n\t\t\tfor (int i = 0; i < arr.length; i++) {\r\n\t\t\t\tj *= Integer.valueOf(arr[i]);\r\n\t\t\t}\r\n\t\t\treturn j;\r\n\t\t}\r\n\t\treturn Integer.valueOf(result);\r\n\t}\r\n\t\r\n\tpublic int getQueryPageSize(){\r\n\t\tString result = getString(QUERY_PAGE_SIZE);\r\n\t\tif(null == result||result.isEmpty()){\r\n\t\t\treturn DEFAULT_PAGE_SIZE;\r\n\t\t}\r\n\t\treturn Integer.valueOf(result);\r\n\t}\r\n\t\r\n\tpublic String getCharSet(){\r\n\t\tString result = getString(CHARSET);\r\n\t\tif(null == result||result.isEmpty()){\r\n\t\t\treturn DEFAULT_CHARSET;\r\n\t\t}\r\n\t\treturn result;\r\n\t}\r\n\r\n\tpublic boolean isGtidPurged(){\r\n\t\tString result = getString(GTID_PURGED);\r\n\t\tif(null == result||result.isEmpty()){\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn Boolean.parseBoolean(result);\r\n\t}\r\n\r\n\tpublic boolean isDeleteTempDir(){\r\n\t\tString result = getString(DELETE_TEMP_FILE_DIR);\r\n\t\tif(null == result||result.isEmpty()||result.equals(\"true\")){\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/DataMigratorUtil.java",
    "content": "package io.mycat.util.dataMigrator;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.RandomAccessFile;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.Map.Entry;\nimport java.util.regex.Matcher;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.util.JdbcUtils;\n\npublic class DataMigratorUtil {\n\t\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(DataMigratorUtil.class);\n\t\n\tstatic{\n\t\ttry {\n\t\t\tClass.forName(\"com.mysql.jdbc.Driver\");\n\t\t} catch (ClassNotFoundException e) {\n\t\t\tLOGGER.error(\"\",e);\n\t\t}\n\t}\n\t\n\t/**\n\t * 添加数据到文件末尾\n\t * @param file\n\t * @param content\n\t * @throws IOException\n\t */\n\tpublic static void appendDataToFile(File file, String content) throws IOException {   \n\t\tRandomAccessFile randomFile = null;  \n\t\ttry {     \n\t\t\t// 打开一个随机访问文件流，按读写方式     \n\t\t\trandomFile = new RandomAccessFile(file, \"rw\");     \n\t\t\t// 文件长度，字节数     \n\t\t\tlong fileLength = randomFile.length();     \n\t\t\t// 将写文件指针移到文件尾。     \n\t\t\trandomFile.seek(fileLength);     \n\t\t\trandomFile.writeBytes(content);\n\t\t\tcontent = null;\n\t\t} catch (IOException e) {     \n\t\t\tLOGGER.error(\"appendDataToFile is error!\",e);\n\t\t} finally{  \n\t\t\tif(randomFile != null){  \n\t\t\t\ttry {  \n\t\t\t\t\trandomFile.close();  \n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tLOGGER.error(\"error\",e);\n\t\t\t\t}  \n\t\t\t}  \n\t\t}  \n\t}\n\t\n\tpublic static String readDataFromFile(File file,long offset,int length) throws IOException{\n\t\tRandomAccessFile randomFile = null;  \n\t\ttry {     \n\t\t\t// 打开一个随机访问文件流，按读写方式     \n\t\t\trandomFile = new RandomAccessFile(file, \"rw\");     \n\t\t\trandomFile.seek(offset);\n\t\t\tbyte[] buffer = new byte[length];\n\t\t\trandomFile.read(buffer);\n\t\t\treturn new String(buffer).trim();\n\t\t} catch (IOException e) {     \n\t\t\tthrow e;   \n\t\t} finally{  \n\t\t\tif(randomFile != null){  \n\t\t\t\ttry {  \n\t\t\t\t\trandomFile.close();  \n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tLOGGER.error(\"error\",e);\n\t\t\t\t}  \n\t\t\t}  \n\t\t}  \n\t}\n\t\n\t/**\n\t * 读取逗号分隔的文件数据\n\t * @param file \n\t * @param start 文件起始位置\n\t * @param length 读取字节数\n\t * @return\n\t * @throws IOException\n\t */\n\tpublic static  String readData(File file,long start,int length) throws IOException{\n\t\tString data = readDataFromFile(file, start, length);\n\t\tif((start+length)<=file.length()){\n\t\t\tdata = data.substring(0, data.lastIndexOf(\",\"));\n\t\t}\n\n\t\treturn data;\n\t}\n\t\n\tpublic static final int BUFSIZE = 1024 * 8; \n\t\n\tpublic static void mergeFiles(File outFile, File f) throws IOException {  \n        FileChannel outChannel = null;\n        FileOutputStream fos = null;\n        FileInputStream fis = null;\n        try {  \n        \tfos = new FileOutputStream(outFile,true);\n        \tfis = new FileInputStream(f);\n            outChannel = fos.getChannel();  \n            FileChannel fc = fis.getChannel();   \n            ByteBuffer bb = ByteBuffer.allocate(BUFSIZE);  \n            while(fc.read(bb) != -1){  \n            \tbb.flip();  \n            \toutChannel.write(bb);  \n            \tbb.clear();  \n            }  \n            fc.close();  \n        } catch (IOException e) {  \n        \tthrow e;\n        } finally {  \n            try {\n            \tif(fos != null){\n            \t\tfos.close();\n            \t}\n            \tif(fis != null){\n            \t\tfis.close();\n            \t}\n            \tif (outChannel != null){\n            \t\toutChannel.close();\n            \t}\n            } \n            catch (IOException e) {\n\t\t\t\tLOGGER.error(\"error\",e);\n            }  \n        }  \n    }\n\t\n\t/**\n\t * 统计文件有多少行\n\t * @param file\n\t * @return\n\t */\n\tpublic static long countLine(File file) throws IOException{\n\t\tlong count = 0L;\n\t\tRandomAccessFile randomFile = null;  \n\t\t\n\t\t// 打开一个随机访问文件流，按读写方式     \n\t\ttry {\n\t\t\trandomFile = new RandomAccessFile(file, \"rw\");\n\t\t\tString s =\"\";\n\t\t\twhile((s=randomFile.readLine())!=null && !s.trim().isEmpty()){\n\t\t\t\tcount++;\n\t\t\t}\n\t\t} catch (FileNotFoundException e) {\n\t\t\tthrow e;\n\t\t}finally{\n\t\t\tif(randomFile != null){  \n\t\t\t\ttry {  \n\t\t\t\t\trandomFile.close();  \n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tLOGGER.error(\"error\",e);\n\t\t\t\t}  \n\t\t\t}  \n\t\t}\n\t\t\n\t\treturn count;\n\t}\n\t\n\t/**\n     * 递归删除目录下的所有文件及子目录下所有文件\n     * @param dir 将要删除的文件目录\n     * @return boolean Returns \"true\" if all deletions were successful.\n     *                 If a deletion fails, the method stops attempting to\n     *                 delete and returns \"false\".\n     */\n    public static boolean deleteDir(File dir) {\n        if (dir.isDirectory()) {\n            String[] children = dir.list();\n            for (int i=0; i<children.length; i++) {\n                boolean success = deleteDir(new File(dir, children[i]));\n                if (!success) {\n                    return false;\n                }\n            }\n        }\n        // 目录此时为空，可以删除\n        return dir.delete();\n    }\n    \n    //将命令行中的？替换为具体参数\n    public static  String paramsAssignment(String cmd,String mark,Object... params){\n\t\tList<Object> paramList= Arrays.asList(params);\n\t\tfor(Object param:paramList){\n\t\t\tcmd = cmd.replaceFirst(\"\\\\\"+mark, Matcher.quoteReplacement(param.toString()));\n\t\t}\n\t\treturn cmd;\n\t}\n    \n    public static Connection getMysqlConnection(DataNode dn) throws SQLException{\n    \tConnection con = null;\n\t\tcon = DriverManager.getConnection(dn.getUrl(), dn.getUserName(), dn.getPwd());\n    \treturn con;\n    }\n    \n    public static List<Map<String, Object>> executeQuery(Connection conn, String sql,Object... parameters) throws SQLException{\n    \treturn JdbcUtils.executeQuery(conn, sql, Arrays.asList(parameters));\n    }\n    \n    //查询表数据量\n  \tpublic static long querySize(DataNode dn,String tableName) throws SQLException{\n  \t\tList<Map<String, Object>> list=null;\n  \t\tlong size = 0L;\n  \t\tConnection con = null;\n  \t\ttry {\n  \t\t\tcon =  getMysqlConnection(dn);\n  \t\t\tlist = executeQuery(con, \"select count(1) size from \"+tableName);\n  \t\t\tsize = (long) list.get(0).get(\"size\");\n  \t\t} catch (SQLException e) {\n  \t\t\tthrow e;\n  \t\t}finally{\n  \t\t\tJdbcUtils.close(con);\n  \t\t}\n  \t\treturn size;\n  \t}\n  \t\n  \tpublic static void createTable(DataNode dn,String table) throws SQLException{\n  \t\tConnection con = null;\n  \t\ttry {\n  \t\t\tcon =  getMysqlConnection(dn);\n  \t\t\tJdbcUtils.execute(con, table, new ArrayList<>());\n  \t\t} catch (SQLException e) {\n  \t\t\tthrow e;\n  \t\t}finally{\n  \t\t\tJdbcUtils.close(con);\n  \t\t}\n  \t}\n  \t\n  \t/**\n  \t * 格式化数据迁移信息\n  \t *  +---------title-------+\n  \t *  |key1 = value1     |\n  \t *  |key2 = value2     |\n  \t *  |...                        |\n  \t *  +---------------------+\n  \t * @param title\n  \t * @param map\n  \t * @param mark\n  \t * @return\n  \t */\n  \tpublic static  String printMigrateInfo(String title,Map<String,String> map,String mark){\n  \t\tStringBuilder result = new StringBuilder(\" \");\n  \t\tList<String> mergeList = new ArrayList<>();\n  \t\t\n  \t\tIterator<Entry<String, String>> itor = map.entrySet().iterator();\n  \t\t\n  \t\tint maxKeyLength = 0;\n  \t\tint maxValueLength = 0;\n  \t\twhile(itor.hasNext()){\n  \t\t\tEntry<String, String> entry = itor.next();\n  \t\t\tString key = entry.getKey();\n  \t\t\tString value = entry.getValue();\n  \t\t\tmaxKeyLength = (key.length()>maxKeyLength)?key.length():maxKeyLength;\n  \t\t\tmaxValueLength =  (value.length()>maxValueLength)?value.length():maxValueLength;\n  \t\t}\n  \t\t\n  \t\tint maxLength=maxKeyLength+maxValueLength+2+mark.length();\n  \t\tif(maxLength<= title.length()){\n  \t\t\tmaxLength = title.length()+8;\n  \t\t}\n  \t\titor = map.entrySet().iterator();\n  \t\t//合并key和value，并找出长度最大的字符串\n  \t\twhile(itor.hasNext()){\n  \t\t\tEntry<String, String> entry = itor.next();\n  \t\t\tString key = entry.getKey();\n  \t\t\tString value = entry.getValue();\n  \t\t\tint keyLength = maxKeyLength-key.length();\n  \t\t\tStringBuilder keySb = new StringBuilder(key);\n  \t\t\tfor(int i=0;i<keyLength;i++){\n  \t\t\t\tkeySb.append(\" \");\n  \t\t\t}\n  \t\t\tkey = keySb.toString();\n  \t\t\t\n  \t\t\tString merge = key+\" \"+mark+\" \"+value;\n  \t\t\tmergeList.add(merge);\n  \t\t}\n  \t\tint maxLineLength = 300;//一行显示最大字符数\n  \t\tif(maxLength > maxLineLength){\n  \t\t\tmaxLength = maxLineLength;\n  \t\t}\n  \t\t//拼第一行title\n  \t\tStringBuilder titleSb = new StringBuilder(\"+\");\n  \t\tint halfLength = (maxLength-title.length())/2;\n  \t\tfor(int i=0;i<halfLength;i++){\n  \t\t\ttitleSb.append(\"-\");\n  \t\t}\n  \t\ttitleSb.append(title);\n  \t\tfor(int i=0;i<(maxLength-halfLength-title.length());i++){\n  \t\t\ttitleSb.append(\"-\");\n  \t\t}\n  \t\ttitleSb.append(\"+\\n\");\n  \t\tresult.append(titleSb);\n  \t\t\n  \t\tList<String> changeList = new ArrayList<>();\n  \t\t//调整内容\n  \t\tfor(int i=0;i<mergeList.size();i++){\n  \t\t\tString content = mergeList.get(i);\n  \t\t    if(content.trim().length()>=maxLength){\n  \t\t    \tString[] str = content.split(mark);\n  \t\t    \tString key = str[0];\n  \t\t    \tString value =str[1];\n  \t\t    \tString[] values = getValues(value,maxLength-maxKeyLength-1-mark.length());\n  \t\t    \tfor(int j=0;j<values.length;j++){\n  \t\t    \t\tString s = \"\";\n  \t\t    \t\tif(j > 0){\n  \t\t    \t\t\tStringBuilder keySb = new StringBuilder();\n  \t\t    \t\t\tfor(int k=0;k<key.length()+1;k++){\n  \t\t    \t\t\t\tkeySb.append(\" \");\n  \t\t    \t\t\t}\n  \t\t    \t\t\ts = keySb.toString()+values[j];\n  \t\t    \t\t}else{\n  \t\t    \t\t\ts = key+mark+values[j];\n  \t\t    \t\t}\n  \t\t    \t\t\n  \t\t    \t\tchangeList.add(s);\n  \t\t    \t}\n  \t\t    }else{\n  \t\t    \tchangeList.add(content);\n  \t\t    }\n  \t\t}\n  \t\t\n  \t\t//拼接内容\n  \t\tfor(int i=0;i<changeList.size();i++){\n  \t\t\tStringBuilder contentSb = new StringBuilder(\" |\");\n  \t\t\tString content = changeList.get(i);\n  \t\t\tcontentSb.append(content);\n  \t\t\tint length = maxLength-content.length();\n  \t\t\tfor(int j=0;j<length;j++){\n  \t\t\t\tcontentSb.append(\" \");\n  \t\t\t}\n  \t\t\tcontentSb.append(\"|\\n\");\n  \t\t\tresult.append(contentSb);\n  \t\t}\n  \t\tStringBuilder endSb = new StringBuilder(\" +\");\n  \t\tfor(int i=0;i<maxLength;i++){\n  \t\t\tendSb.append(\"-\");\n  \t\t}\n  \t\tendSb.append(\"+\\n\");\n  \t\tresult.append(endSb);\n  \t\treturn result.toString();\n  \t\t\n  \t}\n  \t\n  \tpublic static  <T> boolean isKeyExistIgnoreCase(Map<String,T> map,String key){\n\t\treturn map.containsKey(key.toLowerCase()) || map.containsKey(key.toUpperCase()) || map.containsKey(key);\n\t}\n\t\n\tpublic static <T> T getValueIgnoreCase(Map<String,T> map,String key){\n\t\tT result = map.get(key);\n\t\tif (result == null){\n\t\t\t result = map.get(key.toLowerCase());\n\t\t}\n\t\tif (result == null){\n\t\t\tresult = map.get(key.toUpperCase());\n\t\t}\n\t\treturn result;\n\t}\n\t\n\tpublic static Process exeCmdByOs(String cmd) throws IOException{\n\t\tProcess process = null;\n\t\t\n\t\tRuntime runtime = Runtime.getRuntime();\n\t\t\n\t\tString osName = System.getProperty(\"os.name\");\n\t\t\n\t\tif(osName.toLowerCase().startsWith(\"win\")){\n\t\t\tprocess = runtime.exec((new String[]{\"cmd\",\"/C\",cmd}));\n\t\t}else{\n\t\t\tprocess = runtime.exec((new String[]{\"sh\",\"-c\",cmd}));\n\t\t}\n\t\treturn process;\n\t}\n  \t\n  \tprivate static String[] getValues(String value, int maxValueLength) {\n  \t\tint length = value.length()/maxValueLength;\n  \t\tif(value.length()%maxValueLength>0){\n  \t\t\tlength+=1;\n  \t\t}\n  \t\tString[] result = new String[length];\n  \t\tfor(int i=0;i<length-1;i++){\n  \t\t\tString str = value.substring(i*maxValueLength,i*maxValueLength+maxValueLength);\n  \t\t\tresult[i] = str;\n  \t\t}\n  \t\tString str = value.substring((length-1)*maxValueLength,value.length());\n  \t\tresult[length-1] = str;\n\t\treturn result;\n\t}\n  \t\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/DataNode.java",
    "content": "package io.mycat.util.dataMigrator;\r\n\r\n/**\r\n * 数据节点，精确到库名称\r\n * @author haonan108\r\n *\r\n */\r\npublic class DataNode{\r\n\r\n\tprivate String name;\r\n\tprivate String ip;\r\n\tprivate int port;\r\n\tprivate String userName;\r\n\tprivate String pwd;\r\n\tprivate String db;\r\n\tprivate String dbType;\r\n\tprivate int index;\r\n\t\r\n\tpublic DataNode(String name,String ip, int port, String userName, String pwd, String db,String dbType,int index) {\r\n\t\tsuper();\r\n\t\tthis.name = name;\r\n\t\tthis.ip = ip;\r\n\t\tthis.port = port;\r\n\t\tthis.userName = userName;\r\n\t\tthis.pwd = pwd;\r\n\t\tthis.db = db;\r\n\t\tthis.index = index;\r\n\t\tthis.dbType = dbType;\r\n\t}\r\n\r\n\tpublic String getIp() {\r\n\t\treturn ip;\r\n\t}\r\n\r\n\tpublic void setIp(String ip) {\r\n\t\tthis.ip = ip;\r\n\t}\r\n\r\n\tpublic int getPort() {\r\n\t\treturn port;\r\n\t}\r\n\r\n\tpublic void setPort(int port) {\r\n\t\tthis.port = port;\r\n\t}\r\n\r\n\tpublic String getUserName() {\r\n\t\treturn userName;\r\n\t}\r\n\r\n\tpublic void setUserName(String userName) {\r\n\t\tthis.userName = userName;\r\n\t}\r\n\r\n\tpublic String getPwd() {\r\n\t\treturn pwd;\r\n\t}\r\n\r\n\tpublic void setPwd(String pwd) {\r\n\t\tthis.pwd = pwd;\r\n\t}\r\n\r\n\tpublic String getDb() {\r\n\t\treturn db;\r\n\t}\r\n\r\n\tpublic void setDb(String db) {\r\n\t\tthis.db = db;\r\n\t}\r\n\r\n\tpublic String getDbType() {\r\n\t\treturn dbType;\r\n\t}\r\n\r\n\tpublic void setDbType(String dbType) {\r\n\t\tthis.dbType = dbType;\r\n\t}\r\n\t\r\n\tpublic String getName() {\r\n\t\treturn name;\r\n\t}\r\n\r\n\tpublic void setName(String name) {\r\n\t\tthis.name = name;\r\n\t}\r\n\t\r\n\t//暂时只提供mysql驱动\r\n\tpublic String getUrl(){\r\n\t\treturn \"jdbc:mysql://\"+ip+\":\"+port+\"/\"+db;\r\n\t}\r\n\t\r\n\tpublic int getIndex() {\r\n\t\treturn index;\r\n\t}\r\n\r\n\tpublic void setIndex(int index) {\r\n\t\tthis.index = index;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String toString(){\r\n\t\treturn this.name;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean equals(Object o){\r\n\t\tif(o == null) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tif (this == o) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\t\r\n\t\tif(o instanceof DataNode){\r\n\t\t\tDataNode other = (DataNode)o;\r\n\t\t\tif (other.getUrl().equals(this.getUrl())){\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\t\r\n\t@Override\r\n\tpublic int hashCode(){\r\n\t\treturn this.getUrl().hashCode();\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/DataNodeClearGroup.java",
    "content": "package io.mycat.util.dataMigrator;\r\n\r\nimport java.io.File;\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\n/**\r\n * 数据节点按照主机ip进行分组\r\n * @author haonan108\r\n *\r\n */\r\npublic class DataNodeClearGroup {\r\n\r\n\tprivate String ip;\r\n\tprivate Map<File,DataNode>  tempFiles = new HashMap<>();\r\n\tprivate TableMigrateInfo tableInfo;\r\n\t\r\n\tpublic DataNodeClearGroup(String ip, TableMigrateInfo tableInfo) {\r\n\t\tsuper();\r\n\t\tthis.ip = ip;\r\n\t\tthis.tableInfo = tableInfo;\r\n\t}\r\n\tpublic String getIp() {\r\n\t\treturn ip;\r\n\t}\r\n\tpublic void setIp(String ip) {\r\n\t\tthis.ip = ip;\r\n\t}\r\n\tpublic Map<File,DataNode> getTempFiles() {\r\n\t\treturn tempFiles;\r\n\t}\r\n\tpublic void setTempFiles(Map<File,DataNode> tempFiles) {\r\n\t\tthis.tempFiles = tempFiles;\r\n\t}\r\n\tpublic TableMigrateInfo getTableInfo() {\r\n\t\treturn tableInfo;\r\n\t}\r\n\tpublic void setTableInfo(TableMigrateInfo tableInfo) {\r\n\t\tthis.tableInfo = tableInfo;\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/DataNodeMigrateInfo.java",
    "content": "package io.mycat.util.dataMigrator;\r\n\r\nimport java.io.File;\r\n\r\n/**\r\n * 数据迁移时数据节点间迁移信息\r\n * @author haonan108\r\n *\r\n */\r\npublic class DataNodeMigrateInfo {\r\n\r\n\tprivate DataNode src;\r\n\tprivate DataNode target;\r\n\tprivate File tempFile;\r\n\tprivate long size;\r\n\tprivate TableMigrateInfo table;\r\n\t\r\n\tpublic DataNodeMigrateInfo(TableMigrateInfo table, DataNode src, DataNode target, File tempFile, long size) {\r\n\t\tsuper();\r\n\t\tthis.table = table;\r\n\t\tthis.src = src;\r\n\t\tthis.target = target;\r\n\t\tthis.tempFile = tempFile;\r\n\t\tthis.size = size;\r\n\t}\r\n\t\r\n\tpublic TableMigrateInfo getTable() {\r\n\t\treturn table;\r\n\t}\r\n\r\n\tpublic void setTable(TableMigrateInfo table) {\r\n\t\tthis.table = table;\r\n\t}\r\n\r\n\tpublic DataNode getSrc() {\r\n\t\treturn src;\r\n\t}\r\n\tpublic void setSrc(DataNode src) {\r\n\t\tthis.src = src;\r\n\t}\r\n\tpublic DataNode getTarget() {\r\n\t\treturn target;\r\n\t}\r\n\tpublic void setTarget(DataNode target) {\r\n\t\tthis.target = target;\r\n\t}\r\n\tpublic File getTempFile() {\r\n\t\treturn tempFile;\r\n\t}\r\n\tpublic void setTempFile(File tempFile) {\r\n\t\tthis.tempFile = tempFile;\r\n\t}\r\n\tpublic long getSize() {\r\n\t\treturn size;\r\n\t}\r\n\tpublic void setSize(long size) {\r\n\t\tthis.size = size;\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/MigratorConditonFilesMaker.java",
    "content": "package io.mycat.util.dataMigrator;\n\nimport io.mycat.util.StringUtil;\nimport java.io.File;\nimport java.io.IOException;\nimport java.sql.Connection;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.util.JdbcUtils;\n\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\nimport io.mycat.util.CollectionUtil;\n\n/**\n * 对具体某个节点重新路由 生成导出数据所依赖的中间文件\n * @author haonan108\n */\npublic class MigratorConditonFilesMaker implements Runnable{\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(MigratorConditonFilesMaker.class);\n\tprivate DataNode srcDn;\n\tprivate List<DataNode> newDnList;\n\tprivate String column;\n\tprivate String tableName;\n\tprivate AbstractPartitionAlgorithm alg;\n\tprivate String tempFileDir;\n\tprivate TableMigrateInfo tableInfo;\n\tprivate int newDnSize;\n\tprivate int pageSize;\n\t\n\tprivate Map<DataNode,File> files = new HashMap<>();\n\t\n\tMap<DataNode,StringBuilder> map = new HashMap<>();//存放节点发生变化的拆分字段字符串数据 key:dn索引 value 拆分字段值,以逗号分隔\n\t\n\tpublic MigratorConditonFilesMaker(TableMigrateInfo tableInfo,DataNode srcDn,String tempFileDir, int pageSize){\n\t\tthis.tableInfo = tableInfo;\n\t\tthis.tempFileDir = tempFileDir;\n\t\tthis.srcDn = srcDn;\n\t\tthis.newDnList = tableInfo.getNewDataNodes();\n\t\tthis.column = tableInfo.getColumn();\n\t\tthis.tableName = tableInfo.getTableName();\n\t\tthis.alg = tableInfo.getNewRuleAlgorithm();\n\t\tthis.newDnSize = newDnList.size();\n\t\tthis.pageSize = pageSize;\n\t}\n\t\n\t@Override\n\tpublic void run() {\n\t\tif(tableInfo.isError()) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tlong[] count = new long[newDnSize];\n    \tint page=0;\n    \tList<Map<String, Object>> list=null;\n\t\t\n    \tConnection con = null;\n\t\ttry {\n\t\t\tcon = DataMigratorUtil.getMysqlConnection(srcDn);\n\t\t\t//创建空的中间临时文件\n\t\t\tcreateTempFiles();\n\t\t\t\n\t\t\t//暂时只实现mysql的分页查询\n\t\t\tlist = DataMigratorUtil.executeQuery(con, \"select \" \n\t\t\t        + column+ \" from \" + tableName + \" limit ?,?\", page++ * pageSize,\n\t\t\t        pageSize);\n\t\t\tint total = 0; //该节点表总数据量\n\t\t\t\n\t\t\twhile (!CollectionUtil.isEmpty(list)) {\n\t\t\t\tif(tableInfo.isError()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tflushData(false);\n    \t\t\tfor(int i=0,l=list.size();i<l;i++){\n    \t\t\t\tMap<String, Object> sf=list.get(i);\n\t\t\t\t\tObject objFieldVal = sf.get(column);\n\t\t\t\t\tString filedVal = objFieldVal.toString();\n\t\t\t\t\tif (objFieldVal instanceof  String){\n\t\t\t\t\t\tfiledVal = \"'\"+filedVal+\"'\";\n\t\t\t\t\t}\n    \t\t\t\tInteger newIndex=alg.calculate(StringUtil.removeBackquote(objFieldVal.toString()));\n    \t\t\t\ttotal++;\n    \t\t\t\tDataNode newDn = newDnList.get(newIndex);\n    \t\t\t\tif(!srcDn.equals(newDn)){\n    \t\t\t\t\tcount[newIndex]++;\n    \t\t\t\t\tmap.get(newDn).append(filedVal+\",\");\n    \t\t\t\t}\n    \t\t\t}\n    \t\t\tlist = DataMigratorUtil.executeQuery(con, \"select \"\n                        + column + \" from \" + tableName + \" limit ?,?\", page++ * pageSize,\n                        pageSize);\n    \t\t}\n\t\t\tflushData(true);\n\t\t\tstatisticalData(total,count);\n\t\t} catch (Exception e) {\n\t\t\t//发生错误，终止此拆分表所有节点线程任务，记录错误信息，退出此拆分表迁移任务\n\t\t\tString message = \"[\"+tableInfo.getSchemaName()+\":\"+tableName+\"]  src dataNode: \"+srcDn.getUrl()+\n\t\t\t\t\t\" prepare temp files is failed! this table's migrator will exit! \"+e.getMessage();\n\t\t\ttableInfo.setError(true);\n\t\t\ttableInfo.setErrMessage(message);\n\t\t\tSystem.out.println(message);\n\t\t\tLOGGER.error(message, e);\n\t\t}finally{\n\t\t\tJdbcUtils.close(con);\n\t\t}\n\t}\n\t\n\t//创建中间临时文件\n\tprivate void createTempFiles() throws IOException{\n\t\tFile parentFile = createDirIfNotExist();\n\t\tfor(DataNode dn:newDnList){\n\t\t\tif(!srcDn.equals(dn)){\n\t\t\t\tmap.put(dn, new StringBuilder());\n\t\t\t\tcreateTempFile(parentFile,dn);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t//中间临时文件 格式: srcDnName-targetDnName.txt   中间文件存在的话会被清除\n\tprivate void createTempFile(File parentFile, DataNode dn) throws IOException {\n\t\tFile f = new File(parentFile,srcDn.getName()+\"(old)\"+\"-\"+dn.getName()+\"(new).txt\");\n\t\tif(f.exists()){\n\t\t\tf.delete();\n\t\t}\n\t\tf.createNewFile();\n\t\tfiles.put(dn, f);\n\t}\n\t\n\t//统计各节点数据迁移信息,并移除空文件\n\tprivate void statisticalData(int total, long[] count){\n\t\ttableInfo.getSize().addAndGet(total);\n\t\tList<DataNodeMigrateInfo> list = tableInfo.getDataNodesDetail();\n\t\tList<Long> sizeList = new ArrayList<>();\n\t\tfor(int i=0;i<count.length;i++){\n\t\t\tlong c = count[i];\n\t\t\tsizeList.add(c);\n\t\t\tDataNode targetDn = newDnList.get(i);\n\t\t\tif(count[i]>0){\n\t\t\t\tDataNodeMigrateInfo info  =new DataNodeMigrateInfo(tableInfo,srcDn, targetDn, files.get(targetDn), c);\n\t\t\t\tlist.add(info);\n\t\t\t}else{\n\t\t\t\tFile f = files.get(targetDn);\n\t\t\t\tif(f != null && f.exists()){\n\t\t\t\t\tf.delete();\n\t\t\t\t}\n\t\t\t\tfiles.remove(targetDn);\n\t\t\t}\n\t\t}\n\t\tMap<String, String> map = tableInfo.getDnMigrateSize();\n\t\tmap.put(srcDn.getName()+\"[\"+total+\"]\", sizeList.toString());\n\t}\n\t\n\t//将迁移字段值写入中间文件,数据超过1024或者要求强制才写入，避免重复打开关闭写入文件\n\tprivate void flushData(boolean isForce) throws IOException {\n\t\tfor(DataNode dn:newDnList){\n\t\t\tStringBuilder sb = map.get(dn);\n\t\t\tif(sb == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif((isForce || sb.toString().getBytes().length>1024) && sb.length()>0){\n\t\t\t\tString s = sb.toString();\n\t\t\t\tif(isForce){//最后一次将末尾的','截掉\n\t\t\t\t\ts = s.substring(0, s.length()-1);\n\t\t\t\t}\n\t\t\t\tDataMigratorUtil.appendDataToFile(files.get(dn),s);\n\t\t\t\tsb = new StringBuilder();\n\t\t\t\tmap.put(dn, sb);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t//创建中间临时文件父目录\n\tprivate File createDirIfNotExist() {\n\t\tFile f = new File(tempFileDir,tableInfo.getSchemaName()+\"-\"+tableName);\n\t\tif(!f.exists()){\n\t\t\tf.mkdirs();\n\t\t}\n\t\treturn f;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/TableMigrateInfo.java",
    "content": "package io.mycat.util.dataMigrator;\r\n\r\nimport java.sql.Connection;\r\nimport java.sql.SQLException;\r\nimport java.util.ArrayList;\r\nimport java.util.Comparator;\r\nimport java.util.LinkedHashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.TreeMap;\r\nimport java.util.concurrent.atomic.AtomicLong;\r\n\r\nimport com.alibaba.druid.util.JdbcUtils;\r\n\r\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\r\n\r\n\r\n/**\r\n * 表迁移信息，包括:\r\n * 表名、迁移前后的数据节点、表数据量、迁移前后数据分布对比\r\n * @author haonan108\r\n *\r\n */\r\n\r\npublic class TableMigrateInfo {\r\n\r\n\tprivate String schemaName;\r\n\tprivate String tableName;\r\n\tprivate List<DataNode> oldDataNodes;\r\n\tprivate List<DataNode> newDataNodes;\r\n\tprivate AtomicLong size = new AtomicLong();\r\n\t\r\n\tprivate List<DataNodeMigrateInfo> dataNodesDetail = new ArrayList<>();//节点间数据迁移详细信息\r\n\t\r\n\tprivate AbstractPartitionAlgorithm newRuleAlgorithm;\r\n\tprivate String column;\r\n\t\r\n\tprivate boolean isExpantion; //true:扩容 false：缩容\r\n\t\r\n\tprivate volatile boolean isError; \r\n\t\r\n    private StringBuffer errMessage = new StringBuffer();\r\n    \r\n    private String tableStructure = \"\"; //记录建表信息，迁移后的节点表不存在的话自动建表\r\n    \r\n    private Map<String,String> dnMigrateSize;\r\n\t\r\n\tpublic TableMigrateInfo(String schemaName, String tableName, List<DataNode> oldDataNodes,\r\n\t\t\tList<DataNode> newDataNodes, AbstractPartitionAlgorithm newRuleAlgorithm, String column) {\r\n\t\tsuper();\r\n\t\tthis.schemaName = schemaName;\r\n\t\tthis.tableName = tableName;\r\n\t\tthis.oldDataNodes = oldDataNodes;\r\n\t\tthis.newDataNodes = newDataNodes;\r\n\t\tthis.newRuleAlgorithm = newRuleAlgorithm;\r\n\t\tthis.column = column;\r\n\t\tif(newDataNodes.size()>oldDataNodes.size()){\r\n\t\t\tisExpantion = true;\r\n\t\t}else{\r\n\t\t\tisExpantion = false;\r\n\t\t}\r\n\t\tdnMigrateSize = new TreeMap<>(new Comparator<String>() {\r\n\t\t\t@Override\r\n\t\t\tpublic int compare(String o1, String o2) {\r\n\t\t\t\treturn o1.compareTo(o2);\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\t\r\n\t//读取表结构\r\n\tpublic  void setTableStructure() throws SQLException{\r\n\t\tDataNode dn = this.getOldDataNodes().get(0);\r\n\t\tConnection con = null;\r\n\t\ttry {\r\n\t\t\tcon = DataMigratorUtil.getMysqlConnection(dn);\r\n\t\t\tList<Map<String, Object>> list = DataMigratorUtil.executeQuery(con, \"show create table \"+tableName);\r\n\t\t\tMap<String, Object> m  = list.get(0);\r\n\t\t\tString str = m.get(\"Create Table\").toString();\r\n\t\t\tstr = str.replaceAll(\"CREATE TABLE\", \"Create Table if not exists\");\r\n\t\t\tsetTableStructure(str);\r\n\t\t} catch (SQLException e) {\r\n\t\t\tthrow e;\r\n\t\t}finally {\r\n\t\t\tJdbcUtils.close(con);\r\n\t\t}\r\n\t}\r\n\t\r\n\t//缩容后，找出被移除的节点\r\n\tpublic List<DataNode> getRemovedDataNodes(){\r\n\t\tList<DataNode> list = new ArrayList<>();\r\n\t\tlist.addAll(oldDataNodes);\r\n\t\tlist.removeAll(newDataNodes);\r\n\t\treturn list;\r\n\t}\r\n\t\r\n\t//扩容后，找出除旧节点以外新增加的节点\r\n\tpublic List<DataNode> getNewAddDataNodes(){\r\n\t\tList<DataNode> list = new ArrayList<>();\r\n\t\tlist.addAll(newDataNodes);\r\n\t\tlist.removeAll(oldDataNodes);\r\n\t\treturn list;\r\n\t}\r\n\t\r\n\t//对新增的节点创建表：create table if not exists \r\n\tpublic void createTableToNewDataNodes() throws SQLException{\r\n\t\tif(this.isExpantion){\r\n\t\t\tList<DataNode> newDataNodes = getNewAddDataNodes();\r\n\t\t\tfor(DataNode dn:newDataNodes){\r\n\t\t\t\tDataMigratorUtil.createTable(dn, this.tableStructure);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\t\r\n\t//打印迁移信息\r\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\r\n\tpublic void printMigrateInfo(){\r\n\t\tMap<String,String> map = new LinkedHashMap();\r\n\t\tmap.put(\"tableSize\", size.get()+\"\");\r\n\t\tmap.put(\"migrate before\", oldDataNodes.toString());\r\n\t\tmap.put(\"migrate after\", newDataNodes.toString());\r\n\t\tmap.put(\"rule function\", newRuleAlgorithm.getClass().getSimpleName());\r\n\t\tString title = getSchemaAndTableName()+\" migrate info\";\r\n\t\tSystem.out.println(DataMigratorUtil.printMigrateInfo(title, map, \"=\"));\r\n\t}\r\n\t\r\n\tpublic void printMigrateSchedule(){\r\n\t\tString title = getSchemaAndTableName()+\" migrate schedule\";\r\n\t\tSystem.out.println(DataMigratorUtil.printMigrateInfo(title, dnMigrateSize, \"->\"));\r\n\t}\r\n\t\r\n\t/**\r\n\t * 是否为扩容，true：扩容，false：缩容\r\n\t * @return\r\n\t */\r\n\tpublic boolean isExpantion(){\r\n\t\treturn isExpantion;\r\n\t}\r\n\r\n\tpublic List<DataNodeMigrateInfo> getDataNodesDetail() {\r\n\t\treturn dataNodesDetail;\r\n\t}\r\n\r\n\tpublic void setDataNodesDetail(List<DataNodeMigrateInfo> dataNodesDetail) {\r\n\t\tthis.dataNodesDetail = dataNodesDetail;\r\n\t}\r\n\r\n\tpublic String getSchemaName() {\r\n\t\treturn schemaName;\r\n\t}\r\n\r\n\tpublic void setSchemaName(String schemaName) {\r\n\t\tthis.schemaName = schemaName;\r\n\t}\r\n\r\n\tpublic String getTableName() {\r\n\t\treturn tableName;\r\n\t}\r\n\r\n\tpublic void setTableName(String tableName) {\r\n\t\tthis.tableName = tableName;\r\n\t}\r\n\r\n\tpublic List<DataNode> getOldDataNodes() {\r\n\t\treturn oldDataNodes;\r\n\t}\r\n\r\n\tpublic void setOldDataNodes(List<DataNode> oldDataNodes) {\r\n\t\tthis.oldDataNodes = oldDataNodes;\r\n\t}\r\n\r\n\tpublic List<DataNode> getNewDataNodes() {\r\n\t\treturn newDataNodes;\r\n\t}\r\n\r\n\tpublic void setNewDataNodes(List<DataNode> newDataNodes) {\r\n\t\tthis.newDataNodes = newDataNodes;\r\n\t}\r\n\r\n\tpublic AbstractPartitionAlgorithm getNewRuleAlgorithm() {\r\n\t\treturn newRuleAlgorithm;\r\n\t}\r\n\r\n\tpublic void setNewRuleAlgorithm(AbstractPartitionAlgorithm newRuleAlgorithm) {\r\n\t\tthis.newRuleAlgorithm = newRuleAlgorithm;\r\n\t}\r\n\r\n\tpublic String getColumn() {\r\n\t\treturn column;\r\n\t}\r\n\r\n\tpublic void setColumn(String column) {\r\n\t\tthis.column = column;\r\n\t}\r\n\t\r\n\tpublic String getSchemaAndTableName(){\r\n\t\treturn \"[\"+schemaName+\":\"+tableName+\"]\";\r\n\t}\r\n\r\n\tpublic StringBuffer getErrMessage() {\r\n\t\treturn errMessage;\r\n\t}\r\n\r\n\tpublic void setErrMessage(String errMessage) {\r\n\t\tthis.errMessage = new StringBuffer(errMessage);\r\n\t}\r\n\r\n\tpublic AtomicLong getSize() {\r\n\t\treturn size;\r\n\t}\r\n\t\r\n\tpublic void setSize(long size){\r\n\t\tthis.size = new AtomicLong(size);\r\n\t}\r\n\r\n\tpublic boolean isError() {\r\n\t\treturn isError;\r\n\t}\r\n\r\n\tpublic void setError(boolean isError) {\r\n\t\tthis.isError = isError;\r\n\t}\r\n\r\n\tpublic String getTableStructure() {\r\n\t\treturn tableStructure;\r\n\t}\r\n\r\n\tpublic void setTableStructure(String tableStructure) {\r\n\t\tthis.tableStructure = tableStructure;\r\n\t}\r\n\r\n\tpublic void setSize(AtomicLong size) {\r\n\t\tthis.size = size;\r\n\t}\r\n\r\n\tpublic Map<String, String> getDnMigrateSize() {\r\n\t\treturn dnMigrateSize;\r\n\t}\r\n\r\n\tpublic void setDnMigrateSize(Map<String, String> dnMigrateSize) {\r\n\t\tthis.dnMigrateSize = dnMigrateSize;\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/util/dataMigrator/dataIOImpl/MysqlDataIO.java",
    "content": "package io.mycat.util.dataMigrator.dataIOImpl;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.nio.charset.Charset;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.mycat.util.dataMigrator.DataIO;\nimport io.mycat.util.dataMigrator.DataMigrator;\nimport io.mycat.util.dataMigrator.DataMigratorUtil;\nimport io.mycat.util.dataMigrator.DataNode;\nimport io.mycat.util.dataMigrator.TableMigrateInfo;\nimport io.mycat.util.exception.DataMigratorException;\n\n/**\n * mysql导入导出实现类\n * @author haonan108\n *\n */\npublic class MysqlDataIO implements DataIO{\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(MysqlDataIO.class);\n\n\tprivate String mysqlBin;\n\tprivate int cmdLength;\n\tprivate String charset;\n\tprivate boolean gtidPurged = true;\n\n\tpublic MysqlDataIO(){\n\t\tcmdLength = DataMigrator.margs.getCmdLength();\n\t\tcharset = DataMigrator.margs.getCharSet();\n\t\tmysqlBin  = DataMigrator.margs.getMysqlBin();\n\t\tgtidPurged = DataMigrator.margs.isGtidPurged();\n\t}\n\n    public boolean isWindows() {\n\t    try {\n            return System.getProperties().getProperty(\"os.name\", \"WINDOWS\").toUpperCase().indexOf(\"WINDOWS\") != -1;\n        }catch (Throwable e){\n\t        LOGGER.error(\"\",e);\n\t        return true;\n        }\n    }\n\n\t@Override\n\tpublic void importData(TableMigrateInfo table,DataNode dn,String tableName, File file) throws IOException, InterruptedException {\n\t\tString ip = dn.getIp();\n\t\tint port = dn.getPort();\n\t\tString user = dn.getUserName();\n\t\tString pwd = dn.getPwd();\n\t\tString db = dn.getDb();\n\n//\t\tString loadData =\"?mysql -h? -P? -u? -p? -D? --local-infile=1 -e \\\"load data local infile '?' replace into table ? CHARACTER SET '?' FIELDS TERMINATED BY ','  LINES TERMINATED BY '\\\\r\\\\n'\\\"\";\n\t\tString loadData = \"?mysql -h? -P? -u? -p? -D?  -f --default-character-set=? -e \\\"source ?\\\"\";\n\t\tloadData = DataMigratorUtil.paramsAssignment(loadData,\"?\",mysqlBin,ip,port,user,pwd,db,charset,file.getAbsolutePath());\n\t\tLOGGER.info(table.getSchemaAndTableName()+\" \"+loadData);\n\t\tProcess process = DataMigratorUtil.exeCmdByOs(loadData);\n\n\t\t//获取错误信息\n\n\t\tInputStreamReader in = new InputStreamReader(process.getErrorStream(),isWindows()?\"GBK\": Charset.defaultCharset().name());\n\t\tBufferedReader br = new BufferedReader(in);\n\t\tString errMessage = null;\n        while ((errMessage = br.readLine()) != null) {\n            if(errMessage.trim().toLowerCase().contains(\"err\")){\n            \tSystem.out.println(errMessage+\" -> \"+loadData);\n            \tthrow new DataMigratorException(errMessage+\" -> \"+loadData);\n            }\n        }\n\n\t\tprocess.waitFor();\n\t}\n\n\t@Override\n\tpublic File exportData(TableMigrateInfo table,DataNode dn, String tableName, File export, File condition) throws IOException, InterruptedException {\n\t\tString ip = dn.getIp();\n\t\tint port = dn.getPort();\n\t\tString user = dn.getUserName();\n\t\tString pwd = dn.getPwd();\n\t\tString db = dn.getDb();\n\n//\t\tString mysqlDump = \"?mysqldump -h? -P? -u? -p? ? ?  --no-create-info --default-character-set=? \"\n//\t\t\t\t+ \"--add-locks=false --tab='?' --fields-terminated-by=',' --lines-terminated-by='\\\\r\\\\n' --where='? in(?)'\";\n\t\t//由于mysqldump导出csv格式文件只能导出到本地，暂时替换成导出insert形式的文件\n\t\tString mysqlDump = \"?mysqldump -h? -P? -u? -p? ? ?  --compact --no-create-info --default-character-set=? --add-locks=false --where=\\\"? in (#)\\\" --result-file=\\\"?\\\" \";\n\t\tif (gtidPurged){\n\t\t\tmysqlDump += \" --set-gtid-purged=ON \";\n\t\t}else {\n\t\t\tmysqlDump += \" --set-gtid-purged=OFF \";\n\t\t}\n\t\tString fileName = condition.getName();\n\t\tFile exportPath = new File(export,fileName.substring(0, fileName.indexOf(\".txt\")));\n\t\tif(!exportPath.exists()){\n\t\t\texportPath.mkdirs();\n\t\t}\n\t\tFile exportFile = new File(exportPath,tableName.toLowerCase()+\".txt\");\n\t\t//拼接mysqldump命令，不拼接where条件：--where=id in(?)\n\t\tmysqlDump = DataMigratorUtil.paramsAssignment(mysqlDump,\"?\",mysqlBin,ip,port,user,pwd,db,tableName,charset,table.getColumn(),exportFile);\n\n\t\tString data = \"\";\n\t\t//由于操作系统对命令行长度的限制，导出过程被拆分成多次，最后需要将导出的数据文件合并\n\t\tFile mergedFile = new File(exportPath,tableName.toLowerCase()+\".sql\");\n\t\tif(!mergedFile.exists()){\n\t\t\tmergedFile.createNewFile();\n\t\t}\n\t\tint offset = 0;\n\t\twhile((data=DataMigratorUtil.readData(condition,offset,cmdLength)).length()>0){\n\t\t\toffset += data.getBytes().length;\n\t\t\tif(data.startsWith(\",\")){\n\t\t\t\tdata = data.substring(1, data.length());\n\t\t\t}\n\t\t\tif(data.endsWith(\",\")){\n\t\t\t\tdata = data.substring(0,data.length()-1);\n\t\t\t}\n\t\t\tString mysqlDumpCmd = DataMigratorUtil.paramsAssignment(mysqlDump,\"#\",data);\n\t\t\tLOGGER.info(table.getSchemaAndTableName()+mysqlDump);\n\t\t\tLOGGER.debug(table.getSchemaAndTableName()+\" \"+mysqlDumpCmd);\n\n\t\t\tProcess process = DataMigratorUtil.exeCmdByOs(mysqlDumpCmd);\n\t\t\t//获取错误信息\n\t\t\tInputStreamReader in = new InputStreamReader(process.getErrorStream());\n\t\t\tBufferedReader br = new BufferedReader(in);\n\t\t\tString errMessage = null;\n\t        while ((errMessage = br.readLine()) != null) {\n\t            if(errMessage.trim().toLowerCase().contains(\"err\")){\n\t            \tSystem.out.println(errMessage+\" -> \"+mysqlDump);\n\t            \tthrow new DataMigratorException(errMessage+\" -> \"+mysqlDump);\n\t            }else{\n\t            \tLOGGER.info(table.getSchemaAndTableName()+mysqlDump+\" exe info:\"+errMessage);\n\t            }\n\t        }\n\t\t\tprocess.waitFor();\n\n\t\t\t//合并文件\n\t\t\tDataMigratorUtil.mergeFiles(mergedFile, exportFile);\n\t\t\tif(exportFile.exists()){\n\t\t\t\texportFile.delete();\n\t\t\t}\n\t\t}\n\t\treturn mergedFile;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/exception/DataMigratorException.java",
    "content": "package io.mycat.util.exception;\r\n/**\r\n * \r\n * @author haonan108\r\n *\r\n */\r\npublic class DataMigratorException  extends RuntimeException{\r\n\r\n\tprivate static final long serialVersionUID = -6706826479467595980L;\r\n\r\n\tpublic DataMigratorException() {\r\n\t\tsuper();\r\n\t\t\r\n\t}\r\n\r\n\tpublic DataMigratorException(String message, Throwable cause,\r\n\t\t\tboolean enableSuppression, boolean writableStackTrace) {\r\n\t\tsuper(message, cause, enableSuppression, writableStackTrace);\r\n\t\t\r\n\t}\r\n\r\n\tpublic DataMigratorException(String message, Throwable cause) {\r\n\t\tsuper(message, cause);\r\n\t\t\r\n\t}\r\n\r\n\tpublic DataMigratorException(String message) {\r\n\t\tsuper(message);\r\n\t\t\r\n\t}\r\n\r\n\tpublic DataMigratorException(Throwable cause) {\r\n\t\tsuper(cause);\r\n\t\t\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/io/mycat/util/exception/ErrorPacketException.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util.exception;\n\n/**\n * @author mycat\n */\npublic class ErrorPacketException extends RuntimeException {\n    private static final long serialVersionUID = -2692093550257870555L;\n\n    public ErrorPacketException() {\n        super();\n    }\n\n    public ErrorPacketException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public ErrorPacketException(String message) {\n        super(message);\n    }\n\n    public ErrorPacketException(Throwable cause) {\n        super(cause);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/exception/HeartbeatException.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util.exception;\n\n/**\n * @author mycat\n */\npublic class HeartbeatException extends RuntimeException {\n    private static final long serialVersionUID = 7639414445868741580L;\n\n    public HeartbeatException() {\n        super();\n    }\n\n    public HeartbeatException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public HeartbeatException(String message) {\n        super(message);\n    }\n\n    public HeartbeatException(Throwable cause) {\n        super(cause);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/exception/MurmurHashException.java",
    "content": "package io.mycat.util.exception;\n\npublic class MurmurHashException extends RuntimeException{\n\n\t/**\n\t * \n\t */\n\tprivate static final long serialVersionUID = -8040964553491203562L;\n\n\tpublic MurmurHashException() {\n\t\tsuper();\n\t\t\n\t}\n\n\tpublic MurmurHashException(String message, Throwable cause,\n\t\t\tboolean enableSuppression, boolean writableStackTrace) {\n\t\tsuper(message, cause, enableSuppression, writableStackTrace);\n\t\t\n\t}\n\n\tpublic MurmurHashException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t\t\n\t}\n\n\tpublic MurmurHashException(String message) {\n\t\tsuper(message);\n\t\t\n\t}\n\n\tpublic MurmurHashException(Throwable cause) {\n\t\tsuper(cause);\n\t\t\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/exception/RehashException.java",
    "content": "package io.mycat.util.exception;\n\npublic class RehashException extends RuntimeException{\n\n\t/**\n\t * \n\t */\n\tprivate static final long serialVersionUID = 7562724429239862825L;\n\n\tpublic RehashException() {\n\t\tsuper();\n\t\t\n\t}\n\n\tpublic RehashException(String message, Throwable cause,\n\t\t\tboolean enableSuppression, boolean writableStackTrace) {\n\t\tsuper(message, cause, enableSuppression, writableStackTrace);\n\t\t\n\t}\n\n\tpublic RehashException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t\t\n\t}\n\n\tpublic RehashException(String message) {\n\t\tsuper(message);\n\t\t\n\t}\n\n\tpublic RehashException(Throwable cause) {\n\t\tsuper(cause);\n\t\t\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/exception/UnknownCharsetException.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util.exception;\n\n/**\n * 未知字符集异常\n * \n * @author mycat\n */\npublic class UnknownCharsetException extends RuntimeException {\n    private static final long serialVersionUID = 552833416065882969L;\n\n    public UnknownCharsetException() {\n        super();\n    }\n\n    public UnknownCharsetException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public UnknownCharsetException(String message) {\n        super(message);\n    }\n\n    public UnknownCharsetException(Throwable cause) {\n        super(cause);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/exception/UnknownDataNodeException.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util.exception;\n\n/**\n * @author mycat\n */\npublic class UnknownDataNodeException extends RuntimeException {\n    private static final long serialVersionUID = -3752985849571697432L;\n\n    public UnknownDataNodeException() {\n        super();\n    }\n\n    public UnknownDataNodeException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public UnknownDataNodeException(String message) {\n        super(message);\n    }\n\n    public UnknownDataNodeException(Throwable cause) {\n        super(cause);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/exception/UnknownPacketException.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util.exception;\n\n/**\n * 未知数据包异常\n * \n * @author mycat\n */\npublic class UnknownPacketException extends RuntimeException {\n    private static final long serialVersionUID = 3152986441780514147L;\n\n    public UnknownPacketException() {\n        super();\n    }\n\n    public UnknownPacketException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public UnknownPacketException(String message) {\n        super(message);\n    }\n\n    public UnknownPacketException(Throwable cause) {\n        super(cause);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/exception/UnknownTxIsolationException.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util.exception;\n\n/**\n * 未知事物隔离级别异常\n * \n * @author mycat\n */\npublic class UnknownTxIsolationException extends RuntimeException {\n    private static final long serialVersionUID = -3911059999308980358L;\n\n    public UnknownTxIsolationException() {\n        super();\n    }\n\n    public UnknownTxIsolationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public UnknownTxIsolationException(String message) {\n        super(message);\n    }\n\n    public UnknownTxIsolationException(Throwable cause) {\n        super(cause);\n    }\n\n}"
  },
  {
    "path": "src/main/java/io/mycat/util/rehasher/HashType.java",
    "content": "package io.mycat.util.rehasher;\n\npublic enum HashType {\n\tMURMUR,MOD;\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/rehasher/RehashCmdArgs.java",
    "content": "package io.mycat.util.rehasher;\n\nimport java.io.BufferedReader;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.mycat.util.StringUtil;\nimport io.mycat.util.cmd.CmdArgs;\n\npublic class RehashCmdArgs {\n\tpublic static final String JDBC_DRIVER=\"jdbcDriver\";\n\tpublic static final String JDBC_URL=\"jdbcUrl\";\n\tpublic static final String HOST=\"host\";\n\tpublic static final String USER=\"user\";\n\tpublic static final String DATABASE=\"database\";\n\tpublic static final String PASSWORD=\"password\";\n\tpublic static final String TABLES_FILE=\"tablesFile\";\n\tpublic static final String SHARDING_FIELD=\"shardingField\";\n\tpublic static final String REHASH_HOSTS_FILE=\"rehashHostsFile\";\n\tpublic static final String HASH_TYPE=\"hashType\";\n\tpublic static final String SEED=\"seed\";\n\tpublic static final String VIRTUAL_BUCKET_TIMES=\"virtualBucketTimes\";\n\tpublic static final String WEIGHT_MAP_FILE=\"weightMapFile\";\n\tpublic static final String REHASH_NODE_DIR=\"rehashNodeDir\";\n\t\n\t\n\tprivate CmdArgs cmdArgs;\n\t\n\tpublic RehashCmdArgs(String[] args){\n\t\tcmdArgs=CmdArgs.getInstance(args);\n\t}\n\t\n\tpublic String getString(String name){\n\t\treturn cmdArgs.getString(name);\n\t}\n\t\n\tpublic String getJdbcDriver(){\n\t\treturn getString(JDBC_DRIVER);\n\t}\n\tpublic String getJdbcUrl(){\n\t\treturn getString(JDBC_URL);\n\t}\n\t/**\n\t * including host and port, which format is host:port\n\t * @return\n\t */\n\tpublic String getHost(){\n\t\treturn getString(HOST);\n\t}\n\tpublic String getHostName(){\n\t\tString host=getHost();\n\t\treturn host.substring(0,host.indexOf(':'));\n\t}\n\tpublic int getHostPort(){\n\t\tString host=getHost();\n\t\treturn Integer.parseInt(host.substring(host.indexOf(':')+1));\n\t}\n\tpublic String getDatabase(){\n\t\treturn getString(DATABASE);\n\t}\n\tpublic String getHostWithDatabase(){\n\t\treturn getHost()+'/'+getDatabase();\n\t}\n\tpublic String getUser(){\n\t\treturn getString(USER);\n\t}\n\tpublic String getPassword(){\n\t\treturn getString(PASSWORD);\n\t}\n\t\n\tpublic String getTablesFile(){\n\t\treturn getString(TABLES_FILE);\n\t}\n\tpublic String[] getTables() throws IOException{\n\t\treturn readStringArrayFromFile(getTablesFile());\n\t}\n\t\n\tpublic String getShardingField(){\n\t\treturn getString(SHARDING_FIELD);\n\t}\n\t\n\tpublic String getRehashHostsFile(){\n\t\treturn getString(REHASH_HOSTS_FILE);\n\t}\n\tpublic String[] getRehashHosts() throws IOException{\n\t\treturn readStringArrayFromFile(getRehashHostsFile());\n\t}\n\t\n\tpublic HashType getHashType(){\n\t\treturn HashType.valueOf(getString(HASH_TYPE).toUpperCase());\n\t}\n\n\tpublic int getMurmurHashSeed(){\n\t\treturn getIntWithDefaultValue(SEED, 0);\n\t}\n\tpublic int getMurmurHashVirtualBucketTimes(){\n\t\treturn getIntWithDefaultValue(VIRTUAL_BUCKET_TIMES, 160);\n\t}\n\tpublic String getMurmurWeightMapFile(){\n\t\treturn getString(WEIGHT_MAP_FILE);\n\t}\n\t\n\tpublic String getRehashNodeDir(){\n\t\treturn getString(REHASH_NODE_DIR);\n\t}\n\t\n\tprivate int getIntWithDefaultValue(String name,int defaultValue){\n\t\tString val=getString(name);\n\t\tif(StringUtil.isEmpty(val)){\n\t\t\treturn defaultValue;\n\t\t}else{\n\t\t\treturn Integer.parseInt(val);\n\t\t}\n\t}\n\t\n\tprivate String[] readStringArrayFromFile(String file) throws IOException{\n\t\tBufferedReader br=null;\n\t\ttry{\n\t\t\tList<String> tableList=new ArrayList<>();\n\t\t\tbr=new BufferedReader(new InputStreamReader(new FileInputStream(file),\"utf8\"));\n\t\t\tString table=null;\n\t\t\twhile((table=br.readLine())!=null){\n\t\t\t\ttableList.add(table);\n\t\t\t}\n\t\t\treturn tableList.toArray(new String[tableList.size()]);\n\t\t}finally{\n\t\t\tif(br!=null){\n\t\t\t\tbr.close();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/io/mycat/util/rehasher/RehashLauncher.java",
    "content": "package io.mycat.util.rehasher;\n\nimport io.mycat.util.StringUtil;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.pool.DruidDataSource;\nimport com.alibaba.druid.util.JdbcUtils;\n\nimport io.mycat.route.function.AbstractPartitionAlgorithm;\nimport io.mycat.route.function.PartitionByMod;\nimport io.mycat.route.function.PartitionByMurmurHash;\nimport io.mycat.util.CollectionUtil;\nimport io.mycat.util.exception.RehashException;\n\n/**\n * 本工具依赖druid，Mycat已经包含druid，druid配置请查阅相关文档。相关参数请看RehashCmdArgs\n * @author wujingrun\n *\n */\npublic class RehashLauncher {\n\tprivate final class RehashRunner implements Runnable {\n        private final File   output;\n        private final String table;\n        \n        private RehashRunner(File output, String table) {\n            this.output = output;\n            this.table = table;\n        }\n        \n        public void run(){\n        \tint pageSize=500;\n        \tint page=0;\n        \tList<Map<String, Object>> list=null;\n        \t\n        \tint total=0;\n        \tint rehashed=0;\n        \tString hostWithDatabase=args.getHostWithDatabase();\n        \t\n        \tPrintStream ps=null;\n        \ttry {\n        \t\tps=new PrintStream(output);\n        \t\tlist = JdbcUtils.executeQuery(dataSource, \"select \"\n                    + args.getShardingField() + \" from \" + table + \" limit ?,?\", page++ * pageSize,\n                    pageSize);\n                while (!CollectionUtil.isEmpty(list)) {\n        \t\t\tfor(int i=0,l=list.size();i<l;i++){\n        \t\t\t\tMap<String, Object> sf=list.get(i);\n        \t\t\t\tInteger hash=alg.calculate(StringUtil.removeBackquote(sf.get(args.getShardingField()).toString()));\n        \t\t\t\tString host=rehashHosts[hash];\n        \t\t\t\ttotal++;\n        \t\t\t\tif(host.equals(hostWithDatabase)){\n        \t\t\t\t\trehashed++;\n        \t\t\t\t}\n        \t\t\t\tps.println(sf+\"=>\"+host);\n        \t\t\t}\n        \t\t\tlist = JdbcUtils.executeQuery(dataSource, \"select \"\n                        + args.getShardingField() + \" from \" + table + \" limit ?,?\", page++ * pageSize,\n                        pageSize);\n        \t\t}\n        \t\tps.println(\"rehashed ratio:\"+(((double)rehashed)/total));\n        \t} catch (Exception e) {\n        \t\tthrow new RehashException(e);\n        \t}finally{\n        \t\tif(ps!=null){\n        \t\t\tps.close();\n        \t\t}\n        \t\tlatch.countDown();\n        \t}\n        }\n    }\n\n    private RehashCmdArgs args;\n\tprivate DruidDataSource dataSource;\n\tprivate String[] rehashHosts;\n\tprivate AbstractPartitionAlgorithm alg;\n\tprivate ExecutorService executor;\n\tprivate CountDownLatch latch;\n    private static final Logger        LOGGER = LoggerFactory.getLogger(RehashLauncher.class);\n\t\n\tprivate RehashLauncher(String[] args) throws IOException{\n\t\tthis.args=new RehashCmdArgs(args);\n\t\tinitDataSource();\n\t\tthis.rehashHosts=this.args.getRehashHosts();\n\t\tinitHashAlg();\n\t\texecutor=Executors.newCachedThreadPool();\n\t}\n\t\n\tprivate void initHashAlg() throws IOException{\n\t    if (HashType.MURMUR.equals(args.getHashType())) {\n\t        alg=new PartitionByMurmurHash();\n            PartitionByMurmurHash murmur=(PartitionByMurmurHash)alg;\n            murmur.setCount(rehashHosts.length);\n            murmur.setSeed(args.getMurmurHashSeed());\n            murmur.setVirtualBucketTimes(args.getMurmurHashVirtualBucketTimes());\n            murmur.setWeightMapFile(args.getMurmurWeightMapFile());\n            murmur.init();\n\t    } else if (HashType.MOD.equals(args.getHashType())) {\n\t        alg=new PartitionByMod();\n            PartitionByMod mod=(PartitionByMod)alg;\n            mod.setCount(rehashHosts.length);\n            mod.init();\n        }\n\t}\n\t\n\tprivate void initDataSource(){\n\t\tdataSource=new DruidDataSource();\n\t\tdataSource.setAsyncCloseConnectionEnable(true);\n\t\tdataSource.setBreakAfterAcquireFailure(true);\n\t\tdataSource.setDefaultAutoCommit(true);\n\t\tdataSource.setDefaultReadOnly(true);\n\t\tdataSource.setDriverClassName(args.getJdbcDriver());\n\t\tdataSource.setEnable(true);\n\t\tdataSource.setPassword(args.getPassword());\n\t\tdataSource.setTestOnBorrow(true);\n\t\tdataSource.setTestOnReturn(true);\n\t\tdataSource.setTestWhileIdle(true);\n\t\tdataSource.setUrl(args.getJdbcUrl());\n\t\tdataSource.setUsername(args.getUser());\n\t}\n\t\n\tprivate RehashLauncher execute() throws IOException{\n\t\tfinal String[] tables=args.getTables();\n\t\tfinal File outputDir=new File(args.getRehashNodeDir());\n\t\tif(!outputDir.exists()){\n\t\t\toutputDir.mkdirs();\n\t\t}else if(outputDir.isFile()){\n\t\t\tthrow new IllegalArgumentException(\"rehashNodeDir must be a directory\");\n\t\t}else if(outputDir.canWrite()){\n\t\t\tthrow new IllegalArgumentException(\"rehashNodeDir must be writable\");\n\t\t}\n\t\tlatch=new CountDownLatch(tables.length);\n\t\tfor(int i=0,l=tables.length;i<l;i++){\n\t\t\tfinal int tableIdx=i;\n\t\t\tfinal String table=tables[tableIdx];\n\t\t\tfinal File output=new File(outputDir,table);\n\t\t\tif(output.exists()){\n\t\t\t\toutput.delete();\n\t\t\t}\n\t\t\toutput.createNewFile();\n\t\t\texecutor.execute(new RehashRunner(output, table));\n\t\t}\n\t\treturn this;\n\t}\n\t\n\tprivate void shutdown(){\n\t\twhile(true){\n\t\t\ttry {\n\t\t\t\tlatch.await();\n\t\t\t\tbreak;\n\t\t\t} catch (InterruptedException e) {\n\t\t\t    LOGGER.error(\"RehashLauncherError\", e);\n\t\t\t}\n\t\t}\n\t\texecutor.shutdown();\n\t\tif(executor.isTerminated()){\n\t\t\tdataSource.close();\n\t\t}\n\t}\n\t\n\tprivate static void execute(String[] args) throws IOException{\n\t\tRehashLauncher launcher=null;\n\t\ttry{\n\t\t\tlauncher=new RehashLauncher(args).execute();\n\t\t} catch (IOException e) {\n            LOGGER.error(\"RehashLauncherError\", e);\n            throw e;\n        } finally{\n\t\t\tif(launcher!=null){\n\t\t\t\tlauncher.shutdown();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic static void main(String[] args) throws IOException {\n\t\texecute(args);\n\t}\n}\n"
  },
  {
    "path": "src/main/resources/auto-sharding-long.txt",
    "content": "2000001-4000000=1\n0-2000000=0\n4000001-8000000=2\n"
  },
  {
    "path": "src/main/resources/auto-sharding-rang-mod.txt",
    "content": "800M1-1000M=6\n600M1-800M=4\n200M1-400M=1\n0-200M=5\n400M1-600M=4\n"
  },
  {
    "path": "src/main/resources/autopartition-long.txt",
    "content": "# range start-end ,data node index\n# K=1000,M=10000.\n0-500M=0\n500M-1000M=1\n1000M-1500M=2"
  },
  {
    "path": "src/main/resources/cacheservice.properties",
    "content": "#used for mycat cache service conf\nfactory.encache=io.mycat.cache.impl.EnchachePooFactory\n#key is pool name ,value is type,max size, expire seconds\npool.SQLRouteCache=encache,10000,1800\npool.ER_SQL2PARENTID=encache,1000,1800\nlayedpool.TableID2DataNodeCache=encache,10000,18000\nlayedpool.TableID2DataNodeCache.TESTDB_ORDERS=50000,18000"
  },
  {
    "path": "src/main/resources/dbseq - utf8mb4.sql",
    "content": "-- ----------------------------\n-- @author mycat\n-- @author jamie12221 add the UTF8MB4\n-- The charset problem, please change UTF8MB4\n-- ----------------------------\n\nDROP TABLE IF EXISTS MYCAT_SEQUENCE;\nCREATE TABLE MYCAT_SEQUENCE (  name VARCHAR(64) NOT NULL,  current_value BIGINT(20) NOT NULL,  increment INT NOT NULL DEFAULT 1, PRIMARY KEY (name) ) ENGINE=InnoDB;\n\n-- ----------------------------\n-- Function structure for `mycat_seq_currval`\n-- ----------------------------\nDROP FUNCTION IF EXISTS `mycat_seq_currval`;\nDELIMITER ;;\nCREATE FUNCTION `mycat_seq_currval`(seq_name VARCHAR(64)) RETURNS varchar(64) CHARSET UTF8MB4\n    DETERMINISTIC\nBEGIN\n    DECLARE retval VARCHAR(64);\n    SET retval=\"-1,0\";\n    SELECT CONCAT(CONVERT(current_value  USING UTF8MB4 ),\",\",CONVERT(increment USING UTF8MB4)) INTO retval FROM MYCAT_SEQUENCE WHERE name = seq_name;\n    RETURN retval ;\nEND\n;;\nDELIMITER ;\n\n-- ----------------------------\n-- Function structure for `mycat_seq_nextval`\n-- ----------------------------\nDROP FUNCTION IF EXISTS `mycat_seq_nextval`;\nDELIMITER ;;\nCREATE FUNCTION `mycat_seq_nextval`(seq_name VARCHAR(64)) RETURNS varchar(64) CHARSET UTF8MB4\n    DETERMINISTIC\nBEGIN\n    DECLARE retval VARCHAR(64);\n    DECLARE val BIGINT;\n    DECLARE inc INT;\n    DECLARE seq_lock INT;\n    set val = -1;\n    set inc = 0;\n    SET seq_lock = -1;\n    SELECT GET_LOCK(seq_name, 15) into seq_lock;\n    if seq_lock = 1 then\n      SELECT current_value + increment, increment INTO val, inc FROM MYCAT_SEQUENCE WHERE name = seq_name for update;\n      if val != -1 then\n          UPDATE MYCAT_SEQUENCE SET current_value = val WHERE name = seq_name;\n      end if;\n      SELECT RELEASE_LOCK(seq_name) into seq_lock;\n    end if;\n    SELECT concat(CAST((val - inc + 1) as CHAR),\",\",CAST(inc as CHAR)) INTO retval;\n    RETURN retval;\nEND\n;;\nDELIMITER ;\n\n-- ----------------------------\n-- Function structure for `mycat_seq_setvals`\n-- ----------------------------\nDROP FUNCTION IF EXISTS `mycat_seq_nextvals`;\nDELIMITER ;;\nCREATE FUNCTION `mycat_seq_nextvals`(seq_name VARCHAR(64), count INT) RETURNS VARCHAR(64) CHARSET UTF8MB4\n    DETERMINISTIC\nBEGIN\n    DECLARE retval VARCHAR(64);\n    DECLARE val BIGINT;\n    DECLARE seq_lock INT;\n    SET val = -1;\n    SET seq_lock = -1;\n    SELECT GET_LOCK(seq_name, 15) into seq_lock;\n    if seq_lock = 1 then\n        SELECT current_value + count INTO val FROM MYCAT_SEQUENCE WHERE name = seq_name for update;\n        IF val != -1 THEN\n            UPDATE MYCAT_SEQUENCE SET current_value = val WHERE name = seq_name;\n        END IF;\n        SELECT RELEASE_LOCK(seq_name) into seq_lock;\n    end if;\n    SELECT CONCAT(CAST((val - count + 1) as CHAR), \",\", CAST(val as CHAR)) INTO retval;\n    RETURN retval;\nEND\n;;\nDELIMITER ;\n\n-- ----------------------------\n-- Function structure for `mycat_seq_setval`\n-- ----------------------------\nDROP FUNCTION IF EXISTS `mycat_seq_setval`;\nDELIMITER ;;\nCREATE FUNCTION `mycat_seq_setval`(seq_name VARCHAR(64), value BIGINT) RETURNS varchar(64) CHARSET UTF8MB4\n    DETERMINISTIC\nBEGIN\n    DECLARE retval VARCHAR(64);\n    DECLARE inc INT;\n    SET inc = 0;\n    SELECT increment INTO inc FROM MYCAT_SEQUENCE WHERE name = seq_name;\n    UPDATE MYCAT_SEQUENCE SET current_value = value WHERE name = seq_name;\n    SELECT concat(CAST(value as CHAR),\",\",CAST(inc as CHAR)) INTO retval;\n    RETURN retval;\nEND\n;;\nDELIMITER ;\n\nINSERT INTO MYCAT_SEQUENCE VALUES ('GLOBAL', 1, 1);"
  },
  {
    "path": "src/main/resources/dbseq.sql",
    "content": "DROP TABLE IF EXISTS MYCAT_SEQUENCE;\nCREATE TABLE MYCAT_SEQUENCE (  name VARCHAR(64) NOT NULL,  current_value BIGINT(20) NOT NULL,  increment INT NOT NULL DEFAULT 1, PRIMARY KEY (name) ) ENGINE=InnoDB;\n\n-- ----------------------------\n-- Function structure for `mycat_seq_currval`\n-- ----------------------------\nDROP FUNCTION IF EXISTS `mycat_seq_currval`;\nDELIMITER ;;\nCREATE FUNCTION `mycat_seq_currval`(seq_name VARCHAR(64)) RETURNS varchar(64) CHARSET latin1\n    DETERMINISTIC\nBEGIN\n    DECLARE retval VARCHAR(64);\n    SET retval=\"-1,0\";\n    SELECT concat(CAST(current_value AS CHAR),\",\",CAST(increment AS CHAR) ) INTO retval FROM MYCAT_SEQUENCE  WHERE name = seq_name;\n    RETURN retval ;\nEND\n;;\nDELIMITER ;\n\n-- ----------------------------\n-- Function structure for `mycat_seq_nextval`\n-- ----------------------------\nDROP FUNCTION IF EXISTS `mycat_seq_nextval`;\nDELIMITER ;;\nCREATE FUNCTION `mycat_seq_nextval`(seq_name VARCHAR(64)) RETURNS varchar(64) CHARSET latin1\n    DETERMINISTIC\nBEGIN\n    DECLARE retval VARCHAR(64);\n    DECLARE val BIGINT;\n    DECLARE inc INT;\n    DECLARE seq_lock INT;\n    set val = -1;\n    set inc = 0;\n    SET seq_lock = -1;\n    SELECT GET_LOCK(seq_name, 15) into seq_lock;\n    if seq_lock = 1 then\n      SELECT current_value + increment, increment INTO val, inc FROM MYCAT_SEQUENCE WHERE name = seq_name for update;\n      if val != -1 then\n          UPDATE MYCAT_SEQUENCE SET current_value = val WHERE name = seq_name;\n      end if;\n      SELECT RELEASE_LOCK(seq_name) into seq_lock;\n    end if;\n    SELECT concat(CAST((val - inc + 1) as CHAR),\",\",CAST(inc as CHAR)) INTO retval;\n    RETURN retval;\nEND\n;;\nDELIMITER ;\n\n-- ----------------------------\n-- Function structure for `mycat_seq_setvals`\n-- ----------------------------\nDROP FUNCTION IF EXISTS `mycat_seq_nextvals`;\nDELIMITER ;;\nCREATE FUNCTION `mycat_seq_nextvals`(seq_name VARCHAR(64), count INT) RETURNS VARCHAR(64) CHARSET latin1\n    DETERMINISTIC\nBEGIN\n    DECLARE retval VARCHAR(64);\n    DECLARE val BIGINT;\n    DECLARE seq_lock INT;\n    SET val = -1;\n    SET seq_lock = -1;\n    SELECT GET_LOCK(seq_name, 15) into seq_lock;\n    if seq_lock = 1 then\n        SELECT current_value + count INTO val FROM MYCAT_SEQUENCE WHERE name = seq_name for update;\n        IF val != -1 THEN\n            UPDATE MYCAT_SEQUENCE SET current_value = val WHERE name = seq_name;\n        END IF;\n        SELECT RELEASE_LOCK(seq_name) into seq_lock;\n    end if;\n    SELECT CONCAT(CAST((val - count + 1) as CHAR), \",\", CAST(val as CHAR)) INTO retval;\n    RETURN retval;\nEND\n;;\nDELIMITER ;\n\n-- ----------------------------\n-- Function structure for `mycat_seq_setval`\n-- ----------------------------\nDROP FUNCTION IF EXISTS `mycat_seq_setval`;\nDELIMITER ;;\nCREATE FUNCTION `mycat_seq_setval`(seq_name VARCHAR(64), value BIGINT) RETURNS varchar(64) CHARSET latin1\n    DETERMINISTIC\nBEGIN\n    DECLARE retval VARCHAR(64);\n    DECLARE inc INT;\n    SET inc = 0;\n    SELECT increment INTO inc FROM MYCAT_SEQUENCE WHERE name = seq_name;\n    UPDATE MYCAT_SEQUENCE SET current_value = value WHERE name = seq_name;\n    SELECT concat(CAST(value as CHAR),\",\",CAST(inc as CHAR)) INTO retval;\n    RETURN retval;\nEND\n;;\nDELIMITER ;\n\nINSERT INTO MYCAT_SEQUENCE VALUES ('GLOBAL', 1, 1);"
  },
  {
    "path": "src/main/resources/ehcache.xml",
    "content": "<ehcache xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:noNamespaceSchemaLocation=\"ehcache.xsd\" maxEntriesLocalHeap=\"100000000\"\n\tmaxBytesLocalDisk=\"50G\" updateCheck=\"false\" >\n\t<defaultCache maxElementsInMemory=\"1000000\" eternal=\"false\"\n\t\toverflowToDisk=\"false\" diskSpoolBufferSizeMB=\"30\" maxElementsOnDisk=\"10000000\"\n\t\tdiskPersistent=\"false\" diskExpiryThreadIntervalSeconds=\"120\"\n\t\tmemoryStoreEvictionPolicy=\"LRU\" />\n</ehcache>"
  },
  {
    "path": "src/main/resources/index_to_charset.properties",
    "content": "1=big5\n2=latin2\n3=dec8\n4=cp850\n5=latin1\n6=hp8\n7=koi8r\n8=latin1\n9=latin2\n10=swe7\n11=ascii\n12=ujis\n13=sjis\n14=cp1251\n15=latin1\n16=hebrew\n18=tis620\n19=euckr\n20=latin7\n21=latin2\n22=koi8u\n23=cp1251\n24=gb2312\n25=greek\n26=cp1250\n27=latin2\n28=gbk\n29=cp1257\n30=latin5\n31=latin1\n32=armscii8\n33=utf8\n34=cp1250\n35=ucs2\n36=cp866\n37=keybcs2\n38=macce\n39=macroman\n40=cp852\n41=latin7\n42=latin7\n43=macce\n44=cp1250\n45=utf8mb4\n46=utf8mb4\n47=latin1\n48=latin1\n49=latin1\n50=cp1251\n51=cp1251\n52=cp1251\n53=macroman\n54=utf16\n55=utf16\n56=utf16le\n57=cp1256\n58=cp1257\n59=cp1257\n60=utf32\n61=utf32\n62=utf16le\n63=binary\n64=armscii8\n65=ascii\n66=cp1250\n67=cp1256\n68=cp866\n69=dec8\n70=greek\n71=hebrew\n72=hp8\n73=keybcs2\n74=koi8r\n75=koi8u\n77=latin2\n78=latin5\n79=latin7\n80=cp850\n81=cp852\n82=swe7\n83=utf8\n84=big5\n85=euckr\n86=gb2312\n87=gbk\n88=sjis\n89=tis620\n90=ucs2\n91=ujis\n92=geostd8\n93=geostd8\n94=latin1\n95=cp932\n96=cp932\n97=eucjpms\n98=eucjpms\n99=cp1250\n101=utf16\n102=utf16\n103=utf16\n104=utf16\n105=utf16\n106=utf16\n107=utf16\n108=utf16\n109=utf16\n110=utf16\n111=utf16\n112=utf16\n113=utf16\n114=utf16\n115=utf16\n116=utf16\n117=utf16\n118=utf16\n119=utf16\n120=utf16\n121=utf16\n122=utf16\n123=utf16\n124=utf16\n128=ucs2\n129=ucs2\n130=ucs2\n131=ucs2\n132=ucs2\n133=ucs2\n134=ucs2\n135=ucs2\n136=ucs2\n137=ucs2\n138=ucs2\n139=ucs2\n140=ucs2\n141=ucs2\n142=ucs2\n143=ucs2\n144=ucs2\n145=ucs2\n146=ucs2\n147=ucs2\n148=ucs2\n149=ucs2\n150=ucs2\n151=ucs2\n159=ucs2\n160=utf32\n161=utf32\n162=utf32\n163=utf32\n164=utf32\n165=utf32\n166=utf32\n167=utf32\n168=utf32\n169=utf32\n170=utf32\n171=utf32\n172=utf32\n173=utf32\n174=utf32\n175=utf32\n176=utf32\n177=utf32\n178=utf32\n179=utf32\n180=utf32\n181=utf32\n182=utf32\n183=utf32\n192=utf8\n193=utf8\n194=utf8\n195=utf8\n196=utf8\n197=utf8\n198=utf8\n199=utf8\n200=utf8\n201=utf8\n202=utf8\n203=utf8\n204=utf8\n205=utf8\n206=utf8\n207=utf8\n208=utf8\n209=utf8\n210=utf8\n211=utf8\n212=utf8\n213=utf8\n214=utf8\n215=utf8\n223=utf8\n224=utf8mb4\n225=utf8mb4\n226=utf8mb4\n227=utf8mb4\n228=utf8mb4\n229=utf8mb4\n230=utf8mb4\n231=utf8mb4\n232=utf8mb4\n233=utf8mb4\n234=utf8mb4\n235=utf8mb4\n236=utf8mb4\n237=utf8mb4\n238=utf8mb4\n239=utf8mb4\n240=utf8mb4\n241=utf8mb4\n242=utf8mb4\n243=utf8mb4\n244=utf8mb4\n245=utf8mb4\n246=utf8mb4\n247=utf8mb4\n248=utf8mb4\n249=utf8mb4\n250=utf8mb4\n251=utf8mb4\n252=utf8mb4\n253=utf8mb4\n254=utf8mb4\n255=utf8mb4"
  },
  {
    "path": "src/main/resources/log4j2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration shutdownHook=\"disable\" status=\"WARN\">\n    <Appenders>\n        <Console name=\"Console\" target=\"SYSTEM_OUT\">\n            <PatternLayout pattern=\"%d [%-5p][%t] %m %throwable{full} (%C:%F:%L) %n\"/>\n        </Console>\n\n        <RollingFile name=\"RollingFile\" fileName=\"../logs/mycat.log\"\n                     filePattern=\"logs/$${date:yyyy-MM}/mycat-%d{MM-dd}-%i.log.gz\">\n            <PatternLayout>\n                <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%t] (%l) - %m%n</Pattern>\n            </PatternLayout>\n            <Policies>\n                <OnStartupTriggeringPolicy/>\n                <SizeBasedTriggeringPolicy size=\"250 MB\"/>\n                <TimeBasedTriggeringPolicy/>\n            </Policies>\n        </RollingFile>\n    </Appenders>\n    <Loggers>\n        <!--<AsyncLogger name=\"io.mycat\" level=\"info\" includeLocation=\"true\" additivity=\"false\">-->\n            <!--<AppenderRef ref=\"Console\"/>-->\n            <!--<AppenderRef ref=\"RollingFile\"/>-->\n        <!--</AsyncLogger>-->\n        <asyncRoot level=\"debug\" includeLocation=\"true\">\n\n            <AppenderRef ref=\"Console\" />\n            <AppenderRef ref=\"RollingFile\"/>\n\n        </asyncRoot>\n    </Loggers>\n</Configuration>\n"
  },
  {
    "path": "src/main/resources/migrateTables.properties",
    "content": "#schema1=tb1,tb2,...\n#schema2=all（写all或者不写将对此schema下拆分节点变化的拆分表全部进行重新路由）\n#...\n\n#sample\n#TESTDB=travelrecord,company,goods"
  },
  {
    "path": "src/main/resources/myid.properties",
    "content": "loadZk=false\nzkURL=127.0.0.1:2181\nclusterId=mycat-cluster-1\nmyid=mycat_fz_01\nclusterSize=3\nclusterNodes=mycat_fz_01,mycat_fz_02,mycat_fz_04\n#server  booster  ;   booster install on db same server,will reset all minCon to 2\ntype=server\nboosterDataHosts=dataHost1\n"
  },
  {
    "path": "src/main/resources/partition-hash-int.txt",
    "content": "10000=0\n10010=1"
  },
  {
    "path": "src/main/resources/partition-range-mod.txt",
    "content": "# range start-end ,data node group size\n0-200M=5\n200M1-400M=1\n400M1-600M=4\n600M1-800M=4\n800M1-1000M=6\n"
  },
  {
    "path": "src/main/resources/rule.dtd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!--\n -  \n - Licensed under the Apache License, Version 2.0 (the \"License\");\n - you may not use this file except in compliance with the License.\n - You may obtain a copy of the License at\n -  \n -      http://www.apache.org/licenses/LICENSE-2.0\n -  \n - Unless required by applicable law or agreed to in writing, software\n - distributed under the License is distributed on an \"AS IS\" BASIS,\n - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n - See the License for the specific language governing permissions and\n - limitations under the License.\n-->\n<!ELEMENT mycat:rule (tableRule*,function*)>\n<!ATTLIST mycat:rule xmlns:mycat CDATA #FIXED \"http://io.mycat/\">\n\n<!ELEMENT tableRule (rule+)>\n<!ATTLIST tableRule name CDATA #REQUIRED>\n\n<!ELEMENT rule (columns,algorithm)>\n<!ELEMENT columns (#PCDATA)>\n<!ELEMENT algorithm (#PCDATA)>\n\n<!ELEMENT function (property*)>\n<!ATTLIST function name CDATA #REQUIRED>\n<!ATTLIST function class CDATA #REQUIRED>\n\n<!ELEMENT property (#PCDATA | bean)*>\n<!ATTLIST property name NMTOKEN #REQUIRED>\n\n<!ELEMENT bean (property*)>\n<!ATTLIST bean name NMTOKEN #IMPLIED>\n<!ATTLIST bean class NMTOKEN #IMPLIED>\n"
  },
  {
    "path": "src/main/resources/rule.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- - - Licensed under the Apache License, Version 2.0 (the \"License\"); \n\t- you may not use this file except in compliance with the License. - You \n\tmay obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 \n\t- - Unless required by applicable law or agreed to in writing, software - \n\tdistributed under the License is distributed on an \"AS IS\" BASIS, - WITHOUT \n\tWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the \n\tLicense for the specific language governing permissions and - limitations \n\tunder the License. -->\n<!DOCTYPE mycat:rule SYSTEM \"rule.dtd\">\n<mycat:rule xmlns:mycat=\"http://io.mycat/\">\n\t<tableRule name=\"rule1\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>func1</algorithm>\n\t\t</rule>\n\t</tableRule>\n\n\t<tableRule name=\"sharding-by-date\">\n\t\t<rule>\n\t\t\t<columns>createTime</columns>\n\t\t\t<algorithm>partbyday</algorithm>\n\t\t</rule>\n\t</tableRule>\n\n\t<tableRule name=\"rule2\">\n\t\t<rule>\n\t\t\t<columns>user_id</columns>\n\t\t\t<algorithm>func1</algorithm>\n\t\t</rule>\n\t</tableRule>\n\n\t<tableRule name=\"sharding-by-intfile\">\n\t\t<rule>\n\t\t\t<columns>sharding_id</columns>\n\t\t\t<algorithm>hash-int</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"auto-sharding-long\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>rang-long</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"mod-long\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>mod-long</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"sharding-by-murmur\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>murmur</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"crc32slot\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>crc32slot</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"sharding-by-month\">\n\t\t<rule>\n\t\t\t<columns>create_time</columns>\n\t\t\t<algorithm>partbymonth</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"latest-month-calldate\">\n\t\t<rule>\n\t\t\t<columns>calldate</columns>\n\t\t\t<algorithm>latestMonth</algorithm>\n\t\t</rule>\n\t</tableRule>\n\n\t<tableRule name=\"auto-sharding-rang-mod\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>rang-mod</algorithm>\n\t\t</rule>\n\t</tableRule>\n\n\t<tableRule name=\"jch\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>jump-consistent-hash</algorithm>\n\t\t</rule>\n\t</tableRule>\n\n\t<function name=\"murmur\"\n\t\t\t  class=\"io.mycat.route.function.PartitionByMurmurHash\">\n\t\t<property name=\"seed\">0</property><!-- 默认是0 -->\n\t\t<property name=\"count\">2</property><!-- 要分片的数据库节点数量，必须指定，否则没法分片 -->\n\t\t<property name=\"virtualBucketTimes\">160</property><!-- 一个实际的数据库节点被映射为这么多虚拟节点，默认是160倍，也就是虚拟节点数是物理节点数的160倍 -->\n\t\t<!-- <property name=\"weightMapFile\">weightMapFile</property> 节点的权重，没有指定权重的节点默认是1。以properties文件的格式填写，以从0开始到count-1的整数值也就是节点索引为key，以节点权重值为值。所有权重值必须是正整数，否则以1代替 -->\n\t\t<!-- <property name=\"bucketMapPath\">/etc/mycat/bucketMapPath</property>\n\t\t\t用于测试时观察各物理节点与虚拟节点的分布情况，如果指定了这个属性，会把虚拟节点的murmur hash值与物理节点的映射按行输出到这个文件，没有默认值，如果不指定，就不会输出任何东西 -->\n\t</function>\n\n\t<function name=\"crc32slot\"\n\t\t\t  class=\"io.mycat.route.function.PartitionByCRC32PreSlot\">\n\t\t<property name=\"count\">2</property><!-- 要分片的数据库节点数量，必须指定，否则没法分片 -->\n\t</function>\n\t<function name=\"hash-int\"\n\t\t\t  class=\"io.mycat.route.function.PartitionByFileMap\">\n\t\t<property name=\"mapFile\">partition-hash-int.txt</property>\n\t</function>\n\t<function name=\"rang-long\"\n\t\t\t  class=\"io.mycat.route.function.AutoPartitionByLong\">\n\t\t<property name=\"mapFile\">autopartition-long.txt</property>\n\t</function>\n\t<function name=\"mod-long\" class=\"io.mycat.route.function.PartitionByMod\">\n\t\t<!-- how many data nodes -->\n\t\t<property name=\"count\">3</property>\n\t</function>\n\n\t<function name=\"func1\" class=\"io.mycat.route.function.PartitionByLong\">\n\t\t<property name=\"partitionCount\">8</property>\n\t\t<property name=\"partitionLength\">128</property>\n\t</function>\n\t<function name=\"latestMonth\"\n\t\t\t  class=\"io.mycat.route.function.LatestMonthPartion\">\n\t\t<property name=\"splitOneDay\">24</property>\n\t</function>\n\t<function name=\"partbymonth\"\n\t\t\t  class=\"io.mycat.route.function.PartitionByMonth\">\n\t\t<property name=\"dateFormat\">yyyy-MM-dd</property>\n\t\t<property name=\"sBeginDate\">2015-01-01</property>\n\t</function>\n\n\n\t<function name=\"partbyday\"\n\t\t\t  class=\"io.mycat.route.function.PartitionByDate\">\n\t\t<property name=\"dateFormat\">yyyy-MM-dd</property>\n\t\t<property name=\"sNaturalDay\">0</property>\n\t\t<property name=\"sBeginDate\">2014-01-01</property>\n\t\t<property name=\"sEndDate\">2014-01-31</property>\n\t\t<property name=\"sPartionDay\">10</property>\n\t</function>\n\n\t<function name=\"rang-mod\" class=\"io.mycat.route.function.PartitionByRangeMod\">\n\t\t<property name=\"mapFile\">partition-range-mod.txt</property>\n\t</function>\n\n\t<function name=\"jump-consistent-hash\" class=\"io.mycat.route.function.PartitionByJumpConsistentHash\">\n\t\t<property name=\"totalBuckets\">3</property>\n\t</function>\n</mycat:rule>"
  },
  {
    "path": "src/main/resources/schema.dtd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!--\n -\n - Licensed under the Apache License, Version 2.0 (the \"License\");\n - you may not use this file except in compliance with the License.\n - You may obtain a copy of the License at\n -\n -      http://www.apache.org/licenses/LICENSE-2.0\n -\n - Unless required by applicable law or agreed to in writing, software\n - distributed under the License is distributed on an \"AS IS\" BASIS,\n - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n - See the License for the specific language governing permissions and\n - limitations under the License.\n-->\n<!ELEMENT mycat:schema (schema*,dataNode*,dataHost*)>\n<!ATTLIST mycat:schema xmlns:mycat CDATA #FIXED \"http://io.mycat/\">\n\n<!ELEMENT schema (table*)>\n<!ATTLIST schema name NMTOKEN #REQUIRED>\n<!ATTLIST schema checkSQLschema NMTOKEN #IMPLIED>\n<!ATTLIST schema dataNode CDATA #IMPLIED>\n<!ATTLIST schema randomDataNode CDATA #IMPLIED>\n<!ATTLIST schema sqlMaxLimit CDATA #IMPLIED>\n<!ATTLIST schema needSupportMultiDBType CDATA #IMPLIED>\n\n<!ELEMENT table (property*,(childTable*))>\n<!ATTLIST table name CDATA #REQUIRED>\n<!ATTLIST table nameSuffix CDATA #IMPLIED>\n<!ATTLIST table dataNode CDATA #REQUIRED>\n<!ATTLIST table rule NMTOKEN #IMPLIED>\n<!ATTLIST table ruleRequired NMTOKEN #IMPLIED>\n<!ATTLIST table primaryKey NMTOKEN #IMPLIED>\n<!ATTLIST table subTables CDATA #IMPLIED>\n<!ATTLIST table autoIncrement NMTOKEN #IMPLIED>\n<!ATTLIST table needAddLimit NMTOKEN #IMPLIED>\n<!ATTLIST table type NMTOKEN #IMPLIED>\n<!ATTLIST table splitTableNames NMTOKEN #IMPLIED>\n<!ATTLIST table fetchStoreNodeByJdbc NMTOKEN #IMPLIED>\n\n\n<!ELEMENT childTable (property*,(childTable*))>\n<!ATTLIST childTable name NMTOKEN #REQUIRED>\n<!ATTLIST childTable joinKey NMTOKEN #REQUIRED>\n<!ATTLIST childTable parentKey NMTOKEN #REQUIRED>\n<!ATTLIST childTable primaryKey NMTOKEN #IMPLIED>\n<!ATTLIST childTable autoIncrement NMTOKEN #IMPLIED>\n  <!ATTLIST childTable needAddLimit NMTOKEN #IMPLIED>\n\n\n<!ELEMENT dataNode (property*)>\n<!ATTLIST dataNode name CDATA #REQUIRED>\n<!ATTLIST dataNode dataHost CDATA #REQUIRED>\n<!ATTLIST dataNode database CDATA #REQUIRED>\n\n<!ELEMENT dataHost (heartbeat,(connectionInitSql*),(writeHost+))>\n<!ATTLIST dataHost\n  balance CDATA #REQUIRED\n  maxCon CDATA #REQUIRED\n  minCon CDATA #REQUIRED\n  name NMTOKEN #REQUIRED\n  balance CDATA #REQUIRED\n  balanceType CDATA #IMPLIED\n  maxRetryCount CDATA #IMPLIED\n  writeType CDATA #IMPLIED\n  switchType  CDATA #IMPLIED\n  notSwitch CDATA #IMPLIED  \n  slaveThreshold  CDATA #IMPLIED\n  tempReadHostAvailable CDATA #IMPLIED\n  dbType CDATA #REQUIRED\n  filters CDATA #IMPLIED\n  logTime CDATA #IMPLIED\n    slaveIDs CDATA #IMPLIED\n  dbDriver CDATA #REQUIRED>\n\n<!ELEMENT writeHost (readHost)*>\n<!ATTLIST writeHost\n  url CDATA #REQUIRED\n  host NMTOKEN #REQUIRED\n  password CDATA #REQUIRED\n  user CDATA #REQUIRED\n  usingDecrypt CDATA #IMPLIED\n  checkAlive CDATA #IMPLIED\n        >\n\n<!ELEMENT heartbeat (#PCDATA)>\n<!ELEMENT connectionInitSql (#PCDATA)>\n<!ELEMENT readHost (property*) >\n<!ATTLIST readHost\n  host NMTOKEN #REQUIRED\n  url CDATA #REQUIRED\n  password CDATA #REQUIRED\n  user CDATA #REQUIRED\n  weight CDATA #IMPLIED\n  usingDecrypt CDATA #IMPLIED>\n\n\n"
  },
  {
    "path": "src/main/resources/schema.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE mycat:schema SYSTEM \"schema.dtd\">\n<mycat:schema xmlns:mycat=\"http://io.mycat/\">\n\n\t<schema name=\"TESTDB\" checkSQLschema=\"true\" sqlMaxLimit=\"100\" randomDataNode=\"dn1\">\n\t\t<!-- auto sharding by id (long) -->\n\t\t<!--splitTableNames 启用<table name 属性使用逗号分割配置多个表,即多个表使用这个配置-->\n<!--fetchStoreNodeByJdbc 启用ER表使用JDBC方式获取DataNode-->\n\t\t<table name=\"customer\" primaryKey=\"id\" dataNode=\"dn1,dn2\" rule=\"sharding-by-intfile\" autoIncrement=\"true\" fetchStoreNodeByJdbc=\"true\">\n\t\t\t<childTable name=\"customer_addr\" primaryKey=\"id\" joinKey=\"customer_id\" parentKey=\"id\"> </childTable>\n\t\t</table>\n\t\t<!-- <table name=\"oc_call\" primaryKey=\"ID\" dataNode=\"dn1$0-743\" rule=\"latest-month-calldate\"\n\t\t\t/> -->\n\t</schema>\n\t<!-- <dataNode name=\"dn1$0-743\" dataHost=\"localhost1\" database=\"db$0-743\"\n\t\t/> -->\n\t<dataNode name=\"dn1\" dataHost=\"localhost1\" database=\"db1\" />\n\t<dataNode name=\"dn2\" dataHost=\"localhost1\" database=\"db2\" />\n\t<dataNode name=\"dn3\" dataHost=\"localhost1\" database=\"db3\" />\n\t<!--<dataNode name=\"dn4\" dataHost=\"sequoiadb1\" database=\"SAMPLE\" />\n\t <dataNode name=\"jdbc_dn1\" dataHost=\"jdbchost\" database=\"db1\" />\n\t<dataNode\tname=\"jdbc_dn2\" dataHost=\"jdbchost\" database=\"db2\" />\n\t<dataNode name=\"jdbc_dn3\" \tdataHost=\"jdbchost\" database=\"db3\" /> -->\n\t<dataHost name=\"localhost1\" maxCon=\"1000\" minCon=\"10\" balance=\"0\"\n\t\t\t  writeType=\"0\" dbType=\"mysql\" dbDriver=\"jdbc\" switchType=\"1\"  slaveThreshold=\"100\">\n\t\t<heartbeat>select user()</heartbeat>\n\t\t<!-- can have multi write hosts -->\n\t\t<writeHost host=\"hostM1\" url=\"jdbc:mysql://localhost:3306\" user=\"root\"\n\t\t\t\t   password=\"root\">\n\t\t</writeHost>\n\t\t<!-- <writeHost host=\"hostM2\" url=\"localhost:3316\" user=\"root\" password=\"123456\"/> -->\n\t</dataHost>\n\t<!--\n\t\t<dataHost name=\"sequoiadb1\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" dbType=\"sequoiadb\" dbDriver=\"jdbc\">\n\t\t<heartbeat> \t\t</heartbeat>\n\t\t <writeHost host=\"hostM1\" url=\"sequoiadb://1426587161.dbaas.sequoialab.net:11920/SAMPLE\" user=\"jifeng\" \tpassword=\"jifeng\"></writeHost>\n\t\t </dataHost>\n\n\t  <dataHost name=\"oracle1\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" writeType=\"0\" \tdbType=\"oracle\" dbDriver=\"jdbc\"> <heartbeat>select 1 from dual</heartbeat>\n\t\t<connectionInitSql>alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'</connectionInitSql>\n\t\t<writeHost host=\"hostM1\" url=\"jdbc:oracle:thin:@127.0.0.1:1521:nange\" user=\"base\" \tpassword=\"123456\" > </writeHost> </dataHost>\n\n\t\t<dataHost name=\"jdbchost\" maxCon=\"1000\" \tminCon=\"1\" balance=\"0\" writeType=\"0\" dbType=\"mongodb\" dbDriver=\"jdbc\">\n\t\t<heartbeat>select \tuser()</heartbeat>\n\t\t<writeHost host=\"hostM\" url=\"mongodb://192.168.0.99/test\" user=\"admin\" password=\"123456\" ></writeHost> </dataHost>\n\n\t\t<dataHost name=\"sparksql\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" dbType=\"spark\" dbDriver=\"jdbc\">\n\t\t<heartbeat> </heartbeat>\n\t\t <writeHost host=\"hostM1\" url=\"jdbc:hive2://feng01:10000\" user=\"jifeng\" \tpassword=\"jifeng\"></writeHost> </dataHost> -->\n\n\t<!-- <dataHost name=\"jdbchost\" maxCon=\"1000\" minCon=\"10\" balance=\"0\" dbType=\"mysql\"\n\t\tdbDriver=\"jdbc\"> <heartbeat>select user()</heartbeat> <writeHost host=\"hostM1\"\n\t\turl=\"jdbc:mysql://localhost:3306\" user=\"root\" password=\"123456\"> </writeHost>\n\t\t</dataHost> -->\n</mycat:schema>"
  },
  {
    "path": "src/main/resources/sequence_conf.properties",
    "content": "#default global sequence\nGLOBAL.HISIDS=\nGLOBAL.MINID=10001\nGLOBAL.MAXID=20000\nGLOBAL.CURID=10000\n\n# self define sequence\nCOMPANY.HISIDS=\nCOMPANY.MINID=1001\nCOMPANY.MAXID=2000\nCOMPANY.CURID=1000\n\nCUSTOMER.HISIDS=\nCUSTOMER.MINID=1001\nCUSTOMER.MAXID=2000\nCUSTOMER.CURID=1000\n\nORDER.HISIDS=\nORDER.MINID=1001\nORDER.MAXID=2000\nORDER.CURID=1000\n\nHOTNEWS.HISIDS=\nHOTNEWS.MINID=1001\nHOTNEWS.MAXID=2000\nHOTNEWS.CURID=1000\n\n"
  },
  {
    "path": "src/main/resources/sequence_db_conf.properties",
    "content": "#sequence stored in datanode\nGLOBAL=dn1\nCOMPANY=dn1\nCUSTOMER=dn1\nORDERS=dn1"
  },
  {
    "path": "src/main/resources/sequence_distributed_conf.properties",
    "content": "INSTANCEID=01\nCLUSTERID=01\n"
  },
  {
    "path": "src/main/resources/sequence_http_conf.properties",
    "content": "url = http://localhost:8067/"
  },
  {
    "path": "src/main/resources/sequence_time_conf.properties",
    "content": "#sequence depend on TIME\nWORKID=01\nDATAACENTERID=01"
  },
  {
    "path": "src/main/resources/server.dtd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!--\n -  \n - Licensed under the Apache License, Version 2.0 (the \"License\");\n - you may not use this file except in compliance with the License.\n - You may obtain a copy of the License at\n -  \n -      http://www.apache.org/licenses/LICENSE-2.0\n -  \n - Unless required by applicable law or agreed to in writing, software\n - distributed under the License is distributed on an \"AS IS\" BASIS,\n - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n - See the License for the specific language governing permissions and\n - limitations under the License.\n-->\n<!ELEMENT mycat:server (system?,firewall?,user+,cluster?)>\n<!ATTLIST mycat:server xmlns:mycat CDATA #FIXED \"http://io.mycat/\">\n\n<!ELEMENT system (property*) >\n\n<!ELEMENT user (property+,privileges*)>\n<!ATTLIST user \nname NMTOKEN #REQUIRED\ndefaultAccount NMTOKEN #IMPLIED\n>\n \n<!ELEMENT privileges (schema*,dataNode*)>\n<!ATTLIST privileges check NMTOKEN #REQUIRED>\n\n<!ELEMENT schema (table)*>\n<!ATTLIST schema\n  name CDATA #REQUIRED\n  dml CDATA #REQUIRED>\n\n<!ELEMENT table (name*)>\n<!ATTLIST table\n  name CDATA #REQUIRED\n  dml CDATA #REQUIRED\n  ext CDATA #IMPLIED>\n\n<!ELEMENT dataNode (name*)>\n<!ATTLIST dataNode\n  name CDATA #REQUIRED\n  dml CDATA #REQUIRED\n  ext CDATA #IMPLIED>\n\n<!ELEMENT cluster (node+,group*) >\n<!ELEMENT node (property+)>\n<!ATTLIST node name NMTOKEN #REQUIRED>\n<!ELEMENT group (property) >\n<!ATTLIST group name NMTOKEN #REQUIRED>\n\n<!ELEMENT firewall (whitehost*,blacklist*) >\n<!ELEMENT whitehost (host*)>\n<!ELEMENT blacklist (property*)>\n<!ATTLIST blacklist check NMTOKEN #REQUIRED>\n<!ELEMENT host (property*)>\n<!ATTLIST host \n   host CDATA #REQUIRED\n   user CDATA #REQUIRED\n   >\n<!ATTLIST sqllist sql CDATA #REQUIRED>\n\n<!ELEMENT property (#PCDATA | bean)*>\n<!ATTLIST property name NMTOKEN #REQUIRED>\n<!ELEMENT bean (property*)>\n<!ATTLIST bean name NMTOKEN #IMPLIED>\n<!ATTLIST bean class NMTOKEN #REQUIRED>"
  },
  {
    "path": "src/main/resources/server.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- - - Licensed under the Apache License, Version 2.0 (the \"License\"); \n\t- you may not use this file except in compliance with the License. - You \n\tmay obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 \n\t- - Unless required by applicable law or agreed to in writing, software - \n\tdistributed under the License is distributed on an \"AS IS\" BASIS, - WITHOUT \n\tWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the \n\tLicense for the specific language governing permissions and - limitations \n\tunder the License. -->\n<!DOCTYPE mycat:server SYSTEM \"server.dtd\">\n<mycat:server xmlns:mycat=\"http://io.mycat/\">\n\t<system>\n\t<property name=\"nonePasswordLogin\">0</property> <!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0，设置为1则需要指定默认账户-->\n\t<property name=\"ignoreUnknownCommand\">0</property><!-- 0遇上没有实现的报文(Unknown command:),就会报错、1为忽略该报文，返回ok报文。\n\t在某些mysql客户端存在客户端已经登录的时候还会继续发送登录报文,mycat会报错,该设置可以绕过这个错误-->\n\t<property name=\"useHandshakeV10\">1</property>\n    <property name=\"removeGraveAccent\">1</property>\n\t<property name=\"useSqlStat\">0</property>  <!-- 1为开启实时统计、0为关闭 -->\n\t<property name=\"useGlobleTableCheck\">0</property>  <!-- 1为开启全加班一致性检测、0为关闭 -->\n\t<property name=\"sqlExecuteTimeout\">300</property>  <!-- SQL 执行超时 单位:秒-->\n\t\t<property name=\"sequenceHandlerType\">1</property>\n\t<!--<property name=\"sequnceHandlerPattern\">(?:(\\s*next\\s+value\\s+for\\s*MYCATSEQ_(\\w+))(,|\\)|\\s)*)+</property>\n\tINSERT INTO `travelrecord` (`id`,user_id) VALUES ('next value for MYCATSEQ_GLOBAL',\"xxx\");\n\t-->\n\t<!--必须带有MYCATSEQ_或者 mycatseq_进入序列匹配流程 注意MYCATSEQ_有空格的情况-->\n\t<property name=\"sequnceHandlerPattern\">(?:(\\s*next\\s+value\\s+for\\s*MYCATSEQ_(\\w+))(,|\\)|\\s)*)+</property>\n\t<property name=\"subqueryRelationshipCheck\">false</property> <!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false -->\n\t<property name=\"sequenceHanlderClass\">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property>\n      <!--  <property name=\"useCompression\">1</property>--> <!--1为开启mysql压缩协议-->\n        <!--  <property name=\"fakeMySQLVersion\">5.6.20</property>--> <!--设置模拟的MySQL版本号-->\n\t<!-- <property name=\"processorBufferChunk\">40960</property> -->\n\t<!-- \n\t<property name=\"processors\">1</property> \n\t<property name=\"processorExecutor\">32</property> \n\t -->\n        <!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->\n\t\t<property name=\"processorBufferPoolType\">0</property>\n\t\t<!--默认是65535 64K 用于sql解析时最大文本长度 -->\n\t\t<!--<property name=\"maxStringLiteralLength\">65535</property>-->\n\t\t<!--<property name=\"sequenceHandlerType\">0</property>-->\n\t\t<!--<property name=\"backSocketNoDelay\">1</property>-->\n\t\t<!--<property name=\"frontSocketNoDelay\">1</property>-->\n\t\t<!--<property name=\"processorExecutor\">16</property>-->\n\t\t<!--\n\t\t\t<property name=\"serverPort\">8066</property>\n\t\t\t<property name=\"managerPort\">9066</property>\n\t\t\t<property name=\"idleTimeout\">300000</property>\n\t\t\t<property name=\"authTimeout\">15000</property>\n\t\t\t<property name=\"bindIp\">0.0.0.0</property>\n\t\t\t<property name=\"dataNodeIdleCheckPeriod\">300000</property> 5 * 60 * 1000L; //连接空闲检查\n\t\t\t<property name=\"frontWriteQueueSize\">4096</property> <property name=\"processors\">32</property> -->\n\t\t<!--分布式事务开关，0为不过滤分布式事务，1为过滤分布式事务（如果分布式事务内只涉及全局表，则不过滤），2为不过滤分布式事务,但是记录分布式事务日志-->\n\t\t<property name=\"handleDistributedTransactions\">0</property>\n\t\t\n\t\t\t<!--\n\t\t\toff heap for merge/order/group/limit      1开启   0关闭\n\t\t-->\n\t\t<property name=\"useOffHeapForMerge\">0</property>\n\n\t\t<!--\n\t\t\t单位为m\n\t\t-->\n        <property name=\"memoryPageSize\">64k</property>\n\n\t\t<!--\n\t\t\t单位为k\n\t\t-->\n\t\t<property name=\"spillsFileBufferSize\">1k</property>\n\n\t\t<property name=\"useStreamOutput\">0</property>\n\n\t\t<!--\n\t\t\t单位为m\n\t\t-->\n\t\t<property name=\"systemReserveMemorySize\">384m</property>\n\n\n\t\t<!--是否采用zookeeper协调切换  -->\n\t\t<property name=\"useZKSwitch\">false</property>\n\n\t\t<!-- XA Recovery Log日志路径 -->\n\t\t<!--<property name=\"XARecoveryLogBaseDir\">./</property>-->\n\n\t\t<!-- XA Recovery Log日志名称 -->\n\t\t<!--<property name=\"XARecoveryLogBaseName\">tmlog</property>-->\n\t\t<!--如果为 true的话 严格遵守隔离级别,不会在仅仅只有select语句的时候在事务中切换连接-->\n\t\t<property name=\"strictTxIsolation\">true</property>\n\t\t<!--如果为0的话,涉及多个DataNode的catlet任务不会跨线程执行-->\n\t\t<property name=\"parallExecute\">0</property>\n\t\t<property name=\"serverBacklog\">2048</property>\n\t</system>\n\t\n\t<!-- 全局SQL防火墙设置 -->\n\t<!--白名单可以使用通配符%或着*-->\n\t<!--例如<host host=\"127.0.0.*\" user=\"root\"/>-->\n\t<!--例如<host host=\"127.0.*\" user=\"root\"/>-->\n\t<!--例如<host host=\"127.*\" user=\"root\"/>-->\n\t<!--例如<host host=\"1*7.*\" user=\"root\"/>-->\n\t<!--这些配置情况下对于127.0.0.1都能以root账户登录-->\n\t<!--\n\t<firewall>\n\t   <whitehost>\n\t      <host host=\"1*7.0.0.*\" user=\"root\"/>\n\t   </whitehost>\n       <blacklist check=\"false\">\n       </blacklist>\n\t</firewall>\n\t-->\n\n\t<user name=\"root\" defaultAccount=\"true\">\n\t\t<property name=\"password\">123456</property>\n\t\t<property name=\"schemas\">TESTDB</property>\n\t\t<property name=\"defaultSchema\">TESTDB</property>\n\t\t<!--No MyCAT Database selected 错误前会尝试使用该schema作为schema，不设置则为null,报错 -->\n\t\t\n\t\t<!-- 表级 DML 权限设置 -->\n\t\t<!-- \t\t\n\t\t<privileges check=\"false\">\n\t\t\t<schema name=\"TESTDB\" dml=\"0110\" >\n\t\t\t\t<table name=\"tb01\" dml=\"0000\"></table>\n\t\t\t\t<table name=\"tb02\" dml=\"1111\"></table>\n\t\t\t</schema>\n\t\t</privileges>\t\t\n\t\t -->\n\t</user>\n\n\t<user name=\"user\">\n\t\t<property name=\"password\">user</property>\n\t\t<property name=\"schemas\">TESTDB</property>\n\t\t<property name=\"readOnly\">true</property>\n\t\t<property name=\"defaultSchema\">TESTDB</property>\n\t</user>\n\n</mycat:server>\n"
  },
  {
    "path": "src/main/resources/sharding-by-enum.txt",
    "content": "10000=0\n10010=1\n"
  },
  {
    "path": "src/main/resources/zkconf/auto-sharding-long.txt",
    "content": "2000001-4000000=1\n0-2000000=0\n4000001-8000000=2\n"
  },
  {
    "path": "src/main/resources/zkconf/auto-sharding-rang-mod.txt",
    "content": "800M1-1000M=6\n600M1-800M=4\n200M1-400M=1\n0-200M=5\n400M1-600M=4\n"
  },
  {
    "path": "src/main/resources/zkconf/autopartition-long.txt",
    "content": "# range start-end ,data node index\n# K=1000,M=10000.\n0-500M=0\n500M-1000M=1\n1000M-1500M=2"
  },
  {
    "path": "src/main/resources/zkconf/cacheservice.properties",
    "content": "#used for mycat cache service conf\nfactory.encache=io.mycat.cache.impl.EnchachePooFactory\n#key is pool name ,value is type,max size, expire seconds\npool.SQLRouteCache=encache,10000,1800\npool.ER_SQL2PARENTID=encache,1000,1800\nlayedpool.TableID2DataNodeCache=encache,10000,18000\nlayedpool.TableID2DataNodeCache.TESTDB_ORDERS=50000,18000"
  },
  {
    "path": "src/main/resources/zkconf/ehcache.xml",
    "content": "<ehcache xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:noNamespaceSchemaLocation=\"ehcache.xsd\" maxEntriesLocalHeap=\"100000000\"\n\tmaxBytesLocalDisk=\"50G\" updateCheck=\"false\" >\n\t<defaultCache maxElementsInMemory=\"1000000\" eternal=\"false\"\n\t\toverflowToDisk=\"false\" diskSpoolBufferSizeMB=\"30\" maxElementsOnDisk=\"10000000\"\n\t\tdiskPersistent=\"false\" diskExpiryThreadIntervalSeconds=\"120\"\n\t\tmemoryStoreEvictionPolicy=\"LRU\" />\n</ehcache>"
  },
  {
    "path": "src/main/resources/zkconf/index_to_charset.properties",
    "content": "1=big5\n2=latin2\n3=dec8\n4=cp850\n5=latin1\n6=hp8\n7=koi8r\n8=latin1\n9=latin2\n10=swe7\n11=ascii\n12=ujis\n13=sjis\n14=cp1251\n15=latin1\n16=hebrew\n18=tis620\n19=euckr\n20=latin7\n21=latin2\n22=koi8u\n23=cp1251\n24=gb2312\n25=greek\n26=cp1250\n27=latin2\n28=gbk\n29=cp1257\n30=latin5\n31=latin1\n32=armscii8\n33=utf8\n34=cp1250\n35=ucs2\n36=cp866\n37=keybcs2\n38=macce\n39=macroman\n40=cp852\n41=latin7\n42=latin7\n43=macce\n44=cp1250\n45=utf8mb4\n46=utf8mb4\n47=latin1\n48=latin1\n49=latin1\n50=cp1251\n51=cp1251\n52=cp1251\n53=macroman\n54=utf16\n55=utf16\n56=utf16le\n57=cp1256\n58=cp1257\n59=cp1257\n60=utf32\n61=utf32\n62=utf16le\n63=binary\n64=armscii8\n65=ascii\n66=cp1250\n67=cp1256\n68=cp866\n69=dec8\n70=greek\n71=hebrew\n72=hp8\n73=keybcs2\n74=koi8r\n75=koi8u\n77=latin2\n78=latin5\n79=latin7\n80=cp850\n81=cp852\n82=swe7\n83=utf8\n84=big5\n85=euckr\n86=gb2312\n87=gbk\n88=sjis\n89=tis620\n90=ucs2\n91=ujis\n92=geostd8\n93=geostd8\n94=latin1\n95=cp932\n96=cp932\n97=eucjpms\n98=eucjpms\n99=cp1250\n101=utf16\n102=utf16\n103=utf16\n104=utf16\n105=utf16\n106=utf16\n107=utf16\n108=utf16\n109=utf16\n110=utf16\n111=utf16\n112=utf16\n113=utf16\n114=utf16\n115=utf16\n116=utf16\n117=utf16\n118=utf16\n119=utf16\n120=utf16\n121=utf16\n122=utf16\n123=utf16\n124=utf16\n128=ucs2\n129=ucs2\n130=ucs2\n131=ucs2\n132=ucs2\n133=ucs2\n134=ucs2\n135=ucs2\n136=ucs2\n137=ucs2\n138=ucs2\n139=ucs2\n140=ucs2\n141=ucs2\n142=ucs2\n143=ucs2\n144=ucs2\n145=ucs2\n146=ucs2\n147=ucs2\n148=ucs2\n149=ucs2\n150=ucs2\n151=ucs2\n159=ucs2\n160=utf32\n161=utf32\n162=utf32\n163=utf32\n164=utf32\n165=utf32\n166=utf32\n167=utf32\n168=utf32\n169=utf32\n170=utf32\n171=utf32\n172=utf32\n173=utf32\n174=utf32\n175=utf32\n176=utf32\n177=utf32\n178=utf32\n179=utf32\n180=utf32\n181=utf32\n182=utf32\n183=utf32\n192=utf8\n193=utf8\n194=utf8\n195=utf8\n196=utf8\n197=utf8\n198=utf8\n199=utf8\n200=utf8\n201=utf8\n202=utf8\n203=utf8\n204=utf8\n205=utf8\n206=utf8\n207=utf8\n208=utf8\n209=utf8\n210=utf8\n211=utf8\n212=utf8\n213=utf8\n214=utf8\n215=utf8\n223=utf8\n224=utf8mb4\n225=utf8mb4\n226=utf8mb4\n227=utf8mb4\n228=utf8mb4\n229=utf8mb4\n230=utf8mb4\n231=utf8mb4\n232=utf8mb4\n233=utf8mb4\n234=utf8mb4\n235=utf8mb4\n236=utf8mb4\n237=utf8mb4\n238=utf8mb4\n239=utf8mb4\n240=utf8mb4\n241=utf8mb4\n242=utf8mb4\n243=utf8mb4\n244=utf8mb4\n245=utf8mb4\n246=utf8mb4\n247=utf8mb4"
  },
  {
    "path": "src/main/resources/zkconf/partition-hash-int.txt",
    "content": "10000=0\n10010=1"
  },
  {
    "path": "src/main/resources/zkconf/partition-range-mod.txt",
    "content": "# range start-end ,data node group size\n0-200M=5\n200M1-400M=1\n400M1-600M=4\n600M1-800M=4\n800M1-1000M=6\n"
  },
  {
    "path": "src/main/resources/zkconf/rule.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- - - Licensed under the Apache License, Version 2.0 (the \"License\"); \n\t- you may not use this file except in compliance with the License. - You \n\tmay obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 \n\t- - Unless required by applicable law or agreed to in writing, software - \n\tdistributed under the License is distributed on an \"AS IS\" BASIS, - WITHOUT \n\tWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the \n\tLicense for the specific language governing permissions and - limitations \n\tunder the License. -->\n<!DOCTYPE mycat:rule SYSTEM \"rule.dtd\">\n<mycat:rule xmlns:mycat=\"http://io.mycat/\">\n\t<tableRule name=\"rule1\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>func1</algorithm>\n\t\t</rule>\n\t</tableRule>\n\n\t<tableRule name=\"rule2\">\n\t\t<rule>\n\t\t\t<columns>user_id</columns>\n\t\t\t<algorithm>func1</algorithm>\n\t\t</rule>\n\t</tableRule>\n\n\t<tableRule name=\"sharding-by-intfile\">\n\t\t<rule>\n\t\t\t<columns>sharding_id</columns>\n\t\t\t<algorithm>hash-int</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"auto-sharding-long\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>rang-long</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"mod-long\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>mod-long</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"sharding-by-murmur\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>murmur</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"sharding-by-month\">\n\t\t<rule>\n\t\t\t<columns>create_date</columns>\n\t\t\t<algorithm>partbymonth</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"latest-month-calldate\">\n\t\t<rule>\n\t\t\t<columns>calldate</columns>\n\t\t\t<algorithm>latestMonth</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"crc32slot\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>crc32slot</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"auto-sharding-rang-mod\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>rang-mod</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t\n\t<tableRule name=\"jch\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>jump-consistent-hash</algorithm>\n\t\t</rule>\n\t</tableRule>\n\n\t<function name=\"murmur\"\n\t\tclass=\"io.mycat.route.function.PartitionByMurmurHash\">\n\t\t<property name=\"seed\">0</property><!-- 默认是0 -->\n\t\t<property name=\"count\">2</property><!-- 要分片的数据库节点数量，必须指定，否则没法分片 -->\n\t\t<property name=\"virtualBucketTimes\">160</property><!-- 一个实际的数据库节点被映射为这么多虚拟节点，默认是160倍，也就是虚拟节点数是物理节点数的160倍 -->\n\t\t<!-- <property name=\"weightMapFile\">weightMapFile</property> 节点的权重，没有指定权重的节点默认是1。以properties文件的格式填写，以从0开始到count-1的整数值也就是节点索引为key，以节点权重值为值。所有权重值必须是正整数，否则以1代替 -->\n\t\t<!-- <property name=\"bucketMapPath\">/etc/mycat/bucketMapPath</property> \n\t\t\t用于测试时观察各物理节点与虚拟节点的分布情况，如果指定了这个属性，会把虚拟节点的murmur hash值与物理节点的映射按行输出到这个文件，没有默认值，如果不指定，就不会输出任何东西 -->\n\t</function>\n\t<function name=\"hash-int\"\n\t\tclass=\"io.mycat.route.function.PartitionByFileMap\">\n\t\t<property name=\"mapFile\">partition-hash-int.txt</property>\n\t</function>\n\t<function name=\"rang-long\"\n\t\tclass=\"io.mycat.route.function.AutoPartitionByLong\">\n\t\t<property name=\"mapFile\">autopartition-long.txt</property>\n\t</function>\n\t<function name=\"mod-long\" class=\"io.mycat.route.function.PartitionByMod\">\n\t\t<!-- how many data nodes -->\n\t\t<property name=\"count\">3</property>\n\t</function>\n\t<function name=\"crc32slot\"\n\t\t\t  class=\"io.mycat.route.function.PartitionByCRC32PreSlot\">\n\t</function>\n\t<function name=\"func1\" class=\"io.mycat.route.function.PartitionByLong\">\n\t\t<property name=\"partitionCount\">8</property>\n\t\t<property name=\"partitionLength\">128</property>\n\t</function>\n\t<function name=\"latestMonth\"\n\t\tclass=\"io.mycat.route.function.LatestMonthPartion\">\n\t\t<property name=\"splitOneDay\">24</property>\n\t</function>\n\t<function name=\"partbymonth\"\n\t\tclass=\"io.mycat.route.function.PartitionByMonth\">\n\t\t<property name=\"dateFormat\">yyyy-MM-dd</property>\n\t\t<property name=\"sBeginDate\">2015-01-01</property>\n\t</function>\n\t\n\t<function name=\"rang-mod\" class=\"io.mycat.route.function.PartitionByRangeMod\">\n        \t<property name=\"mapFile\">partition-range-mod.txt</property>\n\t</function>\n\t\n\t<function name=\"jump-consistent-hash\" class=\"io.mycat.route.function.PartitionByJumpConsistentHash\">\n\t\t<property name=\"totalBuckets\">3</property>\n\t</function>\n</mycat:rule>\n"
  },
  {
    "path": "src/main/resources/zkconf/schema.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE mycat:schema SYSTEM \"schema.dtd\">\n<mycat:schema xmlns:mycat=\"http://io.mycat/\">\n\n\t<schema name=\"TESTDB\" checkSQLschema=\"false\" sqlMaxLimit=\"100\">\n\t\t<!-- auto sharding by id (long) -->\n\t\t<table name=\"travelrecord\" dataNode=\"dn1,dn2,dn3\" rule=\"auto-sharding-long\" />\n\n\t\t<!-- global table is auto cloned to all defined data nodes ,so can join\n\t\t\twith any table whose sharding node is in the same data node -->\n\t\t<table name=\"company\" primaryKey=\"ID\" type=\"global\" dataNode=\"dn1,dn2,dn3\" />\n\t\t<table name=\"goods\" primaryKey=\"ID\" type=\"global\" dataNode=\"dn1,dn2\" />\n\t\t<!-- random sharding using mod sharind rule -->\n\t\t<table name=\"hotnews\" primaryKey=\"ID\" autoIncrement=\"true\" dataNode=\"dn1,dn2,dn3\"\n\t\t\t   rule=\"mod-long\" />\n\t\t<!-- <table name=\"dual\" primaryKey=\"ID\" dataNode=\"dnx,dnoracle2\" type=\"global\"\n\t\t\tneedAddLimit=\"false\"/> <table name=\"worker\" primaryKey=\"ID\" dataNode=\"jdbc_dn1,jdbc_dn2,jdbc_dn3\"\n\t\t\trule=\"mod-long\" /> -->\n\t\t<table name=\"employee\" primaryKey=\"ID\" dataNode=\"dn1,dn2\"\n\t\t\t   rule=\"sharding-by-intfile\" />\n\t\t<table name=\"customer\" primaryKey=\"ID\" dataNode=\"dn1,dn2\"\n\t\t\t   rule=\"sharding-by-intfile\">\n\t\t\t<childTable name=\"orders\" primaryKey=\"ID\" joinKey=\"customer_id\"\n\t\t\t\t\t\tparentKey=\"id\">\n\t\t\t\t<childTable name=\"order_items\" joinKey=\"order_id\"\n\t\t\t\t\t\t\tparentKey=\"id\" />\n\t\t\t</childTable>\n\t\t\t<childTable name=\"customer_addr\" primaryKey=\"ID\" joinKey=\"customer_id\"\n\t\t\t\t\t\tparentKey=\"id\" />\n\t\t</table>\n\t\t<!-- <table name=\"oc_call\" primaryKey=\"ID\" dataNode=\"dn1$0-743\" rule=\"latest-month-calldate\"\n\t\t\t/> -->\n\t</schema>\n\t<!-- <dataNode name=\"dn1$0-743\" dataHost=\"localhost1\" database=\"db$0-743\"\n\t\t/> -->\n\t<dataNode name=\"dn1\" dataHost=\"localhost1\" database=\"db1\" />\n\t<dataNode name=\"dn2\" dataHost=\"localhost1\" database=\"db2\" />\n\t<dataNode name=\"dn3\" dataHost=\"localhost1\" database=\"db3\" />\n\t<!--<dataNode name=\"dn4\" dataHost=\"sequoiadb1\" database=\"SAMPLE\" />\n\t <dataNode name=\"jdbc_dn1\" dataHost=\"jdbchost\" database=\"db1\" />\n\t<dataNode\tname=\"jdbc_dn2\" dataHost=\"jdbchost\" database=\"db2\" />\n\t<dataNode name=\"jdbc_dn3\" \tdataHost=\"jdbchost\" database=\"db3\" /> -->\n\t<dataHost name=\"localhost1\" maxCon=\"1000\" minCon=\"10\" balance=\"0\"\n\t\t\t  writeType=\"0\" dbType=\"mysql\" dbDriver=\"native\" switchType=\"1\"  slaveThreshold=\"100\">\n\t\t<heartbeat>select user()</heartbeat>\n\t\t<!-- can have multi write hosts -->\n\t\t<writeHost host=\"hostM1\" url=\"localhost:3306\" user=\"root\"\n\t\t\t\t   password=\"123456\">\n\t\t\t<!-- can have multi read hosts -->\n\t\t\t<readHost host=\"hostS2\" url=\"192.168.1.200:3306\" user=\"root\" password=\"xxx\" />\n\t\t</writeHost>\n\t\t<writeHost host=\"hostS1\" url=\"localhost:3316\" user=\"root\"\n\t\t\t\t   password=\"123456\" />\n\t\t<!-- <writeHost host=\"hostM2\" url=\"localhost:3316\" user=\"root\" password=\"123456\"/> -->\n\t</dataHost>\n\t<!--\n\t\t<dataHost name=\"sequoiadb1\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" dbType=\"sequoiadb\" dbDriver=\"jdbc\">\n\t\t<heartbeat> \t\t</heartbeat>\n\t\t <writeHost host=\"hostM1\" url=\"sequoiadb://1426587161.dbaas.sequoialab.net:11920/SAMPLE\" user=\"jifeng\" \tpassword=\"jifeng\"></writeHost>\n\t\t </dataHost>\n\n\t  <dataHost name=\"oracle1\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" writeType=\"0\" \tdbType=\"oracle\" dbDriver=\"jdbc\"> <heartbeat>select 1 from dual</heartbeat>\n\t\t<connectionInitSql>alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'</connectionInitSql>\n\t\t<writeHost host=\"hostM1\" url=\"jdbc:oracle:thin:@127.0.0.1:1521:nange\" user=\"base\" \tpassword=\"123456\" > </writeHost> </dataHost>\n\n\t\t<dataHost name=\"jdbchost\" maxCon=\"1000\" \tminCon=\"1\" balance=\"0\" writeType=\"0\" dbType=\"mongodb\" dbDriver=\"jdbc\">\n\t\t<heartbeat>select \tuser()</heartbeat>\n\t\t<writeHost host=\"hostM\" url=\"mongodb://192.168.0.99/test\" user=\"admin\" password=\"123456\" ></writeHost> </dataHost>\n\n\t\t<dataHost name=\"sparksql\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" dbType=\"spark\" dbDriver=\"jdbc\">\n\t\t<heartbeat> </heartbeat>\n\t\t <writeHost host=\"hostM1\" url=\"jdbc:hive2://feng01:10000\" user=\"jifeng\" \tpassword=\"jifeng\"></writeHost> </dataHost> -->\n\n\t<!-- <dataHost name=\"jdbchost\" maxCon=\"1000\" minCon=\"10\" balance=\"0\" dbType=\"mysql\"\n\t\tdbDriver=\"jdbc\"> <heartbeat>select user()</heartbeat> <writeHost host=\"hostM1\"\n\t\turl=\"jdbc:mysql://localhost:3306\" user=\"root\" password=\"123456\"> </writeHost>\n\t\t</dataHost> -->\n</mycat:schema>"
  },
  {
    "path": "src/main/resources/zkconf/sequence_conf.properties",
    "content": "#default global sequence\nGLOBAL.HISIDS=\nGLOBAL.MINID=10001\nGLOBAL.MAXID=20000\nGLOBAL.CURID=10000\n\n# self define sequence\nCOMPANY.HISIDS=\nCOMPANY.MINID=1001\nCOMPANY.MAXID=2000\nCOMPANY.CURID=1000\n\nCUSTOMER.HISIDS=\nCUSTOMER.MINID=1001\nCUSTOMER.MAXID=2000\nCUSTOMER.CURID=1000\n\nORDER.HISIDS=\nORDER.MINID=1001\nORDER.MAXID=2000\nORDER.CURID=1000\n\nHOTNEWS.HISIDS=\nHOTNEWS.MINID=1001\nHOTNEWS.MAXID=2000\nHOTNEWS.CURID=1000\n\n"
  },
  {
    "path": "src/main/resources/zkconf/sequence_db_conf.properties",
    "content": "#sequence stored in datanode\nGLOBAL=dn1\nCOMPANY=dn1\nCUSTOMER=dn1\nORDERS=dn1"
  },
  {
    "path": "src/main/resources/zkconf/sequence_distributed_conf-mycat_fz_01.properties",
    "content": "INSTANCEID=02\nCLUSTERID=02\n"
  },
  {
    "path": "src/main/resources/zkconf/sequence_distributed_conf.properties",
    "content": "INSTANCEID=01\nCLUSTERID=01\n"
  },
  {
    "path": "src/main/resources/zkconf/sequence_time_conf-mycat_fz_01.properties",
    "content": "#sequence depend on TIME\nWORKID=03\nDATAACENTERID=03"
  },
  {
    "path": "src/main/resources/zkconf/sequence_time_conf.properties",
    "content": "#sequence depend on TIME\nWORKID=01\nDATAACENTERID=01"
  },
  {
    "path": "src/main/resources/zkconf/server-mycat_fz_01.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- - - Licensed under the Apache License, Version 2.0 (the \"License\"); \n\t- you may not use this file except in compliance with the License. - You \n\tmay obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 \n\t- - Unless required by applicable law or agreed to in writing, software - \n\tdistributed under the License is distributed on an \"AS IS\" BASIS, - WITHOUT \n\tWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the \n\tLicense for the specific language governing permissions and - limitations \n\tunder the License. -->\n<!DOCTYPE mycat:server SYSTEM \"server.dtd\">\n<mycat:server xmlns:mycat=\"http://io.mycat/\">\n\t<system>\n\t<property name=\"useSqlStat\">1</property>  <!-- 1为开启实时统计、0为关闭 -->\n\t<property name=\"useGlobleTableCheck\">0</property>  <!-- 1为开启全加班一致性检测、0为关闭 -->\n\t<property name=\"defaultSqlParser\">druidparser</property>\n\t\t<property name=\"sequenceHandlerType\">2</property>\n      <!--  <property name=\"useCompression\">1</property>--> <!--1为开启mysql压缩协议-->\n        <!--  <property name=\"fakeMySQLVersion\">5.6.20</property>--> <!--设置模拟的MySQL版本号-->\n\t<!-- <property name=\"processorBufferChunk\">40960</property> -->\n\t<!-- \n\t<property name=\"processors\">1</property> \n\t<property name=\"processorExecutor\">32</property> \n\t -->\n\t\t<!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena-->\n\t\t<property name=\"processorBufferPoolType\">0</property>\n\t\t<!--默认是65535 64K 用于sql解析时最大文本长度 -->\n\t\t<!--<property name=\"maxStringLiteralLength\">65535</property>-->\n\t\t<!--<property name=\"sequenceHandlerType\">0</property>-->\n\t\t<!--<property name=\"backSocketNoDelay\">1</property>-->\n\t\t<!--<property name=\"frontSocketNoDelay\">1</property>-->\n\t\t<!--<property name=\"processorExecutor\">16</property>-->\n\t\t<!-- \n\t\t\t<property name=\"mutiNodeLimitType\">1</property> 0：开启小数量级（默认） ；1：开启亿级数据排序\n\t    \t<property name=\"mutiNodePatchSize\">100</property> 亿级数量排序批量\n\t\t\t<property name=\"processors\">32</property> <property name=\"processorExecutor\">32</property> \n\t\t\t<property name=\"serverPort\">8066</property> <property name=\"managerPort\">9066</property> \n\t\t\t<property name=\"idleTimeout\">300000</property> <property name=\"bindIp\">0.0.0.0</property> \n\t\t\t<property name=\"frontWriteQueueSize\">4096</property> <property name=\"processors\">32</property> -->\n\t\t<!--分布式事务开关，0为不过滤分布式事务，1为过滤分布式事务（如果分布式事务内只涉及全局表，则不过滤），2为不过滤分布式事务,但是记录分布式事务日志-->\n\t\t<property name=\"handleDistributedTransactions\">0</property>\n\t\t\n\t\t\t<!--\n\t\t\toff heap for merge/order/group/limit      1开启   0关闭\n\t\t-->\n\t\t<property name=\"useOffHeapForMerge\">1</property>\n\n\t\t<!--\n\t\t\t单位为m\n\t\t-->\n\t\t<property name=\"memoryPageSize\">1m</property>\n\n\t\t<!--\n\t\t\t单位为k\n\t\t-->\n\t\t<property name=\"spillsFileBufferSize\">1k</property>\n\n\t\t<property name=\"useStreamOutput\">0</property>\n\n\t\t<!--\n\t\t\t单位为m\n\t\t-->\n\t\t<property name=\"systemReserveMemorySize\">389m</property>\n\t</system>\n\t<user name=\"root\">\n\t\t<property name=\"password\">digdeep</property>\n\t\t<property name=\"schemas\">TESTDB</property>\n\t</user>\n\n\t<user name=\"user\">\n\t\t<property name=\"password\">user</property>\n\t\t<property name=\"schemas\">TESTDB</property>\n\t\t<property name=\"readOnly\">true</property>\n\t</user>\n\t<!-- \n\t<quarantine> \n\t   <whitehost>\n\t      <host host=\"127.0.0.1\" user=\"mycat\"/>\n\t      <host host=\"127.0.0.2\" user=\"mycat\"/>\n\t   </whitehost>\n       <blacklist check=\"false\"></blacklist>\n\t</quarantine>\n\t-->\n\n</mycat:server>\n"
  },
  {
    "path": "src/main/resources/zkconf/server.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- - - Licensed under the Apache License, Version 2.0 (the \"License\"); \n\t- you may not use this file except in compliance with the License. - You \n\tmay obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 \n\t- - Unless required by applicable law or agreed to in writing, software - \n\tdistributed under the License is distributed on an \"AS IS\" BASIS, - WITHOUT \n\tWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the \n\tLicense for the specific language governing permissions and - limitations \n\tunder the License. -->\n<!DOCTYPE mycat:server SYSTEM \"server.dtd\">\n<mycat:server xmlns:mycat=\"http://io.mycat/\">\n\t<system>\n\t<property name=\"useSqlStat\">1</property>  <!-- 1为开启实时统计、0为关闭 -->\n\t<property name=\"useGlobleTableCheck\">0</property>  <!-- 1为开启全加班一致性检测、0为关闭 -->\n\t<property name=\"defaultSqlParser\">druidparser</property>\n\t\t<property name=\"sequenceHandlerType\">2</property>\n      <!--  <property name=\"useCompression\">1</property>--> <!--1为开启mysql压缩协议-->\n        <!--  <property name=\"fakeMySQLVersion\">5.6.20</property>--> <!--设置模拟的MySQL版本号-->\n\t<!-- <property name=\"processorBufferChunk\">40960</property> -->\n\t<!-- \n\t<property name=\"processors\">1</property> \n\t<property name=\"processorExecutor\">32</property> \n\t -->\n\t\t<!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena-->\n\t\t<property name=\"processorBufferPoolType\">0</property>\n\t\t<!--默认是65535 64K 用于sql解析时最大文本长度 -->\n\t\t<!--<property name=\"maxStringLiteralLength\">65535</property>-->\n\t\t<!--<property name=\"sequenceHandlerType\">0</property>-->\n\t\t<!--<property name=\"backSocketNoDelay\">1</property>-->\n\t\t<!--<property name=\"frontSocketNoDelay\">1</property>-->\n\t\t<!--<property name=\"processorExecutor\">16</property>-->\n\t\t<!-- \n\t\t\t<property name=\"mutiNodeLimitType\">1</property> 0：开启小数量级（默认） ；1：开启亿级数据排序\n\t    \t<property name=\"mutiNodePatchSize\">100</property> 亿级数量排序批量\n\t\t\t<property name=\"processors\">32</property> <property name=\"processorExecutor\">32</property> \n\t\t\t<property name=\"serverPort\">8066</property> <property name=\"managerPort\">9066</property> \n\t\t\t<property name=\"idleTimeout\">300000</property> <property name=\"bindIp\">0.0.0.0</property> \n\t\t\t<property name=\"frontWriteQueueSize\">4096</property> <property name=\"processors\">32</property> -->\n\t\t<!--分布式事务开关，0为不过滤分布式事务，1为过滤分布式事务（如果分布式事务内只涉及全局表，则不过滤），2为不过滤分布式事务,但是记录分布式事务日志-->\n\t\t<property name=\"handleDistributedTransactions\">0</property>\n\t\t\n\t\t\t<!--\n\t\t\toff heap for merge/order/group/limit      1开启   0关闭\n\t\t-->\n\t\t<property name=\"useOffHeapForMerge\">1</property>\n\n\t\t<!--\n\t\t\t单位为m\n\t\t-->\n\t\t<property name=\"memoryPageSize\">1m</property>\n\n\t\t<!--\n\t\t\t单位为k\n\t\t-->\n\t\t<property name=\"spillsFileBufferSize\">1k</property>\n\n\t\t<property name=\"useStreamOutput\">0</property>\n\n\t\t<!--\n\t\t\t单位为m\n\t\t-->\n\t\t<property name=\"systemReserveMemorySize\">384m</property>\n\t</system>\n\t<user name=\"root\">\n\t\t<property name=\"password\">digdeep</property>\n\t\t<property name=\"schemas\">TESTDB</property>\n\t</user>\n\n\t<user name=\"user\">\n\t\t<property name=\"password\">user</property>\n\t\t<property name=\"schemas\">TESTDB</property>\n\t\t<property name=\"readOnly\">true</property>\n\t</user>\n\t<!-- \n\t<quarantine> \n\t   <whitehost>\n\t      <host host=\"127.0.0.1\" user=\"mycat\"/>\n\t      <host host=\"127.0.0.2\" user=\"mycat\"/>\n\t   </whitehost>\n       <blacklist check=\"false\"></blacklist>\n\t</quarantine>\n\t-->\n\n</mycat:server>\n"
  },
  {
    "path": "src/main/resources/zkconf/sharding-by-enum.txt",
    "content": "10000=0\n10010=1\n"
  },
  {
    "path": "src/main/resources/zkdownload/auto-sharding-long.txt",
    "content": "2000001-4000000=1\n0-2000000=0\n4000001-8000000=2\n"
  },
  {
    "path": "src/test/java/demo/catlets/MyHellowJoin.java",
    "content": "package demo.catlets;\r\n\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\nimport java.util.concurrent.ConcurrentLinkedQueue;\r\n\r\nimport io.mycat.cache.LayerCachePool;\r\nimport io.mycat.catlets.Catlet;\r\nimport io.mycat.config.model.SchemaConfig;\r\nimport io.mycat.config.model.SystemConfig;\r\nimport io.mycat.net.mysql.RowDataPacket;\r\nimport io.mycat.server.ServerConnection;\r\nimport io.mycat.sqlengine.AllJobFinishedListener;\r\nimport io.mycat.sqlengine.EngineCtx;\r\nimport io.mycat.sqlengine.SQLJobHandler;\r\nimport io.mycat.util.ByteUtil;\r\nimport io.mycat.util.ResultSetUtil;\r\n\r\npublic class MyHellowJoin implements Catlet {\r\n\r\n\tpublic void processSQL(String sql, EngineCtx ctx) {\r\n\r\n\t\tDirectDBJoinHandler joinHandler = new DirectDBJoinHandler(ctx);\r\n\t\tString[] dataNodes = { \"dn1\", \"dn2\", \"dn3\" };\r\n\t\tctx.executeNativeSQLSequnceJob(dataNodes, sql, joinHandler);\r\n\t\tctx.setAllJobFinishedListener(new AllJobFinishedListener() {\r\n\r\n\t\t\t@Override\r\n\t\t\tpublic void onAllJobFinished(EngineCtx ctx) {\r\n\t\t\t\tctx.writeEof();\r\n\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void route(SystemConfig sysConfig, SchemaConfig schema, int sqlType,\r\n\t\t\tString realSQL, String charset, ServerConnection sc,\r\n\t\t\tLayerCachePool cachePool) {\r\n\t\t\r\n\t\t\r\n\t}\r\n}\r\n\r\nclass DirectDBJoinHandler implements SQLJobHandler {\r\n\tprivate List<byte[]> fields;\r\n\tprivate final EngineCtx ctx;\r\n\r\n\tpublic DirectDBJoinHandler(EngineCtx ctx) {\r\n\t\tsuper();\r\n\t\tthis.ctx = ctx;\r\n\t}\r\n\r\n\tprivate Map<String, byte[]> rows = new ConcurrentHashMap<String, byte[]>();\r\n\tprivate ConcurrentLinkedQueue<String> ids = new ConcurrentLinkedQueue<String>();\r\n\r\n\t@Override\r\n\tpublic void onHeader(String dataNode, byte[] header, List<byte[]> fields) {\r\n\t\tthis.fields = fields;\r\n\r\n\t}\r\n\r\n\tprivate void createQryJob(int batchSize) {\r\n\t\tint count = 0;\r\n\t\tMap<String, byte[]> batchRows = new ConcurrentHashMap<String, byte[]>();\r\n\t\tString theId = null;\r\n\t\tStringBuilder sb = new StringBuilder().append('(');\r\n\t\twhile ((theId = ids.poll()) != null) {\r\n\t\t\tbatchRows.put(theId, rows.remove(theId));\r\n\t\t\tsb.append(theId).append(',');\r\n\t\t\tif (count++ > batchSize) {\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (count == 0) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tsb.deleteCharAt(sb.length() - 1).append(')');\r\n\t\tString querySQL = \"select b.id, b.title  from hotnews b where id in \"\r\n\t\t\t\t+ sb;\r\n\t\tctx.executeNativeSQLParallJob(new String[] { \"dn1\", \"dn2\", \"dn3\" },\r\n\t\t\t\tquerySQL, new MyRowOutPutDataHandler(fields, ctx, batchRows));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean onRowData(String dataNode, byte[] rowData) {\r\n\r\n\t\tString id = ResultSetUtil.getColumnValAsString(rowData, fields, 0);\r\n\t\t// 放入结果集\r\n\t\trows.put(id, rowData);\r\n\t\tids.offer(id);\r\n\r\n\t\tint batchSize = 999;\r\n\t\t// 满1000条，发送一个查询请求\r\n\t\tif (ids.size() > batchSize) {\r\n\t\t\tcreateQryJob(batchSize);\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void finished(String dataNode, boolean failed, String errorMsg) {\r\n\t\tif (!failed) {\r\n\t\t\tcreateQryJob(Integer.MAX_VALUE);\r\n\t\t}\r\n\t\t// no more jobs\r\n\t\tctx.endJobInput();\r\n\t}\r\n\r\n}\r\n\r\nclass MyRowOutPutDataHandler implements SQLJobHandler {\r\n\tprivate final List<byte[]> afields;\r\n\tprivate List<byte[]> bfields;\r\n\tprivate final EngineCtx ctx;\r\n\tprivate final Map<String, byte[]> arows;\r\n\r\n\tpublic MyRowOutPutDataHandler(List<byte[]> afields, EngineCtx ctx,\r\n\t\t\tMap<String, byte[]> arows) {\r\n\t\tsuper();\r\n\t\tthis.afields = afields;\r\n\t\tthis.ctx = ctx;\r\n\t\tthis.arows = arows;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void onHeader(String dataNode, byte[] header, List<byte[]> bfields) {\r\n\t\tthis.bfields=bfields;\r\n\t\tctx.writeHeader(afields, bfields);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean onRowData(String dataNode, byte[] rowData) {\r\n\t\tRowDataPacket rowDataPkg = ResultSetUtil.parseRowData(rowData, bfields);\r\n\t\t// 获取Id字段，\r\n\t\tString id = ByteUtil.getString(rowDataPkg.fieldValues.get(0));\r\n\t\tbyte[] bname = rowDataPkg.fieldValues.get(1);\r\n\t\t// 查找ID对应的A表的记录\r\n\t\tbyte[] arow = arows.remove(id);\r\n\t\trowDataPkg = ResultSetUtil.parseRowData(arow, afields);\r\n\t\t// 设置b.name 字段\r\n\t\trowDataPkg.add(bname);\r\n\r\n\t\tctx.writeRow(rowDataPkg);\r\n\t\t// EngineCtx.LOGGER.info(\"out put row \");\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void finished(String dataNode, boolean failed, String errorMsg) {\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/test/java/demo/test/ByteArrayToHexArray.java",
    "content": "package demo.test;\n\nimport java.util.Arrays;\n\npublic class ByteArrayToHexArray {\n\tpublic static String buqi(String hexStr){\n\t\tif(hexStr.length() == 1){\n\t\t\treturn \"0\"+hexStr;\n\t\t}\n\t\treturn hexStr;\n\t}\n\tpublic static void main(String[] args) {\n//\t\tbyte [] array = {19, 0, 0, 8, 4, 49, 53, 53, 57, 1, 49, 4, 49, 49, 49, 53, 5, 52, 52, 51, 49, 55, -5};\t\t\n\t\tbyte [] array =\t{4, 0, 0, 0, 2, 100, 98, 50};\n\t\tfor(byte b : array){\n\t\t\t int a=b&0xff;  \n\t         System.out.print( buqi(Integer.toHexString(b)) + \":\");\n\t\t}\n\t\t\n\t\tSystem.out.println(\"\");\n\t\tfor(byte b : array){\n\t\t\t int a=b&0xff;  \n\t         System.out.print( new String(new byte[]{b}) + \"  \");\n\t\t}\n\t\t\n\t}\n}\n"
  },
  {
    "path": "src/test/java/demo/test/TestClass1.java",
    "content": "package demo.test;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.SQLException;\nimport java.sql.Statement;\n\n/**\n * @author mycat\n *\n */\npublic class TestClass1 {\n\n    public static void main( String args[] ) throws SQLException , ClassNotFoundException {\n        String jdbcdriver=\"com.mysql.jdbc.Driver\";\n        String jdbcurl=\"jdbc:mysql://127.0.0.1:8066/TESTDB?useUnicode=true&characterEncoding=utf-8\";\n        String username=\"test\";\n        String password=\"test\";\n        System.out.println(\"开始连接mysql:\"+jdbcurl);\n        Class.forName(jdbcdriver);\n        Connection c = DriverManager.getConnection(jdbcurl,username,password); \n        Statement st = c.createStatement();\n        print( \"test jdbc \" , st.executeQuery(\"select count(*) from travelrecord \")); \n        System.out.println(\"OK......\");\n    }\n\n         static void print( String name , ResultSet res )\n                    throws SQLException {\n                    System.out.println( name);\n                    ResultSetMetaData meta=res.getMetaData();                       \n                    //System.out.println( \"\\t\"+res.getRow()+\"条记录\");\n                    String  str=\"\";\n                    for(int i=1;i<=meta.getColumnCount();i++){\n                        str+=meta.getColumnName(i)+\"   \";\n                        //System.out.println( meta.getColumnName(i)+\"   \");\n                    }\n                    System.out.println(\"\\t\"+str);\n                    str=\"\";\n                    while ( res.next() ){\n                        for(int i=1;i<=meta.getColumnCount();i++){  \n                            str+= res.getString(i)+\"   \";       \n                            } \n                        System.out.println(\"\\t\"+str);\n                        str=\"\";\n                    }\n                }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/BufferPerformanceMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat;\n\nimport java.nio.ByteBuffer;\n\n/**\n * @author mycat\n */\npublic class BufferPerformanceMain {\n\n    public void getAllocate() {\n        ByteBuffer buffer = ByteBuffer.allocate(4096);\n        byte[] b = new byte[1024];\n\n        int count = 1000000;\n        System.currentTimeMillis();\n\n        long t1 = System.currentTimeMillis();\n        for (int i = 0; i < count; i++) {\n            buffer.position(0);\n            buffer.get(b);\n        }\n        long t2 = System.currentTimeMillis();\n        System.out.println(\"take time:\" + (t2 - t1) + \" ms.(Get:allocate)\");\n    }\n\n    public void getAllocateDirect() {\n        ByteBuffer buffer = ByteBuffer.allocateDirect(4096);\n        byte[] b = new byte[1024];\n\n        int count = 1000000;\n        System.currentTimeMillis();\n\n        long t1 = System.currentTimeMillis();\n        for (int i = 0; i < count; i++) {\n            buffer.position(0);\n            buffer.get(b);\n        }\n        long t2 = System.currentTimeMillis();\n        System.out.println(\"take time:\" + (t2 - t1) + \" ms.(Get:allocateDirect)\");\n    }\n\n    public void putAllocate() {\n        ByteBuffer buffer = ByteBuffer.allocate(4096);\n        byte[] b = new byte[1024];\n\n        int count = 1000000;\n        System.currentTimeMillis();\n\n        long t1 = System.currentTimeMillis();\n        for (int i = 0; i < count; i++) {\n            buffer.position(0);\n            buffer.put(b);\n        }\n        long t2 = System.currentTimeMillis();\n        System.out.println(\"take time:\" + (t2 - t1) + \" ms.(Put:allocate)\");\n    }\n\n    public void putAllocateDirect() {\n        ByteBuffer buffer = ByteBuffer.allocateDirect(4096);\n        byte[] b = new byte[1024];\n\n        int count = 1000000;\n        System.currentTimeMillis();\n\n        long t1 = System.currentTimeMillis();\n        for (int i = 0; i < count; i++) {\n            buffer.position(0);\n            buffer.put(b);\n        }\n        long t2 = System.currentTimeMillis();\n        System.out.println(\"take time:\" + (t2 - t1) + \" ms.(Put:allocateDirect)\");\n    }\n\n    public void copyArrayDirect() {\n        ByteBuffer buffer = ByteBuffer.allocateDirect(4096);\n        while (buffer.hasRemaining()) {\n            buffer.put((byte) 1);\n        }\n        byte[] b = new byte[1024];\n        int count = 10000000;\n        System.currentTimeMillis();\n\n        long t1 = System.currentTimeMillis();\n        for (int i = 0; i < count; i++) {\n            buffer.position(0);\n            buffer.get(b, 0, b.length);\n        }\n        long t2 = System.currentTimeMillis();\n        System.out.println(\"take time:\" + (t2 - t1) + \" ms.(testCopyArrayDirect)\");\n    }\n\n    public void copyArray() {\n        ByteBuffer buffer = ByteBuffer.allocate(4096);\n        while (buffer.hasRemaining()) {\n            buffer.put((byte) 1);\n        }\n        byte[] b = new byte[1024];\n        int count = 10000000;\n        System.currentTimeMillis();\n\n        long t1 = System.currentTimeMillis();\n        for (int i = 0; i < count; i++) {\n            buffer.position(0);\n            buffer.get(b, 0, b.length);\n        }\n        long t2 = System.currentTimeMillis();\n        System.out.println(\"take time:\" + (t2 - t1) + \" ms.(testCopyArray)\");\n    }\n\n    public static void main(String[] args) {\n\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/ConfigInitializerTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat;\n\nimport org.junit.Test;\n\nimport io.mycat.config.ConfigInitializer;\n\n/**\n * @author mycat\n */\npublic class ConfigInitializerTest {\n    @Test\n    public void testConfigLoader() {\n        new ConfigInitializer(true);\n    }\n}"
  },
  {
    "path": "src/test/java/io/mycat/EchoBioServer.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.ServerSocket;\nimport java.net.Socket;\n\n/**\n * @author mycat\n */\npublic class EchoBioServer implements Runnable {\n\n    private static final byte[] FIRST_BYTES = \"Welcome to MyCat Server.\".getBytes();\n\n    private final ServerSocket serverSocket;\n\n    public EchoBioServer(int port) throws IOException {\n        serverSocket = new ServerSocket(port);\n    }\n\n    @Override\n    public void run() {\n        while (true) {\n            try {\n                Socket socket = serverSocket.accept();\n                new Thread(new BioConnection(socket)).start();\n            } catch (IOException e) {\n                \n                e.printStackTrace();\n            }\n        }\n    }\n\n    private class BioConnection implements Runnable {\n\n        private Socket socket;\n        private InputStream input;\n        private OutputStream output;\n        private byte[] readBuffer;\n        private byte[] writeBuffer;\n\n        private BioConnection(Socket socket) throws IOException {\n            this.socket = socket;\n            this.input = socket.getInputStream();\n            this.output = socket.getOutputStream();\n            this.readBuffer = new byte[4096];\n            this.writeBuffer = new byte[4096];\n        }\n\n        @Override\n        public void run() {\n            try {\n                output.write(FIRST_BYTES);\n                output.flush();\n                while (true) {\n                    int got = input.read(readBuffer);\n                    output.write(writeBuffer, 0, got);\n                    // output.flush();\n                }\n            } catch (IOException e) {\n                \n                e.printStackTrace();\n                if (socket != null) {\n                    try {\n                        socket.close();\n                    } catch (IOException e1) {\n\n                        e1.printStackTrace();\n                    }\n                }\n            }\n        }\n    }\n\n    public static void main(String[] args) throws Exception {\n        new Thread(new EchoBioServer(8066)).start();\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/ExecutorTestMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat;\n\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport io.mycat.util.ExecutorUtil;\n\n/**\n * @author mycat\n */\npublic class ExecutorTestMain {\n\n    public static void main(String[] args) {\n        final AtomicLong count = new AtomicLong(0L);\n        final ThreadPoolExecutor executor = ExecutorUtil.create(\"TestExecutor\", 5);\n\n        new Thread() {\n            @Override\n            public void run() {\n                for (;;) {\n                    long c = count.get();\n                    try {\n                        Thread.sleep(5000L);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    System.out.println(\"count:\" + (count.get() - c) / 5);\n                    System.out.println(\"active:\" + executor.getActiveCount());\n                    System.out.println(\"queue:\" + executor.getQueue().size());\n                    System.out.println(\"============================\");\n                }\n            }\n        }.start();\n\n        new Thread() {\n            @Override\n            public void run() {\n                for (;;) {\n                    executor.execute(new Runnable() {\n\n                        @Override\n                        public void run() {\n                            count.incrementAndGet();\n                        }\n                    });\n                }\n            }\n        }.start();\n\n        new Thread() {\n            @Override\n            public void run() {\n                for (;;) {\n                    executor.execute(new Runnable() {\n\n                        @Override\n                        public void run() {\n                            count.incrementAndGet();\n                        }\n                    });\n                }\n            }\n        }.start();\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/SimpleCachePool.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat;\n\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport io.mycat.cache.CacheStatic;\nimport io.mycat.cache.LayerCachePool;\n\npublic class SimpleCachePool implements LayerCachePool {\n\tprivate HashMap<Object, Object> cacheMap;\n\n\tpublic SimpleCachePool() {\n\t\tlong MAX_CACHE_SIZE = getMaxSize();\n\t\tfloat factor = 0.75f;\n\t\tint capacity = (int)Math.ceil(MAX_CACHE_SIZE / factor) + 1;\n\t\tcacheMap =  new LinkedHashMap<Object, Object>(capacity,  factor, true) {\n\t\t\t@Override\n\t\t\tprotected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {\n\t\t\t\treturn size() > MAX_CACHE_SIZE;\n\t\t\t}};\n\t}\n\n\t@Override\n\tpublic void putIfAbsent(Object key, Object value) {\n\t\tcacheMap.put(key, value);\n\t}\n\n\t@Override\n\tpublic Object get(Object key) {\n\t\treturn cacheMap.get(key);\n\t}\n\n\t@Override\n\tpublic void clearCache() {\n\t\tcacheMap.clear();\n\n\t}\n\n\t@Override\n\tpublic CacheStatic getCacheStatic() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void putIfAbsent(String primaryKey, Object secondKey, Object value) {\n\t\tputIfAbsent(primaryKey+\"_\"+secondKey,value);\n\t\t\n\t}\n\n\t@Override\n\tpublic Object get(String primaryKey, Object secondKey) {\n\t\treturn get(primaryKey+\"_\"+secondKey);\n\t}\n\n\t@Override\n\tpublic Map<String, CacheStatic> getAllCacheStatic() {\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void clearCache(String cacheName) {\n\t\tif (cacheName != null) {\n\t\t\tcacheMap.remove(cacheName);\n\t\t}\n\t}\n\n\t@Override\n\tpublic long getMaxSize() {\n\t\treturn 100;\n\t}\n};"
  },
  {
    "path": "src/test/java/io/mycat/VolatileTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat;\n\nimport org.junit.Test;\n\n/**\n * @author mycat\n */\npublic class VolatileTest {\n    @Test\n    public void testNoop() {\n    }\n\n    static class VolatileObject {\n        volatile Object object = new Object();\n    }\n\n    public static void main(String[] args) {\n        final VolatileObject vo = new VolatileObject();\n\n        // set\n        new Thread() {\n            @Override\n            public void run() {\n                System.out.print(\"set...\");\n                while (true) {\n                    vo.object = new Object();\n                }\n            }\n        }.start();\n\n        // get\n        new Thread() {\n            @Override\n            public void run() {\n                System.out.print(\"get...\");\n                while (true) {\n                    Object oo = vo.object;\n                    oo.toString();\n                }\n            }\n        }.start();\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/backend/jdbc/mongodb/MongoClientPropertyHelperTest.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\n\nimport org.junit.Test;\n\nimport java.util.Properties;\n\n/**\n * @author liuxinsi\n * @mail akalxs@gmail.com\n */\npublic class MongoClientPropertyHelperTest {\n    @Test\n    public void testFormatProperties() {\n        Properties pro = new Properties();\n        pro.put(\"authMechanism\", \"SCRAM-SHA-1\");\n        pro.put(\"readPreference\", \"nearest\");\n        pro.put(\"maxPoolSize\", 10);\n        pro.put(\"ssl\", true);\n        String options = MongoClientPropertyHelper.formatProperties(pro);\n        System.out.println(options);\n\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/backend/jdbc/mongodb/MongoEmbeddedObjectProcessorTest.java",
    "content": "package io.mycat.backend.jdbc.mongodb;\n\nimport com.google.common.collect.Lists;\nimport com.mongodb.BasicDBObject;\nimport org.bson.types.ObjectId;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * @author liuxinsi\n * @mail akalxs@gmail.com\n */\npublic class MongoEmbeddedObjectProcessorTest {\n    @Test\n    public void testValueMapperWithObjectId() {\n        String id = \"5978776b8d69f75e091067ed\";\n\n        Object obj = MongoEmbeddedObjectProcessor.valueMapper(\"_id\", id, ObjectId.class);\n        if (!(obj instanceof ObjectId)) {\n            Assert.fail(\"not objectId\");\n        }\n    }\n\n    @Test\n    public void testValueMapperWithEmbeddedObject() {\n        BasicDBObject dbObj = new BasicDBObject();\n        dbObj.put(\"str\", \"t1\");\n        dbObj.put(\"inte\", 1);\n        dbObj.put(\"date\", new Date());\n        dbObj.put(\"lon\", 100L);\n        dbObj.put(\"bool\", true);\n        dbObj.put(\"strs\", new String[]{\"a\", \"b\", \"c\"});\n        dbObj.put(\"intes\", new Integer[]{1, 2, 3});\n        dbObj.put(\"bytes\", \"ttt\".getBytes());\n        dbObj.put(\"b\", \"a\".getBytes()[0]);\n\n        Object o = MongoEmbeddedObjectProcessor.valueMapper(\"embObj\", dbObj, TestObject.class);\n        if (!(o instanceof TestObject)) {\n            Assert.fail(\"not emb obj\");\n        }\n    }\n\n    @Test\n    public void testValueMapperWithDeepEmbeddedObject() {\n        BasicDBObject dbObj = new BasicDBObject();\n        dbObj.put(\"str\", \"t1\");\n        dbObj.put(\"inte\", 1);\n        dbObj.put(\"date\", new Date());\n        dbObj.put(\"lon\", 100L);\n        dbObj.put(\"bool\", true);\n        dbObj.put(\"strs\", new String[]{\"a\", \"b\", \"c\"});\n        dbObj.put(\"intes\", new Integer[]{1, 2, 3});\n        dbObj.put(\"bytes\", \"ttt\".getBytes());\n        dbObj.put(\"b\", \"a\".getBytes()[0]);\n\n        BasicDBObject embedObj = new BasicDBObject();\n        embedObj.put(\"embeddedStr\", \"e1\");\n\n        BasicDBObject deepEmbedObj1 = new BasicDBObject();\n        deepEmbedObj1.put(\"str\", \"aaa\");\n\n        BasicDBObject deepEmbedObj2 = new BasicDBObject();\n        deepEmbedObj2.put(\"str\", \"bbb\");\n\n\n        embedObj.put(\"testObjectList\", Lists.newArrayList(deepEmbedObj1, deepEmbedObj2));\n\n        dbObj.put(\"embeddedObject\", embedObj);\n\n        Object o = MongoEmbeddedObjectProcessor.valueMapper(\"embObj\", dbObj, TestObject.class);\n        if (!(o instanceof TestObject)) {\n            Assert.fail(\"not emb obj\");\n        }\n        System.out.println(o);\n    }\n}\n\nclass TestObject {\n    private ObjectId _id;\n    private String str;\n    private Integer inte;\n    private Date date;\n    private Long lon;\n    private Boolean bool;\n    private String[] strs;\n    private Integer[] intes;\n    private byte[] bytes;\n    private Byte b;\n    private EmbeddedObject embeddedObject;\n\n    public ObjectId get_id() {\n        return _id;\n    }\n\n    public void set_id(ObjectId _id) {\n        this._id = _id;\n    }\n\n    public String getStr() {\n        return str;\n    }\n\n    public void setStr(String str) {\n        this.str = str;\n    }\n\n    public Integer getInte() {\n        return inte;\n    }\n\n    public void setInte(Integer inte) {\n        this.inte = inte;\n    }\n\n    public Date getDate() {\n        return date;\n    }\n\n    public void setDate(Date date) {\n        this.date = date;\n    }\n\n    public Long getLon() {\n        return lon;\n    }\n\n    public void setLon(Long lon) {\n        this.lon = lon;\n    }\n\n    public Boolean getBool() {\n        return bool;\n    }\n\n    public void setBool(Boolean bool) {\n        this.bool = bool;\n    }\n\n    public String[] getStrs() {\n        return strs;\n    }\n\n    public void setStrs(String[] strs) {\n        this.strs = strs;\n    }\n\n    public Integer[] getIntes() {\n        return intes;\n    }\n\n    public void setIntes(Integer[] intes) {\n        this.intes = intes;\n    }\n\n    public byte[] getBytes() {\n        return bytes;\n    }\n\n    public void setBytes(byte[] bytes) {\n        this.bytes = bytes;\n    }\n\n    public Byte getB() {\n        return b;\n    }\n\n    public void setB(Byte b) {\n        this.b = b;\n    }\n\n    public EmbeddedObject getEmbeddedObject() {\n        return embeddedObject;\n    }\n\n    public void setEmbeddedObject(EmbeddedObject embeddedObject) {\n        this.embeddedObject = embeddedObject;\n    }\n}\n\nclass EmbeddedObject {\n    private String embeddedStr;\n    private List<TestObject> testObjectList;\n    private Set<String> someCodeSet;\n\n    public String getEmbeddedStr() {\n        return embeddedStr;\n    }\n\n    public void setEmbeddedStr(String embeddedStr) {\n        this.embeddedStr = embeddedStr;\n    }\n\n    public List<TestObject> getTestObjectList() {\n        return testObjectList;\n    }\n\n    public void setTestObjectList(List<TestObject> testObjectList) {\n        this.testObjectList = testObjectList;\n    }\n\n    public Set<String> getSomeCodeSet() {\n        return someCodeSet;\n    }\n\n    public void setSomeCodeSet(Set<String> someCodeSet) {\n        this.someCodeSet = someCodeSet;\n    }\n}"
  },
  {
    "path": "src/test/java/io/mycat/buffer/TestByteBufferArena.java",
    "content": "package io.mycat.buffer;\n\nimport junit.framework.Assert;\nimport org.junit.Test;\nimport sun.nio.ch.DirectBuffer;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 仿照Netty的思路，针对MyCat内存缓冲策略优化\n * 测试ByteBufferArena\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 17:19 2016/5/17\n * @see @https://github.com/netty/netty\n */\npublic class TestByteBufferArena {\n    int pageSize = 256;\n    int chunkSize = 1024 * 8;\n    int chunkCount = 8*128;\n    @Test\n    public void testAllocate() {\n        int allocTimes =  1024 ;\n        ByteBufferArena byteBufferArena = new ByteBufferArena(chunkSize,pageSize,chunkCount,8);\n        long start = System.currentTimeMillis();\n        for (int i = 0; i < allocTimes; i++) {\n//            System.out.println(\"allocate \"+i);\n//            long start=System.nanoTime();\n            int size = (i % 1024) + 1 ;\n            ByteBuffer byteBufer = byteBufferArena.allocate(size);\n            ByteBuffer byteBufer2 = byteBufferArena.allocate(size);\n            ByteBuffer byteBufer3 = byteBufferArena.allocate(size);\n//            System.out.println(\"alloc \"+size+\" usage \"+(System.nanoTime()-start));\n//            start=System.nanoTime();\n            byteBufferArena.recycle(byteBufer);\n            byteBufferArena.recycle(byteBufer3);\n//            System.out.println(\"recycle usage \"+(System.nanoTime()-start));\n        }\n        long used = (System.currentTimeMillis() - start);\n        System.out.println(\"ByteBufferArena total used time  \" + used + \" avg speed \" + allocTimes / used);\n    }\n\n    @Test\n    public void testAllocateDirect() {\n        int pageSize = 1024 ;\n        int allocTimes = 100;\n        DirectByteBufferPool pool = new DirectByteBufferPool(pageSize, (short) 256, (short) 8,0);\n        long start = System.currentTimeMillis();\n        for (int i = 0; i < allocTimes; i++) {\n            //System.out.println(\"allocate \"+i);\n            //long start=System.nanoTime();\n            int size = (i % 1024) + 1 ;\n            ByteBuffer byteBufer = pool.allocate(size);\n            ByteBuffer byteBufer2 = pool.allocate(size);\n            ByteBuffer byteBufer3 = pool.allocate(size);\n            //System.out.println(\"alloc \"+size+\" usage \"+(System.nanoTime()-start));\n            //start=System.nanoTime();\n            pool.recycle(byteBufer);\n            pool.recycle(byteBufer3);\n            //System.out.println(\"recycle usage \"+(System.nanoTime()-start));\n        }\n        long used = (System.currentTimeMillis() - start);\n//        System.out.println(\"DirectByteBufferPool total used time  \" + used + \" avg speed \" + allocTimes / used);\n    }\n\n    @Test\n    public void testExpansion(){\n        ByteBufferArena byteBufferArena = new ByteBufferArena(1024,8,1,8);\n        for (int i = 0; i < 1 ; i++) {\n            ByteBuffer byteBufer = byteBufferArena.allocate(256);\n            ByteBuffer byteBufer2 = byteBufferArena.allocate(256);\n            ByteBuffer byteBufer3 = byteBufferArena.allocate(256);\n\n            byteBufferArena.recycle(byteBufer);\n        }\n    }\n\n    @Test\n    public void testAllocateWithDifferentAddress() {\n        int size = 256;\n        int pageSize = size * 4;\n        int allocTimes = 8;\n        ByteBufferArena byteBufferArena = new ByteBufferArena(256*4,256,2,8);\n        Map<Long, ByteBuffer> buffs = new HashMap<Long, ByteBuffer>(8);\n        ByteBuffer byteBuffer = null;\n        DirectBuffer directBuffer = null;\n        ByteBuffer temp = null;\n        long address;\n        boolean failure = false;\n        for (int i = 0; i < allocTimes; i++) {\n            byteBuffer = byteBufferArena.allocate(size);\n            if (byteBuffer == null) {\n                Assert.fail(\"Should have enough memory\");\n            }\n            directBuffer = (DirectBuffer) byteBuffer;\n            address = directBuffer.address();\n            System.out.println(address);\n            temp = buffs.get(address);\n            buffs.put(address, byteBuffer);\n            if (null != temp) {\n                failure = true;\n                break;\n            }\n        }\n\n        for (ByteBuffer buff : buffs.values()) {\n            byteBufferArena.recycle(buff);\n        }\n\n        if (failure == true) {\n            Assert.fail(\"Allocate with same address\");\n        }\n    }\n\n    @Test\n    public void testAllocateNullWhenOutOfMemory() {\n        int size = 256;\n        int pageSize = size * 4;\n        int allocTimes = 9;\n        ByteBufferArena pool = new ByteBufferArena(256*4,256,2,8);;\n        long start = System.currentTimeMillis();\n        ByteBuffer byteBuffer = null;\n        List<ByteBuffer> buffs = new ArrayList<ByteBuffer>();\n        int i = 0;\n        for (; i < allocTimes; i++) {\n            byteBuffer = pool.allocate(size);\n            if (byteBuffer == null) {\n                break;\n            }\n            buffs.add(byteBuffer);\n        }\n        for (ByteBuffer buff : buffs) {\n            pool.recycle(buff);\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/buffer/TestDirectByteBufferPool.java",
    "content": "package io.mycat.buffer;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport junit.framework.Assert;\nimport org.junit.Test;\n\nimport sun.nio.ch.DirectBuffer;\n\npublic class TestDirectByteBufferPool {\n\n    @Test\n    public void testAllocate() {\n        int pageSize = 1024 ;\n        int allocTimes = 1024;\n        DirectByteBufferPool pool = new DirectByteBufferPool(pageSize, (short) 256, (short) 8,0);\n        long start = System.currentTimeMillis();\n        for (int i = 0; i < allocTimes; i++) {\n            //System.out.println(\"allocate \"+i);\n            //long start=System.nanoTime();\n            int size = (i % 1024) + 1 ;\n            ByteBuffer byteBufer = pool.allocate(size);\n            ByteBuffer byteBufer2 = pool.allocate(size);\n            ByteBuffer byteBufer3 = pool.allocate(size);\n            //System.out.println(\"alloc \"+size+\" usage \"+(System.nanoTime()-start));\n            //start=System.nanoTime();\n            pool.recycle(byteBufer);\n            pool.recycle(byteBufer3);\n            //System.out.println(\"recycle usage \"+(System.nanoTime()-start));\n        }\n        long used = (System.currentTimeMillis() - start);\n        System.out.println(\"total used time  \" + used + \" avg speed \" + allocTimes / used);\n    }\n\n    @Test\n    public void testAllocateWithDifferentAddress() {\n        int size = 256;\n        int pageSize = size * 4;\n        int allocTimes = 8;\n        DirectByteBufferPool pool = new DirectByteBufferPool(pageSize, (short) 256, (short) 2,0);\n\n        Map<Long, ByteBuffer> buffs = new HashMap<Long, ByteBuffer>(8);\n        ByteBuffer byteBuffer = null;\n        DirectBuffer directBuffer = null;\n        ByteBuffer temp = null;\n        long address;\n        boolean failure = false;\n        for (int i = 0; i < allocTimes; i++) {\n            byteBuffer = pool.allocate(size);\n            if (byteBuffer == null) {\n                Assert.fail(\"Should have enough memory\");\n            }\n            directBuffer = (DirectBuffer) byteBuffer;\n            address = directBuffer.address();\n            System.out.println(address);\n            temp = buffs.get(address);\n            buffs.put(address, byteBuffer);\n            if (null != temp) {\n                failure = true;\n                break;\n            }\n        }\n\n        for (ByteBuffer buff : buffs.values()) {\n            pool.recycle(buff);\n        }\n\n        if (failure == true) {\n            Assert.fail(\"Allocate with same address\");\n        }\n    }\n\n    @Test\n    public void testAllocateNullWhenOutOfMemory() {\n        int size = 256;\n        int pageSize = size * 4;\n        int allocTimes = 9;\n        DirectByteBufferPool pool = new DirectByteBufferPool(pageSize, (short) 256, (short) 2,0);\n        long start = System.currentTimeMillis();\n        ByteBuffer byteBuffer = null;\n        List<ByteBuffer> buffs = new ArrayList<ByteBuffer>();\n        int i = 0;\n        for (; i < allocTimes; i++) {\n            byteBuffer = pool.allocate(size);\n            if (byteBuffer == null||!(byteBuffer instanceof DirectBuffer) ) {\n                break;\n            }\n            buffs.add(byteBuffer);\n        }\n        for (ByteBuffer buff : buffs) {\n            pool.recycle(buff);\n        }\n\n        Assert.assertEquals(\"Should out of memory when i = \" + 8, i, 8);\n    }\n\n    @Test\n    public void testAllocateSign() {\n        int size = 256;\n        int pageSize = size * 4;\n        int allocTimes = 9;\n        DirectByteBufferPool pool = new DirectByteBufferPool(pageSize, (short) 256, (short) 2,0);\n        long start = System.currentTimeMillis();\n        ByteBuffer byteBuffer = null;\n        List<ByteBuffer> buffs = new ArrayList<ByteBuffer>();\n        int i = 0;\n        for (; i < allocTimes; i++) {\n            byteBuffer = pool.allocate(size);\n            if (byteBuffer == null||!(byteBuffer instanceof DirectBuffer) ) {\n                break;\n            }\n            buffs.add(byteBuffer);\n        }\n        for (ByteBuffer buff : buffs) {\n            pool.recycle(buff);\n        }\n\n        Assert.assertEquals(\"Should out of memory when i = \" + 8, i, 8);\n    }\n\n    @Test\n    public  void testExpandBuffer(){\n        int size = 512;\n        int pageSize = 1024*1024;\n        int allocTimes = 9;\n        DirectByteBufferPool pool = new DirectByteBufferPool(pageSize, (short) 512, (short) 64,0);\n        ByteBuffer byteBuffer = pool.allocate(1024);\n        String str = \"DirectByteBufferPool pool = new DirectByteBufferPool(pageSize, (short) 256, (short) 8)\";\n        ByteBuffer newByteBuffer = null;\n        int i = 0;\n        while (i<10){\n            if(byteBuffer.remaining()<str.length()){\n                newByteBuffer = pool.expandBuffer(byteBuffer);\n                byteBuffer = newByteBuffer;\n                i++;\n            }else {\n                byteBuffer.put(str.getBytes());\n            }\n            System.out.println(\"remaining: \" +  byteBuffer.remaining() + \"capacity: \" + byteBuffer.capacity());\n        }\n\n        System.out.println(\"capacity : \" + byteBuffer.capacity());\n        System.out.println(\"capacity : \" + byteBuffer.position());\n\n        byte [] bytes = new byte[byteBuffer.position()];\n        byteBuffer.flip();\n        byteBuffer.get(bytes);\n        String body = new String(bytes);\n\n        System.out.println(byteBuffer.position());\n\n        System.out.println(body);\n\n        System.out.println(\"size :\" + body.length());\n        pool.recycle(byteBuffer);\n    }\n\n\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/buffer/TestMycatMemoryAlloctor.java",
    "content": "package io.mycat.buffer;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.util.internal.PlatformDependent;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.nio.ByteBuffer;\nimport java.nio.CharBuffer;\nimport java.nio.charset.Charset;\nimport java.nio.charset.CharsetDecoder;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * @author zagnix\n * @create 2017-01-18 11:19\n */\n\npublic class TestMycatMemoryAlloctor {\n    private ConcurrentHashMap<Long,ByteBuf> freeMaps = new ConcurrentHashMap<>();\n    final MyCatMemoryAllocator memoryAllocator =\n            new MyCatMemoryAllocator(Runtime.getRuntime().availableProcessors()*2);\n    @Test\n    public void testMemAlloc(){\n\n        for (int i = 0; i <10000/**20000000*/; i++) {\n            ByteBuffer byteBuffer = getBuffer(8194);\n            byteBuffer.put(\"helll world\".getBytes());\n            byteBuffer.flip();\n            byte [] src= new byte[byteBuffer.remaining()];\n            byteBuffer.get(src);\n            Assert.assertEquals(\"helll world\",new String(src));\n            free(byteBuffer);\n        }\n    }\n\n\n    public ByteBuffer getBuffer(int len)\n    {\n        ByteBuf byteBuf = memoryAllocator.directBuffer(len);\n        ByteBuffer  byteBuffer = byteBuf.nioBuffer(0,len);\n        freeMaps.put(PlatformDependent.directBufferAddress(byteBuffer),byteBuf);\n        return byteBuffer;\n    }\n\n    public void free(ByteBuffer byteBuffer)\n    {\n        ByteBuf byteBuf1 = freeMaps.get(PlatformDependent.directBufferAddress(byteBuffer));\n        byteBuf1.release();\n        Assert.assertEquals(0,byteBuf1.refCnt());\n    }\n\n\n    public static String getString(ByteBuffer buffer) {\n        Charset charset = null;\n        CharsetDecoder decoder = null;\n        CharBuffer charBuffer = null;\n        try {\n            charset = Charset.forName(\"UTF-8\");\n            decoder = charset.newDecoder();\n            charBuffer = decoder.decode(buffer.asReadOnlyBuffer());\n            return charBuffer.toString();\n        } catch (Exception ex) {\n            ex.printStackTrace();\n            return \"error\";\n        }\n    }\n\n    public static ByteBuffer getByteBuffer(String str)\n    {\n        return ByteBuffer.wrap(str.getBytes());\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/cache/DefaultLayedCachePoolTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache;\n\nimport junit.framework.Assert;\n\nimport org.junit.Test;\n\nimport io.mycat.cache.CacheStatic;\nimport io.mycat.cache.DefaultLayedCachePool;\nimport io.mycat.cache.impl.EnchachePooFactory;\n\npublic class  DefaultLayedCachePoolTest {\n\n\tprivate static DefaultLayedCachePool layedCachePool;\n\tstatic {\n\t\t\n\t\tlayedCachePool=new DefaultLayedCachePool(\"defaultLayedPool\",new EnchachePooFactory(),1000,1);\n\t\t\n\t}\n\n\t@Test\n\tpublic void testBasic() {\n\t\tlayedCachePool.putIfAbsent(\"2\", \"dn2\");\n\t\tlayedCachePool.putIfAbsent(\"1\", \"dn1\");\n\n\t\tlayedCachePool.putIfAbsent(\"company\", 1, \"dn1\");\n\t\tlayedCachePool.putIfAbsent(\"company\", 2, \"dn2\");\n\n\t\tlayedCachePool.putIfAbsent(\"goods\", \"1\", \"dn1\");\n\t\tlayedCachePool.putIfAbsent(\"goods\", \"2\", \"dn2\");\n\n\t\tAssert.assertEquals(\"dn2\", layedCachePool.get(\"2\"));\n\t\tAssert.assertEquals(\"dn1\", layedCachePool.get(\"1\"));\n\t\tAssert.assertEquals(null, layedCachePool.get(\"3\"));\n\n\t\tAssert.assertEquals(\"dn1\", layedCachePool.get(\"company\", 1));\n\t\tAssert.assertEquals(\"dn2\", layedCachePool.get(\"company\", 2));\n\t\tAssert.assertEquals(null, layedCachePool.get(\"company\", 3));\n\n\t\tAssert.assertEquals(\"dn1\", layedCachePool.get(\"goods\", \"1\"));\n\t\tAssert.assertEquals(\"dn2\", layedCachePool.get(\"goods\", \"2\"));\n\t\tAssert.assertEquals(null, layedCachePool.get(\"goods\", 3));\n\t\tCacheStatic statics = layedCachePool.getCacheStatic();\n\t\tAssert.assertEquals(statics.getItemSize(), 6);\n\t\tAssert.assertEquals(statics.getPutTimes(), 6);\n\t\tAssert.assertEquals(statics.getAccessTimes(), 9);\n\t\tAssert.assertEquals(statics.getHitTimes(), 6);\n\t\tAssert.assertTrue(statics.getLastAccesTime() > 0);\n\t\tAssert.assertTrue(statics.getLastPutTime() > 0);\n\t\tAssert.assertTrue(statics.getLastAccesTime() > 0);\n\t\t// wait expire\n\t\ttry {\n\t\t\tThread.sleep(2000);\n\t\t} catch (InterruptedException e) {\n\t\t}\n\t\tAssert.assertEquals(null, layedCachePool.get(\"2\"));\n\t\tAssert.assertEquals(null, layedCachePool.get(\"1\"));\n\t\tAssert.assertEquals(null, layedCachePool.get(\"goods\", \"2\"));\n\t\tAssert.assertEquals(null, layedCachePool.get(\"company\", 2));\n\t}\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/cache/EnCachePoolTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache;\n\nimport junit.framework.Assert;\nimport net.sf.ehcache.Cache;\nimport net.sf.ehcache.CacheManager;\nimport net.sf.ehcache.config.CacheConfiguration;\nimport net.sf.ehcache.config.MemoryUnit;\n\nimport org.junit.Test;\n\nimport io.mycat.cache.CacheStatic;\nimport io.mycat.cache.impl.EnchachePool;\n\npublic class EnCachePoolTest {\n\n\tprivate static EnchachePool enCachePool;\n\tstatic {\n\t\tCacheConfiguration cacheConf = new CacheConfiguration();\n\t\tcacheConf.setName(\"testcache\");\n\t\tcacheConf.maxBytesLocalHeap(50,MemoryUnit.MEGABYTES).timeToIdleSeconds(2);\n\t\tCache cache=new Cache(cacheConf);\n\t\tCacheManager.create().addCache(cache);\n\t\tenCachePool = new EnchachePool(cacheConf.getName(),cache,50*10000);\n\t}\n\n\t@Test\n\tpublic void testBasic() {\n\t\tenCachePool.putIfAbsent(\"2\", \"dn2\");\n\t\tenCachePool.putIfAbsent(\"1\", \"dn1\");\n\n\t\tAssert.assertEquals(\"dn2\", enCachePool.get(\"2\"));\n\t\tAssert.assertEquals(\"dn1\", enCachePool.get(\"1\"));\n\t\tAssert.assertEquals(null, enCachePool.get(\"3\"));\n\n\t\tCacheStatic statics = enCachePool.getCacheStatic();\n\t\tAssert.assertEquals(statics.getItemSize(), 2);\n\t\tAssert.assertEquals(statics.getPutTimes(), 2);\n\t\tAssert.assertEquals(statics.getAccessTimes(), 3);\n\t\tAssert.assertEquals(statics.getHitTimes(), 2);\n\t\tAssert.assertTrue(statics.getLastAccesTime() > 0);\n\t\tAssert.assertTrue(statics.getLastPutTime() > 0);\n\t\tAssert.assertTrue(statics.getLastAccesTime() > 0);\n\t\t// wait expire\n\t\ttry {\n\t\t\tThread.sleep(4000);\n\t\t} catch (InterruptedException e) {\n\t\t}\n\t\tAssert.assertEquals(null, enCachePool.get(\"2\"));\n\t\tAssert.assertEquals(null, enCachePool.get(\"1\"));\n\t}\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/cache/TestCachePoolPerformance.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.cache;\n\nimport io.mycat.cache.CachePool;\nimport io.mycat.cache.CacheStatic;\nimport io.mycat.cache.impl.EnchachePool;\nimport io.mycat.cache.impl.MapDBCachePooFactory;\n/**\n * test cache performance ,for encache test set  VM param  -server -Xms1100M -Xmx1100M\n * for mapdb set vm param -server -Xms100M -Xmx100M -XX:MaxPermSize=1G\n */\nimport net.sf.ehcache.Cache;\nimport net.sf.ehcache.CacheManager;\nimport net.sf.ehcache.config.CacheConfiguration;\nimport net.sf.ehcache.config.MemoryUnit;\n\npublic class TestCachePoolPerformance {\n\tprivate CachePool pool;\n\tprivate int maxCacheCount = 100 * 10000;\n\n\tpublic static CachePool createEnCachePool() {\n\t\tCacheConfiguration cacheConf = new CacheConfiguration();\n\t\tcacheConf.setName(\"testcache\");\n\t\tcacheConf.maxBytesLocalHeap(400, MemoryUnit.MEGABYTES)\n\t\t\t\t.timeToIdleSeconds(3600);\n\t\tCache cache = new Cache(cacheConf);\n\t\tCacheManager.create().addCache(cache);\n\t\tEnchachePool enCachePool = new EnchachePool(cacheConf.getName(),cache,400*10000);\n\t\treturn enCachePool;\n\t}\n\n\tpublic static CachePool createMapDBCachePool() {\n\t\tMapDBCachePooFactory fact = new MapDBCachePooFactory();\n\t\treturn fact.createCachePool(\"mapdbcache\", 100 * 10000, 3600);\n\n\t}\n\n\tpublic void test() {\n\t\ttestSwarm();\n\t\ttestInsertSpeed();\n\t\ttestSelectSpeed();\n\t}\n\n\tprivate void testSwarm() {\n\t\tSystem.out.println(\"prepare ........\");\n\t\tfor (int i = 0; i < 100000; i++) {\n\t\t\tpool.putIfAbsent(i % 100, \"dn1\");\n\t\t}\n\t\tfor (int i = 0; i < 100000; i++) {\n\t\t\tpool.get(i % 100);\n\t\t}\n\t\tpool.clearCache();\n\t}\n\n\tprivate void testSelectSpeed() {\n\t\tSystem.out.println(\"test select speed for \" + this.pool + \" count:\"\n\t\t\t\t+ this.maxCacheCount);\n\t\tlong startTime = System.currentTimeMillis();\n\t\tfor (int i = 0; i < maxCacheCount; i++) {\n\t\t\tpool.get(i + \"\");\n\t\t}\n\t\tdouble used = (System.currentTimeMillis() - startTime) / 1000.0;\n\t\tCacheStatic statics = pool.getCacheStatic();\n\t\tSystem.out.println(\"used time:\" + used + \" tps:\" + maxCacheCount / used\n\t\t\t\t+ \" cache hit:\" + 100 * statics.getHitTimes()\n\t\t\t\t/ statics.getAccessTimes());\n\t}\n\n\tprivate void GC() {\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tSystem.gc();\n\t\t}\n\t}\n\n\tprivate void testInsertSpeed() {\n\t\tthis.GC();\n\t\tlong freeMem = Runtime.getRuntime().freeMemory();\n\t\tSystem.out.println(\"test insert speed for \" + this.pool\n\t\t\t\t+ \" with insert count:\" + this.maxCacheCount);\n\t\tlong start = System.currentTimeMillis();\n\t\tfor (int i = 0; i < maxCacheCount; i++) {\n\t\t\ttry {\n\t\t\t\tpool.putIfAbsent(i + \"\", \"dn\" + i % 100);\n\t\t\t} catch (Error e) {\n\t\t\t\tSystem.out.println(\"insert \" + i + \" error\");\n\t\t\t\te.printStackTrace();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tlong used = (System.currentTimeMillis() - start) / 1000;\n\t\tlong count = pool.getCacheStatic().getItemSize();\n\t\tthis.GC();\n\t\tlong usedMem = freeMem - Runtime.getRuntime().freeMemory();\n\t\tSystem.out.println(\" cache size is \" + count + \" ,all in cache :\"\n\t\t\t\t+ (count == maxCacheCount) + \" ,used time:\" + used + \" ,tps:\"\n\t\t\t\t+ count / used + \" used memory:\" + usedMem / 1024 / 1024 + \"M\");\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tif (args.length < 1) {\n\t\t\tSystem.out\n\t\t\t\t\t.println(\"usage : \\r\\n cache: 1 for encache 2 for mapdb\\r\\n\");\n\t\t\treturn;\n\t\t}\n\t\tTestCachePoolPerformance tester = new TestCachePoolPerformance();\n\t\tint cacheType = Integer.parseInt(args[0]);\n\t\tif (cacheType == 1) {\n\t\t\ttester.pool = createEnCachePool();\n\t\t\ttester.test();\n\t\t} else if (cacheType == 2) {\n\t\t\ttester.pool = createMapDBCachePool();\n\t\t\ttester.test();\n\t\t} else {\n\t\t\tSystem.out.println(\"not valid input \");\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/classload/TestDynClassLoad.java",
    "content": "package io.mycat.classload;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport io.mycat.config.classloader.DynaClassLoader;\n\npublic class TestDynClassLoad {\n\n\t@Test\n\tpublic void testLoadClass() throws Exception {\n\t\tString path=this.getClass().getResource(\"/\").getPath();\n\t\tString clsName=\"demo.test.TestClass1\";\n\t\tSystem.out.println(\"class load path \"+path);\n\t\tDynaClassLoader loader =new DynaClassLoader(path,1);\n\t\tObject obj=loader.getInstanceofClass(clsName);\n\t\tAssert.assertEquals(obj.getClass().getSimpleName(),\"TestClass1\");\n\t\tAssert.assertEquals(true,loader.getInstanceofClass(clsName)==obj);\n\t\n\t}\n\n\t\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/config/ConfigTest.java",
    "content": "package io.mycat.config;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.Test;\n\nimport io.mycat.backend.datasource.PhysicalDBPool;\nimport io.mycat.backend.datasource.PhysicalDatasource;\nimport io.mycat.backend.jdbc.JDBCDatasource;\nimport io.mycat.backend.mysql.nio.MySQLDataSource;\nimport io.mycat.config.loader.ConfigLoader;\nimport io.mycat.config.loader.xml.XMLConfigLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.DBHostConfig;\nimport io.mycat.config.model.DataHostConfig;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.config.model.UserConfig;\nimport io.mycat.config.util.ConfigException;\nimport junit.framework.Assert;\n\npublic class ConfigTest {\n\t\n\tprivate SystemConfig system;\n\tprivate final Map<String, UserConfig> users;\n\tprivate Map<String, SchemaConfig> schemas;\n\tprivate Map<String, PhysicalDBPool> dataHosts;\t\n\t\n\tpublic ConfigTest() {\n\t\t\n\t\tString schemaFile = \"/config/schema.xml\";\n\t\tString ruleFile = \"/config/rule.xml\";\n\t\t\n\t\tXMLSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tXMLConfigLoader configLoader = new XMLConfigLoader(schemaLoader);\n\t\t\n\t\tthis.system = configLoader.getSystemConfig();\n\t\tthis.users = configLoader.getUserConfigs();\n\t\tthis.schemas = configLoader.getSchemaConfigs();\t\t\n        this.dataHosts = initDataHosts(configLoader);\n        \n\t}\n\t\n\t/**\n\t * 测试 临时读可用 配置\n\t */\n\t@Test\n\tpublic void testTempReadHostAvailable() {\n\t\tPhysicalDBPool pool = this.dataHosts.get(\"localhost2\");   \n\t\tDataHostConfig hostConfig = pool.getSource().getHostConfig();\n\t\tAssert.assertTrue( hostConfig.isTempReadHostAvailable() == true );\n\t}\n\t\n\t/**\n\t * 测试 用户服务降级 拒连 配置\n\t */\n\t@Test\n\tpublic void testReadUserBenchmark() {\n\t\tUserConfig userConfig = this.users.get(\"test\");\n\t\tint benchmark = userConfig.getBenchmark();\n\t\tAssert.assertTrue( benchmark == 11111 );\n\t}\n\t\n\t\n\t/**\n     * 测试 读服务的 权重\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testReadHostWeight() throws Exception {\n    \t\n    \tArrayList<PhysicalDatasource> okSources = new ArrayList<PhysicalDatasource>();\n    \t\n    \tPhysicalDBPool pool = this.dataHosts.get(\"localhost2\");   \n    \tokSources.addAll(pool.getAllDataSources());    \t\n    \tPhysicalDatasource source = pool.randomSelect( okSources );\n  \n    \tAssert.assertTrue( source != null );\n    }\n    \n    /**\n     * 测试 动态日期表\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testDynamicYYYYMMTable() throws Exception {\n    \tSchemaConfig sc = this.schemas.get(\"dbtest1\");\n    \tMap<String, TableConfig> tbm = sc.getTables();\n    \tAssert.assertTrue( tbm.size() == 32);    \t\n    }\n    \n\tprivate Map<String, PhysicalDBPool> initDataHosts(ConfigLoader configLoader) {\n\t\tMap<String, DataHostConfig> nodeConfs = configLoader.getDataHosts();\n\t\tMap<String, PhysicalDBPool> nodes = new HashMap<String, PhysicalDBPool>(\n\t\t\t\tnodeConfs.size());\n\t\tfor (DataHostConfig conf : nodeConfs.values()) {\n\t\t\tPhysicalDBPool pool = getPhysicalDBPool(conf, configLoader);\n\t\t\tnodes.put(pool.getHostName(), pool);\n\t\t}\n\t\treturn nodes;\n\t}\n    \n    private PhysicalDatasource[] createDataSource(DataHostConfig conf,\n\t\t\tString hostName, String dbType, String dbDriver,\n\t\t\tDBHostConfig[] nodes, boolean isRead) {\n\t\tPhysicalDatasource[] dataSources = new PhysicalDatasource[nodes.length];\n\t\tif (dbType.equals(\"mysql\") && dbDriver.equals(\"native\")) {\n\t\t\tfor (int i = 0; i < nodes.length; i++) {\n\t\t\t\tnodes[i].setIdleTimeout(system.getIdleTimeout());\n\t\t\t\tMySQLDataSource ds = new MySQLDataSource(nodes[i], conf, isRead);\n\t\t\t\tdataSources[i] = ds;\n\t\t\t}\n\n\t\t} else if(dbDriver.equals(\"jdbc\"))\n\t\t\t{\n\t\t\tfor (int i = 0; i < nodes.length; i++) {\n\t\t\t\tnodes[i].setIdleTimeout(system.getIdleTimeout());\n\t\t\t\tJDBCDatasource ds = new JDBCDatasource(nodes[i], conf, isRead);\n\t\t\t\tdataSources[i] = ds;\n\t\t\t}\n\t\t\t}\n\t\telse {\n\t\t\tthrow new ConfigException(\"not supported yet !\" + hostName);\n\t\t}\n\t\treturn dataSources;\n\t}\n\n\tprivate PhysicalDBPool getPhysicalDBPool(DataHostConfig conf,\n\t\t\tConfigLoader configLoader) {\n\t\tString name = conf.getName();\n\t\tString dbType = conf.getDbType();\n\t\tString dbDriver = conf.getDbDriver();\n\t\tPhysicalDatasource[] writeSources = createDataSource(conf, name,\n\t\t\t\tdbType, dbDriver, conf.getWriteHosts(), false);\n\t\tMap<Integer, DBHostConfig[]> readHostsMap = conf.getReadHosts();\n\t\tMap<Integer, PhysicalDatasource[]> readSourcesMap = new HashMap<Integer, PhysicalDatasource[]>(\n\t\t\t\treadHostsMap.size());\n\t\tfor (Map.Entry<Integer, DBHostConfig[]> entry : readHostsMap.entrySet()) {\n\t\t\tPhysicalDatasource[] readSources = createDataSource(conf, name,\n\t\t\t\t\tdbType, dbDriver, entry.getValue(), true);\n\t\t\treadSourcesMap.put(entry.getKey(), readSources);\n\t\t}\n\t\tPhysicalDBPool pool = new PhysicalDBPool(conf.getName(),conf, writeSources,\n\t\t\t\treadSourcesMap, conf.getBalance(), conf.getWriteType());\n\t\treturn pool;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/heartbeat/HeartbeatConfigForTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.heartbeat;\n\nimport org.junit.Test;\n\n/**\n * @author mycat\n */\npublic class HeartbeatConfigForTest {\n    @Test\n    public void testNoop() {\n    }\n    // public static DataNodeConfig[] getOfferNodes(int offset, int length) {\n    // DataNodeConfig[] nodes = new DataNodeConfig[length];\n    // for (int i = 0; i < length; i++) {\n    // DataNodeConfig node = new DataNodeConfig();\n    // node.name = \"offer\" + (offset + i);\n    // node.activedIndex = 0;\n    // node.dataSource = getOfferDataSource(node.name);\n    // nodes[i] = node;\n    // }\n    // return nodes;\n    // }\n    //\n    // private static DataSourceConfig[] getOfferDataSource(String schema) {\n    // DataSourceConfig ds1 = new DataSourceConfig();\n    // ds1.host = \"10.20.132.17\";\n    // ds1.port = 3306;\n    // ds1.schema = schema;\n    // ds1.user = \"offer\";\n    // ds1.password = \"offer\";\n    // ds1.statement = \"update xdual set x=now()\";\n    //\n    // DataSourceConfig ds2 = new DataSourceConfig();\n    // ds2.host = \"10.20.153.177\";\n    // ds2.port = 3316;\n    // ds2.schema = schema;\n    // ds2.user = \"offer\";\n    // ds2.password = \"offer\";\n    // ds2.statement = \"update xdual set x=now()\";\n    //\n    // return new DataSourceConfig[] { ds1, ds2 };\n    // }\n    //\n    // public static DataNodeConfig getNodeErrorConfig() {\n    // // 数据源1（IP错误）\n    // DataSourceConfig ds1 = new DataSourceConfig();\n    // ds1.host = \"100.20.132.17\";\n    // ds1.port = 3306;\n    // ds1.schema = \"offer1\";\n    // ds1.user = \"offer\";\n    // ds1.password = \"offer\";\n    // ds1.statement = \"update xdual set x=now()\";\n    //\n    // // 数据源2（端口错误）\n    // DataSourceConfig ds2 = new DataSourceConfig();\n    // ds2.host = \"10.20.132.17\";\n    // ds2.port = 3316;\n    // ds2.schema = \"offer1\";\n    // ds2.user = \"offer\";\n    // ds2.password = \"offer\";\n    // ds2.statement = \"update xdual set x=now()\";\n    //\n    // // 数据源3（SCHEMA错误）\n    // DataSourceConfig ds3 = new DataSourceConfig();\n    // ds3.host = \"10.20.132.17\";\n    // ds3.port = 3306;\n    // ds3.schema = \"offer1_x\";\n    // ds3.user = \"offer\";\n    // ds3.password = \"offer\";\n    // ds3.statement = \"update xdual set x=now()\";\n    //\n    // // 数据源4（用户错误）\n    // DataSourceConfig ds4 = new DataSourceConfig();\n    // ds4.host = \"10.20.132.17\";\n    // ds4.port = 3306;\n    // ds4.schema = \"offer1\";\n    // ds4.user = \"offer_x\";\n    // ds4.password = \"offer\";\n    // ds4.statement = \"update xdual set x=now()\";\n    //\n    // // 数据源5（密码错误）\n    // DataSourceConfig ds5 = new DataSourceConfig();\n    // ds5.host = \"10.20.132.17\";\n    // ds5.port = 3306;\n    // ds5.schema = \"offer1\";\n    // ds5.user = \"offer\";\n    // ds5.password = \"offer_x\";\n    // ds5.statement = \"update xdual set x=now()\";\n    //\n    // // 数据源6（语句错误）\n    // DataSourceConfig ds6 = new DataSourceConfig();\n    // ds6.host = \"10.20.132.17\";\n    // ds6.port = 3306;\n    // ds6.schema = \"offer1\";\n    // ds6.user = \"offer\";\n    // ds6.password = \"offer\";\n    // ds6.statement = \"update xdual_x set x=now()\";\n    //\n    // // 数据源（正确配置）\n    // DataSourceConfig ds = new DataSourceConfig();\n    // ds.host = \"10.20.132.17\";\n    // ds.port = 3306;\n    // ds.schema = \"offer1\";\n    // ds.user = \"offer\";\n    // ds.password = \"offer\";\n    // ds.statement = \"update xdual set x=now()\";\n    //\n    // DataNodeConfig node = new DataNodeConfig();\n    // node.name = \"offer1\";\n    // node.activedIndex = 0;\n    // node.dataSource = new DataSourceConfig[] { ds1, ds2, ds3, ds4, ds5, ds6,\n    // ds };\n    // return node;\n    // }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/heartbeat/HeartbeatContext.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.heartbeat;\n\n/**\n * @author mycat\n */\npublic class HeartbeatContext {\n\n    // private final static long TIMER_PERIOD = 1000L;\n    //\n    // private String name;\n    // private Timer timer;\n    // private NIOProcessor[] processors;\n    // private NIOConnector connector;\n    //\n    // public HeartbeatContext(String name) throws IOException {\n    // this.name = name;\n    // this.init();\n    // }\n    //\n    // public void startup() {\n    // // startup timer\n    // timer.schedule(new TimerTask() {\n    // @Override\n    // public void run() {\n    // TimeUtil.update();\n    // }\n    // }, 0L, TimeUtil.UPDATE_PERIOD);\n    //\n    // // startup processors\n    // for (int i = 0; i < processors.length; i++) {\n    // processors[i].startup();\n    // }\n    //\n    // // startup connector\n    // connector.start();\n    // }\n    //\n    // public void doHeartbeat(HeartbeatConfig heartbeat) {\n    // timer.schedule(new MySQLHeartbeatTask(connector, heartbeat), 0L,\n    // TIMER_PERIOD);\n    // }\n    //\n    // private void init() throws IOException {\n    // // init timer\n    // this.timer = new Timer(name + \"Timer\", false);\n    //\n    // // init processors\n    // processors = new\n    // NIOProcessor[Runtime.getRuntime().availableProcessors()];\n    // for (int i = 0; i < processors.length; i++) {\n    // processors[i] = new NIOProcessor(name + \"Processor\" + i);\n    // }\n    //\n    // // init connector\n    // connector = new NIOConnector(name + \"Connector\");\n    // connector.setProcessors(processors);\n    // }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/heartbeat/HeartbeatStartup.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.heartbeat;\n\n/**\n * @author mycat\n */\npublic class HeartbeatStartup {\n\n    // public static void main(String[] args) throws Exception {\n    // // 初始化心跳运行环境\n    // HeartbeatContext ctx = new HeartbeatContext(\"HB\");\n    //\n    // // 启动心跳运行环境\n    // ctx.startup();\n    //\n    // // 执行心跳任务\n    // doHeartbeat(ctx);\n    // }\n    //\n    // static void doHeartbeat(HeartbeatContext ctx) {\n    // DataNodeConfig[] nodes = HeartbeatConfigForTest.getOfferNodes(1, 32);\n    // for (int i = 0; i < nodes.length; i++) {\n    // HeartbeatConfig config = new HeartbeatConfig();\n    // config.node = nodes[i];\n    // ctx.doHeartbeat(config);\n    // }\n    // }\n    //\n    // static void doZookeeper(HeartbeatContext ctx) {\n    // }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/PlatformUtilSuite.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class PlatformUtilSuite {\n\n  @Test\n  public void overlappingCopyMemory() {\n    byte[] data = new byte[3 * 1024 * 1024];\n    int size = 2 * 1024 * 1024;\n    for (int i = 0; i < data.length; ++i) {\n      data[i] = (byte)i;\n    }\n\n    Platform.copyMemory(data, Platform.BYTE_ARRAY_OFFSET, data, Platform.BYTE_ARRAY_OFFSET, size);\n    for (int i = 0; i < data.length; ++i) {\n      Assert.assertEquals((byte)i, data[i]);\n    }\n\n    Platform.copyMemory(\n        data,\n        Platform.BYTE_ARRAY_OFFSET + 1,\n        data,\n        Platform.BYTE_ARRAY_OFFSET,\n        size);\n    for (int i = 0; i < size; ++i) {\n      Assert.assertEquals((byte)(i + 1), data[i]);\n    }\n\n    for (int i = 0; i < data.length; ++i) {\n      data[i] = (byte)i;\n    }\n    Platform.copyMemory(\n        data,\n        Platform.BYTE_ARRAY_OFFSET,\n        data,\n        Platform.BYTE_ARRAY_OFFSET + 1,\n        size);\n    for (int i = 0; i < size; ++i) {\n      Assert.assertEquals((byte)i, data[i + 1]);\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/array/LongArraySuite.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.array;\n\nimport io.mycat.memory.unsafe.memory.MemoryBlock;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n\npublic class LongArraySuite {\n\n  @Test\n  public void basicTest() {\n    long[] bytes = new long[2];\n    LongArray arr = new LongArray(MemoryBlock.fromLongArray(bytes));\n    arr.set(0, 1L);\n    arr.set(1, 2L);\n    arr.set(1, 3L);\n    Assert.assertEquals(2, arr.size());\n    Assert.assertEquals(1L, arr.get(0));\n    Assert.assertEquals(3L, arr.get(1));\n\n    arr.zeroOut();\n    Assert.assertEquals(0L, arr.get(0));\n    Assert.assertEquals(0L, arr.get(1));\n  }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/hash/Murmur3_x86_32Suite.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.hash;\n\n\nimport io.mycat.memory.unsafe.Platform;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashSet;\nimport java.util.Random;\nimport java.util.Set;\n\n/**\n * Test file based on Guava's Murmur3Hash32Test.\n */\npublic class Murmur3_x86_32Suite {\n\n  private static final Murmur3_x86_32 hasher = new Murmur3_x86_32(0);\n\n  @Test\n  public void testKnownIntegerInputs() {\n    Assert.assertEquals(593689054, hasher.hashInt(0));\n    Assert.assertEquals(-189366624, hasher.hashInt(-42));\n    Assert.assertEquals(-1134849565, hasher.hashInt(42));\n    Assert.assertEquals(-1718298732, hasher.hashInt(Integer.MIN_VALUE));\n    Assert.assertEquals(-1653689534, hasher.hashInt(Integer.MAX_VALUE));\n  }\n\n  @Test\n  public void testKnownLongInputs() {\n    Assert.assertEquals(1669671676, hasher.hashLong(0L));\n    Assert.assertEquals(-846261623, hasher.hashLong(-42L));\n    Assert.assertEquals(1871679806, hasher.hashLong(42L));\n    Assert.assertEquals(1366273829, hasher.hashLong(Long.MIN_VALUE));\n    Assert.assertEquals(-2106506049, hasher.hashLong(Long.MAX_VALUE));\n  }\n\n  @Test\n  public void randomizedStressTest() {\n    int size = 65536;\n    Random rand = new Random();\n\n    // A set used to track collision rate.\n    Set<Integer> hashcodes = new HashSet<Integer>();\n    for (int i = 0; i < size; i++) {\n      int vint = rand.nextInt();\n      long lint = rand.nextLong();\n      Assert.assertEquals(hasher.hashInt(vint), hasher.hashInt(vint));\n      Assert.assertEquals(hasher.hashLong(lint), hasher.hashLong(lint));\n\n      hashcodes.add(hasher.hashLong(lint));\n    }\n\n    // A very loose bound.\n    Assert.assertTrue(hashcodes.size() > size * 0.95);\n  }\n\n  @Test\n  public void randomizedStressTestBytes() {\n    int size = 65536;\n    Random rand = new Random();\n\n    // A set used to track collision rate.\n    Set<Integer> hashcodes = new HashSet<Integer>();\n    for (int i = 0; i < size; i++) {\n      int byteArrSize = rand.nextInt(100) * 8;\n      byte[] bytes = new byte[byteArrSize];\n      rand.nextBytes(bytes);\n\n      Assert.assertEquals(\n        hasher.hashUnsafeWords(bytes, Platform.BYTE_ARRAY_OFFSET, byteArrSize),\n        hasher.hashUnsafeWords(bytes, Platform.BYTE_ARRAY_OFFSET, byteArrSize));\n\n      hashcodes.add(hasher.hashUnsafeWords(\n        bytes, Platform.BYTE_ARRAY_OFFSET, byteArrSize));\n    }\n\n    // A very loose bound.\n    Assert.assertTrue(hashcodes.size() > size * 0.95);\n  }\n\n  @Test\n  public void randomizedStressTestPaddedStrings() {\n    int size = 64000;\n    // A set used to track collision rate.\n    Set<Integer> hashcodes = new HashSet<Integer>();\n    for (int i = 0; i < size; i++) {\n      int byteArrSize = 8;\n      byte[] strBytes = String.valueOf(i).getBytes(StandardCharsets.UTF_8);\n      byte[] paddedBytes = new byte[byteArrSize];\n      System.arraycopy(strBytes, 0, paddedBytes, 0, strBytes.length);\n\n      Assert.assertEquals(\n        hasher.hashUnsafeWords(paddedBytes, Platform.BYTE_ARRAY_OFFSET, byteArrSize),\n        hasher.hashUnsafeWords(paddedBytes, Platform.BYTE_ARRAY_OFFSET, byteArrSize));\n\n      hashcodes.add(hasher.hashUnsafeWords(\n        paddedBytes, Platform.BYTE_ARRAY_OFFSET, byteArrSize));\n    }\n\n    // A very loose bound.\n    Assert.assertTrue(hashcodes.size() > size * 0.95);\n  }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/map/AbstractBytesToBytesMapSuite.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.map;\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.array.ByteArrayMethods;\nimport io.mycat.memory.unsafe.memory.TestMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.storage.DataNodeDiskManager;\nimport io.mycat.memory.unsafe.storage.SerializerManager;\nimport io.mycat.memory.unsafe.utils.JavaUtils;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\n\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.util.*;\n\nimport static org.hamcrest.Matchers.greaterThan;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.mockito.Answers.RETURNS_SMART_NULLS;\n\n\npublic abstract class AbstractBytesToBytesMapSuite {\n\n  private final Random rand = new Random(42);\n  MycatPropertyConf conf = new MycatPropertyConf()\n          .set(\"mycat.memory.offHeap.enabled\", \"\" + useOffHeapMemoryAllocator())\n          .set(\"mycat.memory.offHeap.size\", \"256mb\");\n  private TestMemoryManager memoryManager =\n          new TestMemoryManager(conf\n          );\n\n  private DataNodeMemoryManager dataNodeMemoryManager =\n          new DataNodeMemoryManager(memoryManager,0);\n\n  private SerializerManager serializerManager = new SerializerManager();\n  private static final long PAGE_SIZE_BYTES = 1L << 26; // 64 megabytes\n\n  final LinkedList<File> spillFilesCreated = new LinkedList<File>();\n  File tempDir;\n\n  DataNodeDiskManager blockManager = new DataNodeDiskManager(conf,true,serializerManager);\n\n\n/*\n  private static final class CompressStream extends AbstractFunction1<OutputStream, OutputStream> {\n    @Override\n    public OutputStream apply(OutputStream stream) {\n      return stream;\n    }\n  }\n*/\n  @Before\n  public void setup() {\n  }\n\n  @After\n  public void tearDown() throws IOException {\n    //Utils.deleteRecursively(tempDir);\n    //tempDir = null;\n\n    if (dataNodeMemoryManager != null) {\n      Assert.assertEquals(0L, dataNodeMemoryManager.cleanUpAllAllocatedMemory());\n      long leakedMemory = dataNodeMemoryManager.getMemoryConsumptionForThisConnection();\n      dataNodeMemoryManager = null;\n      Assert.assertEquals(0L, leakedMemory);\n    }\n  }\n\n  protected abstract boolean useOffHeapMemoryAllocator();\n\n  private static byte[] getByteArray(Object base, long offset, int size) {\n    final byte[] arr = new byte[size];\n    Platform.copyMemory(base, offset, arr, Platform.BYTE_ARRAY_OFFSET, size);\n    return arr;\n  }\n\n  private byte[] getRandomByteArray(int numWords) {\n    Assert.assertTrue(numWords >= 0);\n    final int lengthInBytes = numWords * 8;\n    final byte[] bytes = new byte[lengthInBytes];\n    rand.nextBytes(bytes);\n    return bytes;\n  }\n\n  /**\n   * Fast equality checking for byte arrays, since these comparisons are a bottleneck\n   * in our stress tests.\n   */\n  private static boolean arrayEquals(\n      byte[] expected,\n      Object base,\n      long offset,\n      long actualLengthBytes) {\n    return (actualLengthBytes == expected.length) && ByteArrayMethods.arrayEquals(\n      expected,\n      Platform.BYTE_ARRAY_OFFSET,\n      base,\n      offset,\n      expected.length\n    );\n  }\n\n  @Test\n  public void emptyMap() {\n    BytesToBytesMap map = new BytesToBytesMap(dataNodeMemoryManager, 64, PAGE_SIZE_BYTES);\n    try {\n      Assert.assertEquals(0, map.numKeys());\n      final int keyLengthInWords = 10;\n      final int keyLengthInBytes = keyLengthInWords * 8;\n      final byte[] key = getRandomByteArray(keyLengthInWords);\n      Assert.assertFalse(map.lookup(key, Platform.BYTE_ARRAY_OFFSET, keyLengthInBytes).isDefined());\n      Assert.assertFalse(map.iterator().hasNext());\n    } finally {\n      map.free();\n    }\n  }\n\n  @Test\n  public void setAndRetrieveAKey() {\n    BytesToBytesMap map = new BytesToBytesMap(dataNodeMemoryManager, 64, PAGE_SIZE_BYTES);\n    final int recordLengthWords = 10;\n    final int recordLengthBytes = recordLengthWords * 8;\n    final byte[] keyData = getRandomByteArray(recordLengthWords);\n    final byte[] valueData = getRandomByteArray(recordLengthWords);\n    try {\n      final BytesToBytesMap.Location loc =\n        map.lookup(keyData, Platform.BYTE_ARRAY_OFFSET, recordLengthBytes);\n      Assert.assertFalse(loc.isDefined());\n      Assert.assertTrue(loc.append(\n        keyData,\n        Platform.BYTE_ARRAY_OFFSET,\n        recordLengthBytes,\n        valueData,\n        Platform.BYTE_ARRAY_OFFSET,\n        recordLengthBytes\n      ));\n      // After storing the key and value, the other location methods should return results that\n      // reflect the result of this store without us having to call lookup() again on the same key.\n      Assert.assertEquals(recordLengthBytes, loc.getKeyLength());\n      Assert.assertEquals(recordLengthBytes, loc.getValueLength());\n      Assert.assertArrayEquals(keyData,\n        getByteArray(loc.getKeyBase(), loc.getKeyOffset(), recordLengthBytes));\n      Assert.assertArrayEquals(valueData,\n        getByteArray(loc.getValueBase(), loc.getValueOffset(), recordLengthBytes));\n\n      // After calling lookup() the location should still point to the correct data.\n      Assert.assertTrue(\n        map.lookup(keyData, Platform.BYTE_ARRAY_OFFSET, recordLengthBytes).isDefined());\n      Assert.assertEquals(recordLengthBytes, loc.getKeyLength());\n      Assert.assertEquals(recordLengthBytes, loc.getValueLength());\n      Assert.assertArrayEquals(keyData,\n        getByteArray(loc.getKeyBase(), loc.getKeyOffset(), recordLengthBytes));\n      Assert.assertArrayEquals(valueData,\n        getByteArray(loc.getValueBase(), loc.getValueOffset(), recordLengthBytes));\n\n      try {\n        Assert.assertTrue(loc.append(\n          keyData,\n          Platform.BYTE_ARRAY_OFFSET,\n          recordLengthBytes,\n          valueData,\n          Platform.BYTE_ARRAY_OFFSET,\n          recordLengthBytes\n        ));\n        Assert.fail(\"Should not be able to set a new value for a key\");\n      } catch (AssertionError e) {\n        // Expected exception; do nothing.\n      }\n    } finally {\n      map.free();\n    }\n  }\n\n  private void iteratorTestBase(boolean destructive) throws Exception {\n    final int size = 4096;\n    BytesToBytesMap map = new BytesToBytesMap(dataNodeMemoryManager, size / 2, PAGE_SIZE_BYTES);\n    try {\n      for (long i = 0; i < size; i++) {\n        final long[] value = new long[] { i };\n        final BytesToBytesMap.Location loc =\n          map.lookup(value, Platform.LONG_ARRAY_OFFSET, 8);\n        Assert.assertFalse(loc.isDefined());\n        // Ensure that we store some zero-length keys\n        if (i % 5 == 0) {\n          Assert.assertTrue(loc.append(\n            null,\n            Platform.LONG_ARRAY_OFFSET,\n            0,\n            value,\n            Platform.LONG_ARRAY_OFFSET,\n            8\n          ));\n        } else {\n          Assert.assertTrue(loc.append(\n            value,\n            Platform.LONG_ARRAY_OFFSET,\n            8,\n            value,\n            Platform.LONG_ARRAY_OFFSET,\n            8\n          ));\n        }\n      }\n      final BitSet valuesSeen = new BitSet(size);\n      final Iterator<BytesToBytesMap.Location> iter;\n      if (destructive) {\n        iter = map.destructiveIterator();\n      } else {\n        iter = map.iterator();\n      }\n      int numPages = map.getNumDataPages();\n      int countFreedPages = 0;\n      while (iter.hasNext()) {\n        final BytesToBytesMap.Location loc = iter.next();\n        Assert.assertTrue(loc.isDefined());\n        final long value = Platform.getLong(loc.getValueBase(), loc.getValueOffset());\n        final long keyLength = loc.getKeyLength();\n        if (keyLength == 0) {\n          Assert.assertTrue(\"value \" + value + \" was not divisible by 5\", value % 5 == 0);\n        } else {\n          final long key = Platform.getLong(loc.getKeyBase(), loc.getKeyOffset());\n          Assert.assertEquals(value, key);\n        }\n        valuesSeen.set((int) value);\n        if (destructive) {\n          // The iterator moves onto next page and frees previous page\n          if (map.getNumDataPages() < numPages) {\n            numPages = map.getNumDataPages();\n            countFreedPages++;\n          }\n        }\n      }\n      if (destructive) {\n        // Latest page is not freed by iterator but by map itself\n        Assert.assertEquals(countFreedPages, numPages - 1);\n      }\n      Assert.assertEquals(size, valuesSeen.cardinality());\n    } finally {\n      map.free();\n    }\n  }\n\n  @Test\n  public void iteratorTest() throws Exception {\n    iteratorTestBase(false);\n  }\n\n  @Test\n  public void destructiveIteratorTest() throws Exception {\n    iteratorTestBase(true);\n  }\n\n  @Test\n  public void iteratingOverDataPagesWithWastedSpace() throws Exception {\n    final int NUM_ENTRIES = 1000 * 1000;\n    final int KEY_LENGTH = 24;\n    final int VALUE_LENGTH = 40;\n    final BytesToBytesMap map =\n      new BytesToBytesMap(dataNodeMemoryManager, NUM_ENTRIES, PAGE_SIZE_BYTES);\n    // Each record will take 8 + 24 + 40 = 72 bytes of space in the data page. Our 64-megabyte\n    // pages won't be evenly-divisible by records of this size, which will cause us to waste some\n    // space at the end of the page. This is necessary in order for us to take the end-of-record\n    // handling branch in iterator().\n    try {\n      for (int i = 0; i < NUM_ENTRIES; i++) {\n        final long[] key = new long[] { i, i, i };  // 3 * 8 = 24 bytes\n        final long[] value = new long[] { i, i, i, i, i }; // 5 * 8 = 40 bytes\n\n        final BytesToBytesMap.Location loc = map.lookup(\n          key,\n          Platform.LONG_ARRAY_OFFSET,\n          KEY_LENGTH\n        );\n\n        Assert.assertFalse(loc.isDefined());\n        Assert.assertTrue(loc.append(\n          key,\n          Platform.LONG_ARRAY_OFFSET,\n          KEY_LENGTH,\n          value,\n          Platform.LONG_ARRAY_OFFSET,\n          VALUE_LENGTH\n        ));\n      }\n      Assert.assertEquals(2, map.getNumDataPages());\n\n      final BitSet valuesSeen = new BitSet(NUM_ENTRIES);\n      final Iterator<BytesToBytesMap.Location> iter = map.iterator();\n      final long[] key = new long[KEY_LENGTH / 8];\n      final long[] value = new long[VALUE_LENGTH / 8];\n      while (iter.hasNext()) {\n        final BytesToBytesMap.Location loc = iter.next();\n        Assert.assertTrue(loc.isDefined());\n        Assert.assertEquals(KEY_LENGTH, loc.getKeyLength());\n        Assert.assertEquals(VALUE_LENGTH, loc.getValueLength());\n        Platform.copyMemory(\n          loc.getKeyBase(),\n          loc.getKeyOffset(),\n          key,\n          Platform.LONG_ARRAY_OFFSET,\n          KEY_LENGTH\n        );\n        Platform.copyMemory(\n          loc.getValueBase(),\n          loc.getValueOffset(),\n          value,\n          Platform.LONG_ARRAY_OFFSET,\n          VALUE_LENGTH\n        );\n        for (long j : key) {\n          Assert.assertEquals(key[0], j);\n        }\n        for (long j : value) {\n          Assert.assertEquals(key[0], j);\n        }\n        valuesSeen.set((int) key[0]);\n      }\n      Assert.assertEquals(NUM_ENTRIES, valuesSeen.cardinality());\n    } finally {\n      map.free();\n    }\n  }\n\n  @Test\n  public void randomizedStressTest() {\n    final int size = 65536;\n    // Java arrays' hashCodes() aren't based on the arrays' contents, so we need to wrap arrays\n    // into ByteBuffers in order to use them as keys here.\n    final Map<ByteBuffer, byte[]> expected = new HashMap<ByteBuffer,byte[]>();\n    final BytesToBytesMap map = new BytesToBytesMap(dataNodeMemoryManager, size, PAGE_SIZE_BYTES);\n    try {\n      // Fill the map to 90% full so that we can trigger probing\n      for (int i = 0; i < size * 0.9; i++) {\n        final byte[] key = getRandomByteArray(rand.nextInt(10) + 1);\n        final byte[] value = getRandomByteArray(rand.nextInt(10) + 1);\n\n        if (!expected.containsKey(ByteBuffer.wrap(key))) {\n          expected.put(ByteBuffer.wrap(key), value);\n          final BytesToBytesMap.Location loc = map.lookup(\n            key,\n            Platform.BYTE_ARRAY_OFFSET,\n            key.length\n          );\n          Assert.assertFalse(loc.isDefined());\n          Assert.assertTrue(loc.append(\n            key,\n            Platform.BYTE_ARRAY_OFFSET,\n            key.length,\n            value,\n            Platform.BYTE_ARRAY_OFFSET,\n            value.length\n          ));\n          // After calling putNewKey, the following should be true, even before calling\n          // lookup():\n          Assert.assertTrue(loc.isDefined());\n          Assert.assertEquals(key.length, loc.getKeyLength());\n          Assert.assertEquals(value.length, loc.getValueLength());\n          Assert.assertTrue(arrayEquals(key, loc.getKeyBase(), loc.getKeyOffset(), key.length));\n          Assert.assertTrue(\n            arrayEquals(value, loc.getValueBase(), loc.getValueOffset(), value.length));\n        }\n      }\n\n/**\n      for (Map.Entry<ByteBuffer, byte[]> entry : expected.entrySet()) {\n        final byte[] key = JavaUtils.bufferToArray(entry.getKey());\n        final byte[] value = entry.getValue();\n        final BytesToBytesMap.Location loc =\n          map.lookup(key, Platform.BYTE_ARRAY_OFFSET, key.length);\n        Assert.assertTrue(loc.isDefined());\n        Assert.assertTrue(\n          arrayEquals(key, loc.getKeyBase(), loc.getKeyOffset(), loc.getKeyLength()));\n        Assert.assertTrue(\n          arrayEquals(value, loc.getValueBase(), loc.getValueOffset(), loc.getValueLength()));\n }\n*/\n    } finally {\n      map.free();\n    }\n  }\n\n  @Test\n  public void randomizedTestWithRecordsLargerThanPageSize() {\n    final long pageSizeBytes = 128;\n    final BytesToBytesMap map = new BytesToBytesMap(dataNodeMemoryManager, 64, pageSizeBytes);\n    // Java arrays' hashCodes() aren't based on the arrays' contents, so we need to wrap arrays\n    // into ByteBuffers in order to use them as keys here.\n    final Map<ByteBuffer, byte[]> expected = new HashMap<ByteBuffer, byte[]>();\n    try {\n      for (int i = 0; i < 1000; i++) {\n        final byte[] key = getRandomByteArray(rand.nextInt(128));\n        final byte[] value = getRandomByteArray(rand.nextInt(128));\n        if (!expected.containsKey(ByteBuffer.wrap(key))) {\n          expected.put(ByteBuffer.wrap(key), value);\n          final BytesToBytesMap.Location loc = map.lookup(\n            key,\n            Platform.BYTE_ARRAY_OFFSET,\n            key.length\n          );\n          Assert.assertFalse(loc.isDefined());\n          Assert.assertTrue(loc.append(\n            key,\n            Platform.BYTE_ARRAY_OFFSET,\n            key.length,\n            value,\n            Platform.BYTE_ARRAY_OFFSET,\n            value.length\n          ));\n          // After calling putNewKey, the following should be true, even before calling\n          // lookup():\n          Assert.assertTrue(loc.isDefined());\n          Assert.assertEquals(key.length, loc.getKeyLength());\n          Assert.assertEquals(value.length, loc.getValueLength());\n          Assert.assertTrue(arrayEquals(key, loc.getKeyBase(), loc.getKeyOffset(), key.length));\n          Assert.assertTrue(\n            arrayEquals(value, loc.getValueBase(), loc.getValueOffset(), value.length));\n        }\n      }\n/**\n      for (Map.Entry<ByteBuffer, byte[]> entry : expected.entrySet()) {\n        final byte[] key = JavaUtils.bufferToArray(entry.getKey());\n        final byte[] value = entry.getValue();\n        final BytesToBytesMap.Location loc =\n          map.lookup(key, Platform.BYTE_ARRAY_OFFSET, key.length);\n        Assert.assertTrue(loc.isDefined());\n        Assert.assertTrue(\n          arrayEquals(key, loc.getKeyBase(), loc.getKeyOffset(), loc.getKeyLength()));\n        Assert.assertTrue(\n          arrayEquals(value, loc.getValueBase(), loc.getValueOffset(), loc.getValueLength()));\n      }\n*/\n    } finally {\n      map.free();\n    }\n  }\n\n  @Test\n  public void failureToAllocateFirstPage() {\n    memoryManager.limit(1024);  // longArray\n    BytesToBytesMap map = new BytesToBytesMap(dataNodeMemoryManager, 1, PAGE_SIZE_BYTES);\n    try {\n      final long[] emptyArray = new long[0];\n      final BytesToBytesMap.Location loc =\n        map.lookup(emptyArray, Platform.LONG_ARRAY_OFFSET, 0);\n      Assert.assertFalse(loc.isDefined());\n      Assert.assertFalse(loc.append(\n        emptyArray, Platform.LONG_ARRAY_OFFSET, 0, emptyArray, Platform.LONG_ARRAY_OFFSET, 0));\n    } finally {\n      map.free();\n    }\n  }\n\n\n  @Test\n  public void failureToGrow() {\n    BytesToBytesMap map = new BytesToBytesMap(dataNodeMemoryManager, 1, 1024);\n    try {\n      boolean success = true;\n      int i;\n      for (i = 0; i < 127; i++) {\n        if (i > 0) {\n          memoryManager.limit(0);\n        }\n        final long[] arr = new long[]{i};\n        final BytesToBytesMap.Location loc = map.lookup(arr, Platform.LONG_ARRAY_OFFSET, 8);\n        success =\n          loc.append(arr, Platform.LONG_ARRAY_OFFSET, 8, arr, Platform.LONG_ARRAY_OFFSET, 8);\n        if (!success) {\n          break;\n        }\n      }\n      Assert.assertThat(i, greaterThan(0));\n      Assert.assertFalse(success);\n    } finally {\n      map.free();\n    }\n  }\n\n  @Test\n  public void spillInIterator() throws IOException {\n    BytesToBytesMap map = new BytesToBytesMap(\n            dataNodeMemoryManager, blockManager, serializerManager, 1, 0.75, 1024, false);\n    try {\n      int i;\n      for (i = 0; i < 1024; i++) {\n        final long[] arr = new long[]{i};\n        final BytesToBytesMap.Location loc = map.lookup(arr, Platform.LONG_ARRAY_OFFSET, 8);\n        loc.append(arr, Platform.LONG_ARRAY_OFFSET, 8, arr, Platform.LONG_ARRAY_OFFSET, 8);\n      }\n      BytesToBytesMap.MapIterator iter = map.iterator();\n      for (i = 0; i < 100; i++) {\n        iter.next();\n      }\n      // Non-destructive iterator is not spillable\n      Assert.assertEquals(0, iter.spill(1024L * 10));\n      for (i = 100; i < 1024; i++) {\n        iter.next();\n      }\n\n      BytesToBytesMap.MapIterator iter2 = map.destructiveIterator();\n      for (i = 0; i < 100; i++) {\n        iter2.next();\n      }\n      Assert.assertTrue(iter2.spill(1024) >= 1024);\n      for (i = 100; i < 1024; i++) {\n        iter2.next();\n      }\n      assertFalse(iter2.hasNext());\n    } finally {\n      map.free();\n      for (File spillFile : spillFilesCreated) {\n        assertFalse(\"Spill file \" + spillFile.getPath() + \" was not cleaned up\",\n          spillFile.exists());\n      }\n    }\n  }\n\n  @Test\n  public void multipleValuesForSameKey() {\n    BytesToBytesMap map =\n      new BytesToBytesMap(dataNodeMemoryManager, blockManager, serializerManager, 1, 0.75, 1024, false);\n    try {\n      int i;\n      for (i = 0; i < 1024; i++) {\n        final long[] arr = new long[]{i};\n        map.lookup(arr, Platform.LONG_ARRAY_OFFSET, 8)\n          .append(arr, Platform.LONG_ARRAY_OFFSET, 8, arr, Platform.LONG_ARRAY_OFFSET, 8);\n      }\n      assert map.numKeys() == 1024;\n      assert map.numValues() == 1024;\n      for (i = 0; i < 1024; i++) {\n        final long[] arr = new long[]{i};\n        map.lookup(arr, Platform.LONG_ARRAY_OFFSET, 8)\n          .append(arr, Platform.LONG_ARRAY_OFFSET, 8, arr, Platform.LONG_ARRAY_OFFSET, 8);\n      }\n      assert map.numKeys() == 1024;\n      assert map.numValues() == 2048;\n      for (i = 0; i < 1024; i++) {\n        final long[] arr = new long[]{i};\n        final BytesToBytesMap.Location loc = map.lookup(arr, Platform.LONG_ARRAY_OFFSET, 8);\n        assert loc.isDefined();\n        assert loc.nextValue();\n        assert !loc.nextValue();\n      }\n      BytesToBytesMap.MapIterator iter = map.iterator();\n      for (i = 0; i < 2048; i++) {\n        assert iter.hasNext();\n        final BytesToBytesMap.Location loc = iter.next();\n        assert loc.isDefined();\n      }\n    } finally {\n      map.free();\n    }\n  }\n\n  @Test\n  public void initialCapacityBoundsChecking() {\n    try {\n      new BytesToBytesMap(dataNodeMemoryManager, 0, PAGE_SIZE_BYTES);\n      Assert.fail(\"Expected IllegalArgumentException to be thrown\");\n    } catch (IllegalArgumentException e) {\n      // expected exception\n    }\n\n    try {\n      new BytesToBytesMap(\n              dataNodeMemoryManager,\n        BytesToBytesMap.MAX_CAPACITY + 1,\n        PAGE_SIZE_BYTES);\n      Assert.fail(\"Expected IllegalArgumentException to be thrown\");\n    } catch (IllegalArgumentException e) {\n      // expected exception\n    }\n  }\n\n  @Test\n  public void testPeakMemoryUsed() {\n    final long recordLengthBytes = 32;\n    final long pageSizeBytes = 256 + 8; // 8 bytes for end-of-page marker\n    final long numRecordsPerPage = (pageSizeBytes - 8) / recordLengthBytes;\n    final BytesToBytesMap map = new BytesToBytesMap(dataNodeMemoryManager, 1024, pageSizeBytes);\n\n    // Since BytesToBytesMap is append-only, we expect the total memory consumption to be\n    // monotonically increasing. More specifically, every time we allocate a new page it\n    // should increase by exactly the size of the page. In this regard, the memory usage\n    // at any given time is also the peak memory used.\n    long previousPeakMemory = map.getPeakMemoryUsedBytes();\n    long newPeakMemory;\n    try {\n      for (long i = 0; i < numRecordsPerPage * 10; i++) {\n        final long[] value = new long[]{i};\n        map.lookup(value, Platform.LONG_ARRAY_OFFSET, 8).append(\n          value,\n          Platform.LONG_ARRAY_OFFSET,\n          8,\n          value,\n          Platform.LONG_ARRAY_OFFSET,\n          8);\n        newPeakMemory = map.getPeakMemoryUsedBytes();\n        if (i % numRecordsPerPage == 0) {\n          // We allocated a new page for this record, so peak memory should change\n          assertEquals(previousPeakMemory + pageSizeBytes, newPeakMemory);\n        } else {\n          assertEquals(previousPeakMemory, newPeakMemory);\n        }\n        previousPeakMemory = newPeakMemory;\n      }\n\n      // Freeing the map should not change the peak memory\n      map.free();\n      newPeakMemory = map.getPeakMemoryUsedBytes();\n      assertEquals(previousPeakMemory, newPeakMemory);\n\n    } finally {\n      map.free();\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/map/BytesToBytesMapOffHeapSuite.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.map;\n\npublic class BytesToBytesMapOffHeapSuite extends AbstractBytesToBytesMapSuite {\n\n  @Override\n  protected boolean useOffHeapMemoryAllocator() {\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/map/BytesToBytesMapOnHeapSuite.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.map;\n\npublic class BytesToBytesMapOnHeapSuite extends AbstractBytesToBytesMapSuite {\n\n  @Override\n  protected boolean useOffHeapMemoryAllocator() {\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/map/MapSorterByValueTest.java",
    "content": "package io.mycat.memory.unsafe.map;\n\nimport org.junit.Test;\n\nimport java.util.*;\n\n/**\n * Created by znix on 2016/7/4.\n */\npublic class MapSorterByValueTest {\n    @Test\n    public void testMapSorterByValue(){\n        Map<String, Integer> map = new HashMap<String, Integer>();\n        map.put(\"q\",23);\n        map.put(\"b\",4);\n        map.put(\"c\",5);\n        map.put(\"d\",6);\n\n        Map<String, Integer> resultMap = mapSorterByValue(map); //按Value进行排序\n\n        for (Map.Entry<String,Integer> entry : resultMap.entrySet()) {\n            System.out.println(entry.getKey() + \" \" + entry.getValue());\n        }\n    }\n\n    private Map<String,Integer> mapSorterByValue(Map<String,Integer> map) {\n        if (map == null || map.isEmpty()) {\n            return null;\n        }\n\n        Map<String,Integer> sortedMap = new LinkedHashMap<String, Integer>();\n\n        List<Map.Entry<String, Integer>> entryList = new ArrayList<\n                Map.Entry<String, Integer>>(\n                map.entrySet());\n\n        Collections.sort(entryList, new Comparator<Map.Entry<String, Integer>>() {\n            @Override\n            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {\n                return o1.getValue().compareTo(o2.getValue());\n            }\n        });\n\n        Iterator<Map.Entry<String, Integer>> iter = entryList.iterator();\n        Map.Entry<String, Integer> tmpEntry = null;\n        while (iter.hasNext()) {\n            tmpEntry = iter.next();\n            sortedMap.put(tmpEntry.getKey(), tmpEntry.getValue());\n        }\n        return sortedMap;\n    }\n}\n\n\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/map/UnsafeFixedWidthAggregationMapSuite.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.map;\n\nimport io.mycat.memory.MyCatMemory;\nimport io.mycat.memory.unsafe.KVIterator;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.MemoryManager;\nimport io.mycat.memory.unsafe.row.BufferHolder;\nimport io.mycat.memory.unsafe.row.StructType;\nimport io.mycat.memory.unsafe.row.UnsafeRow;\nimport io.mycat.memory.unsafe.row.UnsafeRowWriter;\nimport io.mycat.memory.unsafe.utils.BytesTools;\nimport io.mycat.sqlengine.mpp.ColMeta;\nimport io.mycat.sqlengine.mpp.OrderCol;\nimport org.apache.log4j.Logger;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n\nimport java.io.IOException;\nimport java.util.*;\n\n/**\n * Created by zagnix on 2016/6/4.\n */\npublic class UnsafeFixedWidthAggregationMapSuite {\n  private StructType groupKeySchema ;\n  private StructType aggBufferSchema;\n  private UnsafeRow emptyAggregationBuffer;\n  private long PAGE_SIZE_BYTES  = 1L << 20;\n\n  private final Random rand = new Random(42);\n\n  private static Logger LOGGER = Logger.getLogger(UnsafeFixedWidthAggregationMapSuite.class);\n  @Test\n  public void testAggregateMap() throws NoSuchFieldException, IllegalAccessException, IOException {\n    /**\n     * 创造上文环境\n     */\n    MyCatMemory myCatMemory = new MyCatMemory();\n    MemoryManager memoryManager = myCatMemory.getResultMergeMemoryManager();\n    DataNodeMemoryManager dataNodeMemoryManager = new DataNodeMemoryManager(memoryManager, Thread.currentThread().getId());\n\n      /**\n       * 构造数据字段group key\n       */\n\n    int fieldCount = 2;\n    ColMeta colMeta = null;\n    Map<String,ColMeta> colMetaMap = new HashMap<String,ColMeta>(fieldCount);\n    colMeta = new ColMeta(0,ColMeta.COL_TYPE_STRING);\n    colMetaMap.put(\"id\",colMeta);\n    colMeta = new ColMeta(1,ColMeta.COL_TYPE_STRING);\n    colMetaMap.put(\"name\",colMeta);\n\n    OrderCol[] orderCols = new OrderCol[1];\n    OrderCol orderCol = new OrderCol(colMetaMap.get(\"id\"),OrderCol.COL_ORDER_TYPE_DESC);\n    orderCols[0] = orderCol;\n\n    groupKeySchema = new StructType(colMetaMap,fieldCount);\n    groupKeySchema.setOrderCols(orderCols);\n\n\n  /**\n   * 构造数据字段value key\n   */\n    fieldCount = 4;\n    colMeta = null;\n    colMetaMap = new HashMap<String,ColMeta>(fieldCount);\n    colMeta = new ColMeta(0,ColMeta.COL_TYPE_STRING);\n    colMetaMap.put(\"id\",colMeta);\n    colMeta = new ColMeta(1,ColMeta.COL_TYPE_STRING);\n    colMetaMap.put(\"name\",colMeta);\n    colMeta = new ColMeta(2,ColMeta.COL_TYPE_INT);\n    colMetaMap.put(\"age\",colMeta);\n\n    colMeta = new ColMeta(3,ColMeta.COL_TYPE_LONGLONG);\n    colMetaMap.put(\"score\",colMeta);\n\n\n    orderCols = new OrderCol[1];\n    orderCol = new OrderCol(colMetaMap.get(\"id\"),OrderCol.COL_ORDER_TYPE_DESC);\n    orderCols[0] = orderCol;\n\n    aggBufferSchema = new StructType(colMetaMap,fieldCount);\n    aggBufferSchema.setOrderCols(orderCols);\n\n    /**\n     *emtpy Row value\n     */\n    BufferHolder bufferHolder ;\n    emptyAggregationBuffer = new UnsafeRow(4);\n    bufferHolder = new BufferHolder(emptyAggregationBuffer,0);\n    UnsafeRowWriter unsafeRowWriter = new UnsafeRowWriter(bufferHolder,4);\n    bufferHolder.reset();\n    String value = \"o\";\n    unsafeRowWriter.write(0,value.getBytes());\n    unsafeRowWriter.write(1,value.getBytes());\n    emptyAggregationBuffer.setInt(2,0);\n    emptyAggregationBuffer.setLong(3,0);\n    emptyAggregationBuffer.setTotalSize(bufferHolder.totalSize());\n\n\n    UnsafeFixedWidthAggregationMap map = new UnsafeFixedWidthAggregationMap(\n            emptyAggregationBuffer,\n            aggBufferSchema,\n            groupKeySchema,\n            dataNodeMemoryManager,\n            2*1024,\n            PAGE_SIZE_BYTES,\n            true);\n\n\n      /**\n       * 造数据\n       */\n\n    int i;\n\n    List<UnsafeRow> rows = new  ArrayList<UnsafeRow>();\n    for ( i = 0; i < 100000; i++) {\n      /**\n       * key\n       */\n      UnsafeRow groupKey = new UnsafeRow(2);\n      bufferHolder = new BufferHolder(groupKey,0);\n      unsafeRowWriter = new UnsafeRowWriter(bufferHolder,2);\n      bufferHolder.reset();\n\n      unsafeRowWriter.write(0, BytesTools.toBytes(rand.nextInt(10000000)));\n      unsafeRowWriter.write(1,BytesTools.toBytes(rand.nextInt(10000000)));\n\n      groupKey.setTotalSize(bufferHolder.totalSize());\n\n      UnsafeRow valueKey = new UnsafeRow(4);\n      bufferHolder = new BufferHolder(valueKey,0);\n      unsafeRowWriter = new UnsafeRowWriter(bufferHolder,4);\n      bufferHolder.reset();\n\n      unsafeRowWriter.write(0, BytesTools.toBytes(rand.nextInt(10)));\n      unsafeRowWriter.write(1,BytesTools.toBytes(rand.nextInt(10)));\n      valueKey.setInt(2,i);\n      valueKey.setLong(3,1);\n      valueKey.setTotalSize(bufferHolder.totalSize());\n\n      if(map.find(groupKey)){\n          UnsafeRow rs = map.getAggregationBuffer(groupKey);\n          rs.setLong(3,i+valueKey.getLong(3));\n          rs.setInt(2,100+valueKey.getInt(2));\n      }else {\n        map.put(groupKey,valueKey);\n      }\n      rows.add(valueKey);\n    }\n\n\n    KVIterator<UnsafeRow,UnsafeRow> iter = map.iterator();\n    int j = 0;\n    while (iter.next()){\n      Assert.assertEquals(j,iter.getValue().getInt(2));\n      j++;\n      iter.getValue().setInt(2,5000000);\n      iter.getValue().setLong(3,600000);\n    }\n\n    Assert.assertEquals(rows.size(),j);\n    int k = 0;\n    KVIterator<UnsafeRow,UnsafeRow> iter1 = map.iterator();\n    while (iter1.next()){\n      k++;\n     // LOGGER.error(\"(\" + BytesTools.toInt(iter1.getKey().getBinary(0)) + \",\" +\n      //       iter1.getValue().getInt(2) +\",\" +iter1.getValue().getLong(3)+\")\");\n\n      Assert.assertEquals(5000000,iter1.getValue().getInt(2));\n      Assert.assertEquals(600000,iter1.getValue().getLong(3));\n    }\n\n    Assert.assertEquals(j,k);\n\n    map.free();\n\n  }\n@Test\npublic void  testWithMemoryLeakDetection() throws IOException, NoSuchFieldException, IllegalAccessException {\n  MyCatMemory myCatMemory = new MyCatMemory();\n  MemoryManager memoryManager = myCatMemory.getResultMergeMemoryManager();\n  DataNodeMemoryManager dataNodeMemoryManager = new DataNodeMemoryManager(memoryManager,\n          Thread.currentThread().getId());\n    int fieldCount = 3;\n    ColMeta colMeta = null;\n    Map<String,ColMeta> colMetaMap = new HashMap<String,ColMeta>(fieldCount);\n    colMeta = new ColMeta(0,ColMeta.COL_TYPE_STRING);\n    colMetaMap.put(\"id\",colMeta);\n    colMeta = new ColMeta(1,ColMeta.COL_TYPE_STRING);\n    colMetaMap.put(\"name\",colMeta);\n    colMeta = new ColMeta(2,ColMeta.COL_TYPE_STRING);\n    colMetaMap.put(\"age\",colMeta);\n\n\n    OrderCol[] orderCols = new OrderCol[1];\n    OrderCol orderCol = new OrderCol(colMetaMap.get(\"id\"),OrderCol.COL_ORDER_TYPE_DESC);\n    orderCols[0] = orderCol;\n\n    groupKeySchema = new StructType(colMetaMap,fieldCount);\n    groupKeySchema.setOrderCols(orderCols);\n\n\n\n   fieldCount = 3;\n   colMeta = null;\n   colMetaMap = new HashMap<String,ColMeta>(fieldCount);\n   colMeta = new ColMeta(0,ColMeta.COL_TYPE_LONGLONG);\n   colMetaMap.put(\"age\",colMeta);\n   colMeta = new ColMeta(1,ColMeta.COL_TYPE_LONGLONG);\n   colMetaMap.put(\"age1\",colMeta);\n   colMeta = new ColMeta(2,ColMeta.COL_TYPE_STRING);\n   colMetaMap.put(\"name\",colMeta);\n\n   orderCols = new OrderCol[1];\n   orderCol = new OrderCol(colMetaMap.get(\"id\"),OrderCol.COL_ORDER_TYPE_DESC);\n   orderCols[0] = orderCol;\n\n  aggBufferSchema = new StructType(colMetaMap,fieldCount);\n  aggBufferSchema.setOrderCols(orderCols);\n\n  /**\n   * value\n   */\n  BufferHolder bufferHolder ;\n  emptyAggregationBuffer = new UnsafeRow(3);\n  bufferHolder = new BufferHolder(emptyAggregationBuffer,0);\n  UnsafeRowWriter unsafeRowWriter = new UnsafeRowWriter(bufferHolder,3);\n  bufferHolder.reset();\n  String value = \"ok,hello\";\n  emptyAggregationBuffer.setLong(0,0);\n  emptyAggregationBuffer.setLong(1,0);\n  unsafeRowWriter.write(2,value.getBytes());\n  emptyAggregationBuffer.setTotalSize(bufferHolder.totalSize());\n\n  UnsafeFixedWidthAggregationMap map = new UnsafeFixedWidthAggregationMap(\n          emptyAggregationBuffer,\n          aggBufferSchema,\n          groupKeySchema,\n          dataNodeMemoryManager,\n          2*1024,\n          PAGE_SIZE_BYTES,\n          false\n  );\n\n\n  int i;\n\n  List<UnsafeRow> rows = new  ArrayList<UnsafeRow>();\n  for ( i = 0; i < 1000; i++) {\n    String line = \"testUnsafeRow\" + i;\n    /**\n     * key\n     */\n    UnsafeRow groupKey = new UnsafeRow(3);\n    bufferHolder = new BufferHolder(groupKey,0);\n    unsafeRowWriter = new UnsafeRowWriter(bufferHolder,3);\n    bufferHolder.reset();\n\n    final byte[] key = getRandomByteArray(rand.nextInt(8));\n    String age = \"5\"+i;\n    unsafeRowWriter.write(0,key);\n    unsafeRowWriter.write(1,line.getBytes());\n    unsafeRowWriter.write(2,age.getBytes());\n    groupKey.setTotalSize(bufferHolder.totalSize());\n\n    map.getAggregationBuffer(groupKey);\n\n    rows.add(groupKey);\n  }\n\n  Assert.assertEquals(i ,rows.size() );\n\n\n\n  UnsafeRow row = rows.get(12);\n  UnsafeRow rs = map.getAggregationBuffer(row);\n  rs.setLong(0,12);\n  rs = map.getAggregationBuffer(row);\n  Assert.assertEquals(12,rs.getLong(0));\n\n  map.free();\n\n  }\n\n  private byte[] getRandomByteArray(int numWords) {\n    Assert.assertTrue(numWords >= 0);\n    final int lengthInBytes = numWords * 8;\n    final byte[] bytes = new byte[lengthInBytes];\n    rand.nextBytes(bytes);\n    return bytes;\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/memory/MemoryManagerSuite.java",
    "content": "package io.mycat.memory.unsafe.memory;\n\n/**\n * Created by zagnix on 2016/6/6.\n */\npublic interface MemoryManagerSuite {\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/memory/MycatMemoryTest.java",
    "content": "package io.mycat.memory.unsafe.memory;\n\n\nimport io.mycat.memory.MyCatMemory;\nimport io.mycat.memory.unsafe.Platform;\nimport org.junit.Test;\n\n/**\n * Created by zagnix on 2016/6/12.\n */\npublic class MycatMemoryTest {\n\n    /**\n     * -Xmx1024m -XX:MaxDirectMemorySize=1G\n     */\n    @Test\n    public void testMycatMemory() throws NoSuchFieldException, IllegalAccessException {\n        MyCatMemory myCatMemory = new MyCatMemory();\n        System.out.println(myCatMemory.getResultSetBufferSize());\n        System.out.println(Platform.getMaxHeapMemory());\n        System.out.println(Platform.getMaxDirectMemory());\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/memory/TaskMemoryManagerSuite.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.memory;\n\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.MemoryMode;\nimport io.mycat.memory.unsafe.memory.mm.ResultMergeMemoryManager;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class TaskMemoryManagerSuite {\n\n  @Test\n  public void leakedPageMemoryIsDetected() {\n    final DataNodeMemoryManager manager = new DataNodeMemoryManager(\n      new ResultMergeMemoryManager(\n        new MycatPropertyConf().set(\"mycat.memory.offHeap.enabled\", \"false\")\n              .set(\"mycat.memory.offHeap.size\",\"32768\"),\n              1,\n        Long.MAX_VALUE\n       ),\n      0);\n    manager.allocatePage(4096, null);  // leak memory\n    Assert.assertEquals(4096, manager.getMemoryConsumptionForThisConnection());\n    Assert.assertEquals(4096, manager.cleanUpAllAllocatedMemory());\n  }\n\n  @Test\n  public void encodePageNumberAndOffsetOffHeap() {\n    final MycatPropertyConf conf = new MycatPropertyConf()\n      .set(\"mycat.memory.offHeap.enabled\", \"true\")\n      .set(\"mycat.memory.offHeap.size\", \"1000\");\n    final DataNodeMemoryManager manager = new DataNodeMemoryManager(new TestMemoryManager(conf), 0);\n    final MemoryBlock dataPage = manager.allocatePage(256, null);\n    // In off-heap mode, an offset is an absolute address that may require more than 51 bits to\n    // encode. This map exercises that corner-case:\n    final long offset = ((1L << DataNodeMemoryManager.OFFSET_BITS) + 10);\n    final long encodedAddress = manager.encodePageNumberAndOffset(dataPage, offset);\n    Assert.assertEquals(null, manager.getPage(encodedAddress));\n    Assert.assertEquals(offset, manager.getOffsetInPage(encodedAddress));\n  }\n\n  @Test\n  public void encodePageNumberAndOffsetOnHeap() {\n    final DataNodeMemoryManager manager = new DataNodeMemoryManager(\n      new TestMemoryManager(new MycatPropertyConf().set(\"mycat.memory.offHeap.enabled\", \"false\")), 0);\n    final MemoryBlock dataPage = manager.allocatePage(256, null);\n    final long encodedAddress = manager.encodePageNumberAndOffset(dataPage, 64);\n    Assert.assertEquals(dataPage.getBaseObject(), manager.getPage(encodedAddress));\n    Assert.assertEquals(64, manager.getOffsetInPage(encodedAddress));\n  }\n\n  @Test\n  public void cooperativeSpilling() throws InterruptedException {\n    final TestMemoryManager memoryManager = new TestMemoryManager(new MycatPropertyConf());\n    memoryManager.limit(100);\n    final DataNodeMemoryManager manager = new DataNodeMemoryManager(memoryManager, 0);\n\n    TestMemoryConsumer c1 = new TestMemoryConsumer(manager);\n    TestMemoryConsumer c2 = new TestMemoryConsumer(manager);\n    c1.use(100);\n    Assert.assertEquals(100, c1.getUsed());\n    c2.use(100);\n    Assert.assertEquals(100, c2.getUsed());\n    Assert.assertEquals(0, c1.getUsed());  // spilled\n    c1.use(100);\n    Assert.assertEquals(100, c1.getUsed());\n    Assert.assertEquals(0, c2.getUsed());  // spilled\n\n    c1.use(50);\n    Assert.assertEquals(50, c1.getUsed());  // spilled\n    Assert.assertEquals(0, c2.getUsed());\n    c2.use(50);\n    Assert.assertEquals(50, c1.getUsed());\n    Assert.assertEquals(50, c2.getUsed());\n\n    c1.use(100);\n    Assert.assertEquals(100, c1.getUsed());\n    Assert.assertEquals(0, c2.getUsed());  // spilled\n\n    c1.free(20);\n    Assert.assertEquals(80, c1.getUsed());\n    c2.use(10);\n    Assert.assertEquals(80, c1.getUsed());\n    Assert.assertEquals(10, c2.getUsed());\n    c2.use(100);\n    Assert.assertEquals(100, c2.getUsed());\n    Assert.assertEquals(0, c1.getUsed());  // spilled\n\n    c1.free(0);\n    c2.free(100);\n    Assert.assertEquals(0, manager.cleanUpAllAllocatedMemory());\n  }\n\n  @Test\n  public void offHeapConfigurationBackwardsCompatibility() {\n    final MycatPropertyConf conf = new MycatPropertyConf()\n    .set(\"mycat.memory.offHeap.enabled\", \"true\")\n      .set(\"mycat.memory.offHeap.size\",\"1000\");\n    final DataNodeMemoryManager manager = new DataNodeMemoryManager(new TestMemoryManager(conf), 0);\n    Assert.assertSame(MemoryMode.OFF_HEAP, manager.tungstenMemoryMode);\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/memory/TestMemoryConsumer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.memory;\n\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.MemoryConsumer;\n\nimport java.io.IOException;\n\npublic class TestMemoryConsumer extends MemoryConsumer {\n  public TestMemoryConsumer(DataNodeMemoryManager memoryManager) {\n    super(memoryManager);\n  }\n\n  @Override\n  public long spill(long size, MemoryConsumer trigger) throws IOException {\n    long used = getUsed();\n    free(used);\n    return used;\n  }\n\n  void use(long size) throws InterruptedException {\n    long got = dataNodeMemoryManager.acquireExecutionMemory(\n      size,\n      dataNodeMemoryManager.tungstenMemoryMode,\n      this);\n    used += got;\n  }\n\n  void free(long size) {\n    used -= size;\n    dataNodeMemoryManager.releaseExecutionMemory(\n      size,\n      dataNodeMemoryManager.tungstenMemoryMode,\n      this);\n  }\n}\n\n\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/memory/TestMemoryManager.java",
    "content": "\n\npackage io.mycat.memory.unsafe.memory;\n\n\nimport io.mycat.memory.unsafe.memory.mm.MemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.MemoryMode;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\n\npublic  class TestMemoryManager extends MemoryManager {\n\n  public  TestMemoryManager(MycatPropertyConf conf){\n          super(conf,1, Long.MAX_VALUE);\n  }\n\n  private boolean oomOnce = false;\n  private long available = Long.MAX_VALUE;\n\n\n\n    @Override\n   protected long  acquireExecutionMemory(\n     long numBytes,\n     long taskAttemptId,\n     MemoryMode memoryMode){\n    if (oomOnce) {\n      oomOnce = false;\n      return 0;\n    } else if (available >= numBytes) {\n      available -= numBytes;\n     return numBytes;\n    } else {\n      long grant = available;\n      available = 0;\n     return grant;\n    }\n  }\n\n@Override\npublic void releaseExecutionMemory(\n        long numBytes,\n        long taskAttemptId,\n        MemoryMode memoryMode){\n    available += numBytes;\n  }\n\n\n  public void markExecutionAsOutOfMemoryOnce(){\n    oomOnce = true;\n  }\n\n  public void limit(long avail){\n    available = avail;\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/row/UnsafeRowListTest.java",
    "content": "package io.mycat.memory.unsafe.row;\n\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.ArrayList;\n\n/**\n * Created by zagnix on 2016/6/27.\n */\npublic class UnsafeRowListTest {\n    @Test\n    public  void testUnsafeRowList(){\n        ArrayList<UnsafeRow> list = new ArrayList<UnsafeRow>();\n        UnsafeRow unsafeRow  ;\n        BufferHolder bufferHolder ;\n        UnsafeRowWriter unsafeRowWriter;\n        String line = \"testUnsafeRow\";\n\n        for (int i = 0; i <10; i++) {\n            unsafeRow = new UnsafeRow(3);\n            bufferHolder = new BufferHolder(unsafeRow);\n            unsafeRowWriter = new UnsafeRowWriter(bufferHolder,3);\n            bufferHolder.reset();\n\n            unsafeRow.setInt(0,89);\n            unsafeRowWriter.write(1,line.getBytes(),0,line.length());\n            unsafeRow.setInt(2,23);\n\n            unsafeRow.setTotalSize(bufferHolder.totalSize());\n            list.add(unsafeRow);\n        }\n\n\n        for (int i = 0; i <10; i++) {\n            UnsafeRow row = list.get(i);\n            row.setInt(0,1000+i);\n        }\n\n\n        for (int i = 0; i <10; i++) {\n            UnsafeRow row = list.get(i);\n           Assert.assertEquals(1000+i,row.getInt(0));\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/row/UnsafeRowSuite.java",
    "content": "package io.mycat.memory.unsafe.row;\n\n\nimport junit.framework.Assert;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.math.BigDecimal;\n\nimport org.junit.Test;\n\n/**\n * Created by zagnix on 2016/6/10.\n */\npublic class UnsafeRowSuite {\n\n\n    @Test\n    public void  testUnsafeRowSingle(){\n        UnsafeRow unsafeRow = new UnsafeRow(5);\n        BufferHolder bufferHolder = new BufferHolder(unsafeRow,64);\n        UnsafeRowWriter unsafeRowWriter = new UnsafeRowWriter(bufferHolder,5);\n        bufferHolder.reset();\n\n        String line2 = \"testUnsafeRow3\";\n        unsafeRow.setFloat(0, 7.4f);\n        unsafeRow.setInt(1, 7);\n        unsafeRow.setLong(2,455555);\n        unsafeRowWriter.write(3,line2.getBytes(),0, line2.length());\n        unsafeRow.setNullAt(4);\n\n        unsafeRow.setInt(1, 9);\n\n        assert(unsafeRow.getFloat(0) == 7.4f);\n        assert(unsafeRow.getInt(1) == 9);\n        assert(unsafeRow.getLong(2) == 455555);\n        Assert.assertEquals(\"testUnsafeRow3\",new String(unsafeRow.getBinary(3)));\n        assert (false==unsafeRow.isNullAt(3));\n        assert (true==unsafeRow.isNullAt(4));\n    }\n    \n    public void testUnsafeRowWithDecimal() {\n     \t\n     \tint fieldCount = 4;\n     \t\n     \tString value = \"12345678901234567890123456789.0123456789\";\n     \tString value1 = \"100\";\n     \tBigDecimal decimal = new BigDecimal(value);\n     \tBigDecimal decimal1 = new BigDecimal(value1);\n     \tSystem.out.println(\"decimal precision : \" + decimal.precision() + \", scale : \" + decimal.scale());\n     \t\n     \tUnsafeRow unsafeRow = new UnsafeRow(fieldCount);\n         BufferHolder bufferHolder = new BufferHolder(unsafeRow,64);\n         UnsafeRowWriter unsafeRowWriter = new UnsafeRowWriter(bufferHolder,fieldCount);\n         bufferHolder.reset();\n         \n         unsafeRow.setInt(0, 100);\n         unsafeRow.setDouble(1, 0.99);\n         unsafeRow.setLong(2, 1000);\n         unsafeRowWriter.write(3, decimal);\n         \n         assertEquals(100, unsafeRow.getInt(0));\n         assertEquals(\"0.99\", String.valueOf(unsafeRow.getDouble(1)));\n         assertEquals(1000, unsafeRow.getLong(2));\n         assertEquals(decimal, unsafeRow.getDecimal(3, decimal.scale()));\n         \n         unsafeRow.updateDecimal(3, decimal1);\n         assertEquals(decimal1, unsafeRow.getDecimal(3, decimal1.scale()));\n         \n         // update null decimal\n         BigDecimal nullDecimal = null;\n         unsafeRow.updateDecimal(3, nullDecimal);\n         assertEquals(nullDecimal, unsafeRow.getDecimal(3, 0));\n         \n         unsafeRow.updateDecimal(3, decimal);\n         assertEquals(decimal, unsafeRow.getDecimal(3, decimal.scale()));\n         \n     }\n\n\n//    @Test\n//    public void  testUnsafeRowInsert(){\n//        UnsafeRow unsafeRow = new UnsafeRow(4);\n//\n//        assert(unsafeRow.getFloat(0) == 7.4f);\n//        assert(unsafeRow.getInt(1) == 9);\n//        assert(unsafeRow.getLong(2) == 455555);\n//        Assert.assertEquals(\"testUnsafeRow3\",new String(unsafeRow.getBinary(3)));\n//    }\n\n};\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/sort/HashPartitioner.java",
    "content": "package io.mycat.memory.unsafe.sort;\n\n\nimport io.mycat.memory.unsafe.utils.JavaUtils;\n\n/**\n * Created by zagnix on 2016/6/6.\n */\npublic class HashPartitioner {\n    private int index =0;\n    public HashPartitioner(int i) {\n        this.index = i;\n    }\n    public int getPartition(String key){\n        return JavaUtils.nonNegativeMod(key.hashCode(), index);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/sort/TestTimSort.java",
    "content": "/**\n * Copyright 2015 Stijn de Gouw\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\npackage io.mycat.memory.unsafe.sort;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * This codes generates a int array which fails the standard TimSort.\n *\n * The blog that reported the bug\n * http://www.envisage-project.eu/timsort-specification-and-verification/\n *\n * This codes was originally wrote by Stijn de Gouw, modified by Evan Yu to adapt to\n * our test suite.\n *\n * https://github.com/abstools/java-timsort-bug\n * https://github.com/abstools/java-timsort-bug/blob/master/LICENSE\n */\npublic class TestTimSort {\n\n  private static final int MIN_MERGE = 32;\n\n  /**\n   * Returns an array of integers that demonstrate the bug in TimSort\n   */\n  public static int[] getTimSortBugTestSet(int length) {\n    int minRun = minRunLength(length);\n    List<Long> runs = runsJDKWorstCase(minRun, length);\n    return createArray(runs, length);\n  }\n\n  private static int minRunLength(int n) {\n    int r = 0; // Becomes 1 if any 1 bits are shifted off\n    while (n >= MIN_MERGE) {\n      r |= (n & 1);\n      n >>= 1;\n    }\n    return n + r;\n  }\n\n  private static int[] createArray(List<Long> runs, int length) {\n    int[] a = new int[length];\n    Arrays.fill(a, 0);\n    int endRun = -1;\n    for (long len : runs) {\n      a[endRun += len] = 1;\n    }\n    a[length - 1] = 0;\n    return a;\n  }\n\n  /**\n   * Fills <code>runs</code> with a sequence of run lengths of the form<br>\n   * Y_n     x_{n,1}   x_{n,2}   ... x_{n,l_n} <br>\n   * Y_{n-1} x_{n-1,1} x_{n-1,2} ... x_{n-1,l_{n-1}} <br>\n   * ... <br>\n   * Y_1     x_{1,1}   x_{1,2}   ... x_{1,l_1}<br>\n   * The Y_i's are chosen to satisfy the invariant throughout execution,\n   * but the x_{i,j}'s are merged (by <code>TimSort.mergeCollapse</code>)\n   * into an X_i that violates the invariant.\n   *\n   * @param length The sum of all run lengths that will be added to <code>runs</code>.\n   */\n  private static List<Long> runsJDKWorstCase(int minRun, int length) {\n    List<Long> runs = new ArrayList<>();\n\n    long runningTotal = 0, Y = minRun + 4, X = minRun;\n\n    while (runningTotal + Y + X <= length) {\n      runningTotal += X + Y;\n      generateJDKWrongElem(runs, minRun, X);\n      runs.add(0, Y);\n      // X_{i+1} = Y_i + x_{i,1} + 1, since runs.get(1) = x_{i,1}\n      X = Y + runs.get(1) + 1;\n      // Y_{i+1} = X_{i+1} + Y_i + 1\n      Y += X + 1;\n    }\n\n    if (runningTotal + X <= length) {\n      runningTotal += X;\n      generateJDKWrongElem(runs, minRun, X);\n    }\n\n    runs.add(length - runningTotal);\n    return runs;\n  }\n\n  /**\n   * Adds a sequence x_1, ..., x_n of run lengths to <code>runs</code> such that:<br>\n   * 1. X = x_1 + ... + x_n <br>\n   * 2. x_j >= minRun for all j <br>\n   * 3. x_1 + ... + x_{j-2}  <  x_j  <  x_1 + ... + x_{j-1} for all j <br>\n   * These conditions guarantee that TimSort merges all x_j's one by one\n   * (resulting in X) using only merges on the second-to-last element.\n   *\n   * @param X The sum of the sequence that should be added to runs.\n   */\n  private static void generateJDKWrongElem(List<Long> runs, int minRun, long X) {\n    for (long newTotal; X >= 2 * minRun + 1; X = newTotal) {\n      //Default strategy\n      newTotal = X / 2 + 1;\n      //Specialized strategies\n      if (3 * minRun + 3 <= X && X <= 4 * minRun + 1) {\n        // add x_1=MIN+1, x_2=MIN, x_3=X-newTotal  to runs\n        newTotal = 2 * minRun + 1;\n      } else if (5 * minRun + 5 <= X && X <= 6 * minRun + 5) {\n        // add x_1=MIN+1, x_2=MIN, x_3=MIN+2, x_4=X-newTotal  to runs\n        newTotal = 3 * minRun + 3;\n      } else if (8 * minRun + 9 <= X && X <= 10 * minRun + 9) {\n        // add x_1=MIN+1, x_2=MIN, x_3=MIN+2, x_4=2MIN+2, x_5=X-newTotal  to runs\n        newTotal = 5 * minRun + 5;\n      } else if (13 * minRun + 15 <= X && X <= 16 * minRun + 17) {\n        // add x_1=MIN+1, x_2=MIN, x_3=MIN+2, x_4=2MIN+2, x_5=3MIN+4, x_6=X-newTotal to runs\n        newTotal = 8 * minRun + 9;\n      }\n      runs.add(0, X - newTotal);\n    }\n    runs.add(0, X);\n  }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/sort/UnsafeExternalRowSorterTest.java",
    "content": "package io.mycat.memory.unsafe.sort;\n\nimport io.mycat.memory.MyCatMemory;\nimport io.mycat.memory.unsafe.array.ByteArrayMethods;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.MemoryManager;\nimport io.mycat.memory.unsafe.row.BufferHolder;\nimport io.mycat.memory.unsafe.row.StructType;\nimport io.mycat.memory.unsafe.row.UnsafeRow;\nimport io.mycat.memory.unsafe.row.UnsafeRowWriter;\nimport io.mycat.memory.unsafe.storage.DataNodeDiskManager;\nimport io.mycat.memory.unsafe.storage.SerializerManager;\nimport io.mycat.memory.unsafe.utils.BytesTools;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\nimport io.mycat.memory.unsafe.utils.sort.PrefixComparator;\nimport io.mycat.memory.unsafe.utils.sort.PrefixComparators;\nimport io.mycat.memory.unsafe.utils.sort.RowPrefixComputer;\nimport io.mycat.memory.unsafe.utils.sort.UnsafeExternalRowSorter;\nimport io.mycat.sqlengine.mpp.ColMeta;\nimport io.mycat.sqlengine.mpp.OrderCol;\nimport io.mycat.util.ExecutorUtil;\nimport io.mycat.util.NameableExecutor;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n\nimport java.io.IOException;\n\nimport java.lang.reflect.Array;\nimport java.util.*;\nimport java.util.concurrent.Future;\n\n/**\n * Created by zagnix on 2016/6/19.\n */\npublic class UnsafeExternalRowSorterTest {\n\n    private static  final  int TEST_SIZE = 100000;\n    public static final Logger LOGGER = LoggerFactory.getLogger(UnsafeExternalRowSorterTest.class);\n\n    /**\n     * 测试类型 LONG，INT，SHORT,Float，Double，String，Binary\n     * 经测试基数排序可以适用上述数据类型，大大提高排序速度\n\n     */\n    @Test\n    public  void  testUnsafeExternalRowSorter() throws NoSuchFieldException, IllegalAccessException, IOException {\n        MyCatMemory myCatMemory = new MyCatMemory();\n        MemoryManager memoryManager = myCatMemory.getResultMergeMemoryManager();\n        DataNodeDiskManager blockManager = myCatMemory.getBlockManager();\n        SerializerManager serializerManager = myCatMemory.getSerializerManager();\n        MycatPropertyConf conf = myCatMemory.getConf();\n        DataNodeMemoryManager dataNodeMemoryManager = new DataNodeMemoryManager(memoryManager,\n                Thread.currentThread().getId());\n        /**\n         * 1.schema ,模拟一个field字段值\n         *\n         */\n        int fieldCount = 3;\n        ColMeta colMeta = null;\n        Map<String, ColMeta> colMetaMap = new HashMap<String, ColMeta>(fieldCount);\n        colMeta = new ColMeta(0, ColMeta.COL_TYPE_STRING);\n        colMetaMap.put(\"id\", colMeta);\n        colMeta = new ColMeta(1, ColMeta.COL_TYPE_STRING);\n        colMetaMap.put(\"name\", colMeta);\n        colMeta = new ColMeta(2, ColMeta.COL_TYPE_STRING);\n        colMetaMap.put(\"age\", colMeta);\n\n\n        OrderCol[] orderCols = new OrderCol[1];\n        OrderCol orderCol = new OrderCol(colMetaMap.get(\"id\"),\n                OrderCol.COL_ORDER_TYPE_ASC);\n        orderCols[0] = orderCol;\n        /**\n         * 2 .PrefixComputer\n         */\n        StructType schema = new StructType(colMetaMap, fieldCount);\n        schema.setOrderCols(orderCols);\n\n        UnsafeExternalRowSorter.PrefixComputer prefixComputer =\n                new RowPrefixComputer(schema);\n\n        /**\n         * 3 .PrefixComparator 默认是ASC，可以选择DESC\n         */\n        final PrefixComparator prefixComparator = PrefixComparators.LONG;\n\n        UnsafeExternalRowSorter sorter =\n                new UnsafeExternalRowSorter(dataNodeMemoryManager,\n                        myCatMemory,\n                        schema,\n                        prefixComparator,\n                        prefixComputer,\n                        conf.getSizeAsBytes(\"mycat.buffer.pageSize\",\"1m\"),\n                        true, /**使用基数排序？true or false*/\n                        true);\n\n        UnsafeRow unsafeRow;\n        BufferHolder bufferHolder;\n        UnsafeRowWriter unsafeRowWriter;\n        String line = \"testUnsafeRow\";\n       // List<Float> floats = new ArrayList<Float>();\n         List<Long> longs = new ArrayList<Long>();\n        final Random rand = new Random(42);\n        for (int i = 0; i < TEST_SIZE; i++) {\n            unsafeRow = new UnsafeRow(3);\n            bufferHolder = new BufferHolder(unsafeRow);\n            unsafeRowWriter = new UnsafeRowWriter(bufferHolder,3);\n            bufferHolder.reset();\n\n            String key = getRandomString(rand.nextInt(300)+100);\n\n            //long v = rand.nextLong();\n           // longs.add(v);\n            unsafeRowWriter.write(0,key.getBytes());\n           // unsafeRowWriter.write(0, BytesTools.toBytes(v));\n            unsafeRowWriter.write(1, line.getBytes());\n            unsafeRowWriter.write(2, (\"35\" + 1).getBytes());\n\n            unsafeRow.setTotalSize(bufferHolder.totalSize());\n            sorter.insertRow(unsafeRow);\n        }\n\n        Iterator<UnsafeRow> iter = sorter.sort();\n/*\n         float [] com = new float[floats.size()];\n        for (int i = 0; i <floats.size() ; i++) {\n                com[i] = floats.get(i);\n        }\n        Arrays.sort(com);\n\n\n        long[] com = new long[longs.size()];\n        for (int i = 0; i < longs.size() ; i++) {\n            com[i] = longs.get(i);\n        }\n\n        Arrays.sort(com);\n        */\n        UnsafeRow row = null;\n        int indexprint = 0;\n        while (iter.hasNext()) {\n            row = iter.next();\n\n           // LOGGER.error(indexprint + \"    \" +  row.getUTF8String(0));\n            //Assert.assertEquals(com[indexprint],\n            //        BytesTools.toLong(row.getBinary(0)));\n           // Double c = Double.parseDouble(String.valueOf(com[indexprint])) ;\n           // Double c1 = Double.parseDouble(String.valueOf(BytesTools.toFloat(row.getBinary(0)))) ;\n          //  Assert.assertEquals(0,c.compareTo(c1));\n\n            indexprint++;\n        }\n        Assert.assertEquals(TEST_SIZE,indexprint);\n    }\n\n    public static String getRandomString(int length) { //length表示生成字符串的长度\n        String base = \"abcdefghijklmnopqrstuvwxyz0123456789\";\n        Random random = new Random();\n        StringBuffer sb = new StringBuffer();\n        for (int i = 0; i < length; i++) {\n            int number = random.nextInt(base.length());\n            sb.append(base.charAt(number));\n        }\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/sort/UnsafeExternalSorterRadixSortSuite.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.sort;\n\npublic class UnsafeExternalSorterRadixSortSuite extends UnsafeExternalSorterSuite {\n  @Override\n  protected boolean shouldUseRadixSort() { return true; }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/sort/UnsafeExternalSorterSuite.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.sort;\n\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.memory.TestMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.storage.DataNodeDiskManager;\nimport io.mycat.memory.unsafe.storage.DataNodeFileManager;\nimport io.mycat.memory.unsafe.storage.DiskRowWriter;\nimport io.mycat.memory.unsafe.storage.SerializerManager;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\nimport io.mycat.memory.unsafe.utils.sort.*;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.LinkedList;\n\nimport static org.hamcrest.Matchers.greaterThan;\nimport static org.junit.Assert.*;\n\n\npublic class UnsafeExternalSorterSuite {\n  private static final Logger logger = LoggerFactory.getLogger(UnsafeExternalSorterSuite.class);\n\n  static final TestMemoryManager memoryManager =\n    new TestMemoryManager(new MycatPropertyConf().\n            set(\"mycat.memory.offHeap.enabled\", \"false\"));\n  static  final DataNodeMemoryManager DATA_NODE_MEMORY_MANAGER = new DataNodeMemoryManager(memoryManager, 0);\n  static final SerializerManager serializerManager = new SerializerManager();\n  static  final MycatPropertyConf conf = new MycatPropertyConf();\n\n  static final DataNodeDiskManager blockManager = new DataNodeDiskManager(conf,true,serializerManager);\n  static DataNodeFileManager diskBlockManager ;\n\n  // Use integer comparison for comparing prefixes (which are partition ids, in this case)\n  final PrefixComparator prefixComparator = PrefixComparators.LONG;\n  // Since the key fits within the 8-byte prefix, we don't need to do any record comparison, so\n  // use a dummy comparator\n  final RecordComparator recordComparator = new RecordComparator() {\n    @Override\n    public int compare(\n      Object leftBaseObject,\n      long leftBaseOffset,\n      Object rightBaseObject,\n      long rightBaseOffset) {\n      return 0;\n    }\n  };\n  static File tempDir;\n\n  static {\n    try {\n          diskBlockManager = blockManager.diskBlockManager();\n    } catch (IOException e) {\n\n      logger.error(e.getMessage());\n    }\n  }\n\n  protected boolean shouldUseRadixSort() { return false; }\n\n  private final long pageSizeBytes = new MycatPropertyConf().\n          getSizeAsBytes(\"mycat.buffer.pageSize\",\"4m\");\n\n  @Before\n  public void setUp() {\n\n  }\n\n  @After\n  public void tearDown() throws IOException {\n    try {\n      assertEquals(0L, DATA_NODE_MEMORY_MANAGER.cleanUpAllAllocatedMemory());\n    } finally {\n      //Utils.deleteRecursively(tempDir);\n     // tempDir = null;\n    }\n  }\n\n  private void assertSpillFilesWereCleanedUp() {\n    return;\n  }\n\n  private static void insertNumber(UnsafeExternalSorter sorter, int value) throws Exception {\n\n    final int[] arr = new int[]{ value };\n\n    sorter.insertRecord(arr, Platform.INT_ARRAY_OFFSET, 4, value);\n  }\n\n  private static void insertRecord(\n      UnsafeExternalSorter sorter,\n      int[] record,\n      long prefix) throws IOException {\n\n    sorter.insertRecord(record, Platform.INT_ARRAY_OFFSET, record.length * 4, prefix);\n  }\n\n  private UnsafeExternalSorter newSorter() throws IOException {\n    return UnsafeExternalSorter.create(\n            DATA_NODE_MEMORY_MANAGER,\n      blockManager,\n      serializerManager,\n      recordComparator,\n      prefixComparator,\n      /* initialSize */ 1024,\n      pageSizeBytes,\n      shouldUseRadixSort(),true);\n  }\n\n  @Test\n  public void testSortingOnlyByPrefix() throws Exception {\n    final UnsafeExternalSorter sorter = newSorter();\n    insertNumber(sorter, 5);\n    insertNumber(sorter, 1);\n    insertNumber(sorter, 3);\n    sorter.spill();\n    insertNumber(sorter, 4);\n    sorter.spill();\n    insertNumber(sorter, 2);\n\n    UnsafeSorterIterator iter = sorter.getSortedIterator();\n\n    for (int i = 1; i <= 5; i++) {\n      iter.loadNext();\n      assertEquals(i, iter.getKeyPrefix());\n      assertEquals(4, iter.getRecordLength());\n      assertEquals(i, Platform.getInt(iter.getBaseObject(), iter.getBaseOffset()));\n    }\n\n    sorter.cleanupResources();\n    assertSpillFilesWereCleanedUp();\n  }\n\n  @Test\n  public void testSortingEmptyArrays() throws Exception {\n    final UnsafeExternalSorter sorter = newSorter();\n    sorter.insertRecord(null, 0, 0, 0);\n    sorter.insertRecord(null, 0, 0, 0);\n    sorter.spill();\n    sorter.insertRecord(null, 0, 0, 0);\n    sorter.spill();\n    sorter.insertRecord(null, 0, 0, 0);\n    sorter.insertRecord(null, 0, 0, 0);\n\n    UnsafeSorterIterator iter = sorter.getSortedIterator();\n\n    for (int i = 1; i <= 5; i++) {\n      iter.loadNext();\n      assertEquals(0, iter.getKeyPrefix());\n      assertEquals(0, iter.getRecordLength());\n    }\n\n    sorter.cleanupResources();\n    assertSpillFilesWereCleanedUp();\n  }\n\n  @Test\n  public void testSortTimeMetric() throws Exception {\n    final UnsafeExternalSorter sorter = newSorter();\n    long prevSortTime = sorter.getSortTimeNanos();\n    assertEquals(prevSortTime, 0);\n\n    sorter.insertRecord(null, 0, 0, 0);\n    sorter.spill();\n    assertThat(sorter.getSortTimeNanos(), greaterThan(prevSortTime));\n    prevSortTime = sorter.getSortTimeNanos();\n\n    sorter.spill();  // no sort needed\n    assertEquals(sorter.getSortTimeNanos(), prevSortTime);\n\n    sorter.insertRecord(null, 0, 0, 0);\n    UnsafeSorterIterator iter = sorter.getSortedIterator();\n    assertThat(sorter.getSortTimeNanos(), greaterThan(prevSortTime));\n  }\n\n  @Test\n  public void spillingOccursInResponseToMemoryPressure() throws Exception {\n    final UnsafeExternalSorter sorter = newSorter();\n    // This should be enough records to completely fill up a data page:\n    final int numRecords = (int) (pageSizeBytes / (4 + 4));\n    for (int i = 0; i < numRecords; i++) {\n      insertNumber(sorter, numRecords - i);\n    }\n    assertEquals(1, sorter.getNumberOfAllocatedPages());\n    memoryManager.markExecutionAsOutOfMemoryOnce();\n    // The insertion of this record should trigger a spill:\n    insertNumber(sorter, 0);\n    // Ensure that spill files were created\n   // assertThat(tempDir.listFiles().length, greaterThanOrEqualTo(1));\n    // Read back the sorted data:\n    UnsafeSorterIterator iter = sorter.getSortedIterator();\n\n    int i = 0;\n    while (iter.hasNext()) {\n      iter.loadNext();\n      assertEquals(i, iter.getKeyPrefix());\n      assertEquals(4, iter.getRecordLength());\n      assertEquals(i, Platform.getInt(iter.getBaseObject(), iter.getBaseOffset()));\n      i++;\n    }\n    assertEquals(numRecords + 1, i);\n    sorter.cleanupResources();\n    assertSpillFilesWereCleanedUp();\n  }\n\n  @Test\n  public void testFillingPage() throws Exception {\n    final UnsafeExternalSorter sorter = newSorter();\n    byte[] record = new byte[16];\n    while (sorter.getNumberOfAllocatedPages() < 2) {\n      sorter.insertRecord(record, Platform.BYTE_ARRAY_OFFSET, record.length, 0);\n    }\n    sorter.cleanupResources();\n    assertSpillFilesWereCleanedUp();\n  }\n\n  @Test\n  public void sortingRecordsThatExceedPageSize() throws Exception {\n    final UnsafeExternalSorter sorter = newSorter();\n    final int[] largeRecord = new int[(int) pageSizeBytes + 16];\n    Arrays.fill(largeRecord, 456);\n    final int[] smallRecord = new int[100];\n    Arrays.fill(smallRecord, 123);\n\n    insertRecord(sorter, largeRecord, 456);\n    sorter.spill();\n    insertRecord(sorter, smallRecord, 123);\n    sorter.spill();\n    insertRecord(sorter, smallRecord, 123);\n    insertRecord(sorter, largeRecord, 456);\n\n    UnsafeSorterIterator iter = sorter.getSortedIterator();\n    // Small record\n    assertTrue(iter.hasNext());\n    iter.loadNext();\n    assertEquals(123, iter.getKeyPrefix());\n    assertEquals(smallRecord.length * 4, iter.getRecordLength());\n    assertEquals(123, Platform.getInt(iter.getBaseObject(), iter.getBaseOffset()));\n    // Small record\n    assertTrue(iter.hasNext());\n    iter.loadNext();\n    assertEquals(123, iter.getKeyPrefix());\n    assertEquals(smallRecord.length * 4, iter.getRecordLength());\n    assertEquals(123, Platform.getInt(iter.getBaseObject(), iter.getBaseOffset()));\n    // Large record\n    assertTrue(iter.hasNext());\n    iter.loadNext();\n    assertEquals(456, iter.getKeyPrefix());\n    assertEquals(largeRecord.length * 4, iter.getRecordLength());\n    assertEquals(456, Platform.getInt(iter.getBaseObject(), iter.getBaseOffset()));\n    // Large record\n    assertTrue(iter.hasNext());\n    iter.loadNext();\n    assertEquals(456, iter.getKeyPrefix());\n    assertEquals(largeRecord.length * 4, iter.getRecordLength());\n    assertEquals(456, Platform.getInt(iter.getBaseObject(), iter.getBaseOffset()));\n\n    assertFalse(iter.hasNext());\n    //sorter.cleanupResources();\n    assertSpillFilesWereCleanedUp();\n  }\n\n  @Test\n  public void forcedSpillingWithReadIterator() throws Exception {\n    final UnsafeExternalSorter sorter = newSorter();\n    long[] record = new long[100];\n    int recordSize = record.length * 8;\n    int n = (int) pageSizeBytes / recordSize * 3;\n    for (int i = 0; i < n; i++) {\n      record[0] = (long) i;\n      sorter.insertRecord(record, Platform.LONG_ARRAY_OFFSET, recordSize, 0);\n    }\n    assertTrue(sorter.getNumberOfAllocatedPages() >= 2);\n    UnsafeExternalSorter.SpillableIterator iter =\n      (UnsafeExternalSorter.SpillableIterator) sorter.getSortedIterator();\n    int lastv = 0;\n    for (int i = 0; i < n / 3; i++) {\n      iter.hasNext();\n      iter.loadNext();\n      assertTrue(Platform.getLong(iter.getBaseObject(), iter.getBaseOffset()) == i);\n      lastv = i;\n    }\n    assertTrue(iter.spill() > 0);\n    assertEquals(0, iter.spill());\n    assertTrue(Platform.getLong(iter.getBaseObject(), iter.getBaseOffset()) == lastv);\n    for (int i = n / 3; i < n; i++) {\n      iter.hasNext();\n      iter.loadNext();\n      assertEquals(i, Platform.getLong(iter.getBaseObject(), iter.getBaseOffset()));\n    }\n    sorter.cleanupResources();\n    assertSpillFilesWereCleanedUp();\n  }\n\n  @Test\n  public void forcedSpillingWithNotReadIterator() throws Exception {\n    final UnsafeExternalSorter sorter = newSorter();\n    long[] record = new long[100];\n    int recordSize = record.length * 8;\n    int n = (int) pageSizeBytes / recordSize * 3;\n    for (int i = 0; i < n; i++) {\n      record[0] = (long) i;\n      sorter.insertRecord(record, Platform.LONG_ARRAY_OFFSET, recordSize, 0);\n    }\n    assertTrue(sorter.getNumberOfAllocatedPages() >= 2);\n    UnsafeExternalSorter.SpillableIterator iter =\n      (UnsafeExternalSorter.SpillableIterator) sorter.getSortedIterator();\n    assertTrue(iter.spill() > 0);\n    assertEquals(0, iter.spill());\n    for (int i = 0; i < n; i++) {\n      iter.hasNext();\n      iter.loadNext();\n      assertEquals(i, Platform.getLong(iter.getBaseObject(), iter.getBaseOffset()));\n    }\n    sorter.cleanupResources();\n    assertSpillFilesWereCleanedUp();\n  }\n\n  @Test\n  public void forcedSpillingWithoutComparator() throws Exception {\n    final UnsafeExternalSorter sorter = UnsafeExternalSorter.create(\n            DATA_NODE_MEMORY_MANAGER,\n      blockManager,\n      serializerManager,\n      null,\n      null,\n      /* initialSize */ 1024,\n      pageSizeBytes,\n      shouldUseRadixSort(),true);\n    long[] record = new long[100];\n    int recordSize = record.length * 8;\n    int n = (int) pageSizeBytes / recordSize * 3;\n    int batch = n / 4;\n    for (int i = 0; i < n; i++) {\n      record[0] = (long) i;\n      sorter.insertRecord(record, Platform.LONG_ARRAY_OFFSET, recordSize, 0);\n      if (i % batch == batch - 1) {\n        sorter.spill();\n      }\n    }\n    UnsafeSorterIterator iter = sorter.getIterator();\n    for (int i = 0; i < n; i++) {\n      iter.hasNext();\n      iter.loadNext();\n      assertEquals(i, Platform.getLong(iter.getBaseObject(), iter.getBaseOffset()));\n    }\n    sorter.cleanupResources();\n    assertSpillFilesWereCleanedUp();\n  }\n\n  @Test\n  public void testPeakMemoryUsed() throws Exception {\n    final long recordLengthBytes = 8;\n    final long pageSizeBytes = 256;\n    final long numRecordsPerPage = pageSizeBytes / recordLengthBytes;\n    final UnsafeExternalSorter sorter = UnsafeExternalSorter.create(\n            DATA_NODE_MEMORY_MANAGER,\n      blockManager,\n      serializerManager,\n      recordComparator,\n      prefixComparator,\n      1024,\n      pageSizeBytes,\n      shouldUseRadixSort(),true);\n\n    // Peak memory should be monotonically increasing. More specifically, every time\n    // we allocate a new page it should increase by exactly the size of the page.\n    long previousPeakMemory = sorter.getPeakMemoryUsedBytes();\n    long newPeakMemory;\n    try {\n      for (int i = 0; i < numRecordsPerPage * 10; i++) {\n        insertNumber(sorter, i);\n        newPeakMemory = sorter.getPeakMemoryUsedBytes();\n        if (i % numRecordsPerPage == 0) {\n          // We allocated a new page for this record, so peak memory should change\n          assertEquals(previousPeakMemory + pageSizeBytes, newPeakMemory);\n        } else {\n          assertEquals(previousPeakMemory, newPeakMemory);\n        }\n        previousPeakMemory = newPeakMemory;\n      }\n\n      // Spilling should not change peak memory\n      sorter.spill();\n      newPeakMemory = sorter.getPeakMemoryUsedBytes();\n      assertEquals(previousPeakMemory, newPeakMemory);\n      for (int i = 0; i < numRecordsPerPage; i++) {\n        insertNumber(sorter, i);\n      }\n      newPeakMemory = sorter.getPeakMemoryUsedBytes();\n      assertEquals(previousPeakMemory, newPeakMemory);\n    } finally {\n      sorter.cleanupResources();\n      assertSpillFilesWereCleanedUp();\n    }\n  }\n\n}\n\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/sort/UnsafeInMemorySorterRadixSortSuite.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.sort;\n\npublic class UnsafeInMemorySorterRadixSortSuite extends UnsafeInMemorySorterSuite {\n  @Override\n  protected boolean shouldUseRadixSort() { return true; }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/sort/UnsafeInMemorySorterSuite.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.mycat.memory.unsafe.sort;\n\nimport io.mycat.memory.unsafe.Platform;\nimport io.mycat.memory.unsafe.memory.MemoryBlock;\nimport io.mycat.memory.unsafe.memory.TestMemoryConsumer;\nimport io.mycat.memory.unsafe.memory.TestMemoryManager;\nimport io.mycat.memory.unsafe.memory.mm.DataNodeMemoryManager;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\nimport io.mycat.memory.unsafe.utils.sort.*;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\n\nimport static org.hamcrest.Matchers.greaterThanOrEqualTo;\nimport static org.hamcrest.Matchers.isIn;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertThat;\nimport static org.mockito.Mockito.mock;\n\npublic class UnsafeInMemorySorterSuite {\n\n  protected boolean shouldUseRadixSort() { return true; }\n\n  private static String getStringFromDataPage(Object baseObject,long baseOffset,int length) {\n    final byte[] strBytes = new byte[length];\n    Platform.copyMemory(baseObject,baseOffset,strBytes, Platform.BYTE_ARRAY_OFFSET,length);\n    return new String(strBytes,StandardCharsets.UTF_8);\n  }\n\n  @Test\n  public void testSortingEmptyInput() {\n    final DataNodeMemoryManager memoryManager = new DataNodeMemoryManager(\n      new TestMemoryManager(new MycatPropertyConf().set(\"mycat.memory.offHeap.enabled\", \"false\")), 0);\n    final TestMemoryConsumer consumer = new TestMemoryConsumer(memoryManager);\n    final UnsafeInMemorySorter sorter = new UnsafeInMemorySorter(consumer,\n      memoryManager,\n      mock(RecordComparator.class),\n      mock(PrefixComparator.class),\n      100,\n      shouldUseRadixSort(),true);\n    final UnsafeSorterIterator iter = sorter.getSortedIterator();\n    Assert.assertFalse(iter.hasNext());\n  }\n\n  @Test\n  public void testSortingOnlyByIntegerPrefix() throws Exception {\n    final String[] dataToSort = new String[] {\n      \"Boba\",\n      \"Pearls\",\n      \"Tapioca\",\n      \"Taho\",\n      \"Condensed Milk\",\n      \"Jasmine\",\n      \"Milk Tea\",\n      \"Lychee\",\n      \"Mango\"\n    };\n    final DataNodeMemoryManager memoryManager = new DataNodeMemoryManager(\n      new TestMemoryManager(new MycatPropertyConf().set(\"mycat.memory.offHeap.enabled\",\"false\")), 0);\n    final TestMemoryConsumer consumer = new TestMemoryConsumer(memoryManager);\n    final MemoryBlock dataPage = memoryManager.allocatePage(2048, null);\n\n    final Object baseObject = dataPage.getBaseObject();\n\n    // Write the records into the data page:\n    long position = dataPage.getBaseOffset();\n\n    for (String str : dataToSort) {\n      final byte[] strBytes = str.getBytes(StandardCharsets.UTF_8);\n      Platform.putInt(baseObject, position, strBytes.length);\n      position += 4;\n      Platform.copyMemory(strBytes,Platform.BYTE_ARRAY_OFFSET,baseObject, position, strBytes.length);\n      position += strBytes.length;\n    }\n\n    // Since the key fits within the 8-byte prefix, we don't need to do any record comparison, so\n    // use a dummy comparator\n    final RecordComparator recordComparator = new RecordComparator() {\n      @Override\n      public int compare(\n        Object leftBaseObject,\n        long leftBaseOffset,\n        Object rightBaseObject,\n        long rightBaseOffset) {\n        return 0;\n      }\n    };\n    // Compute key prefixes based on the records' partition ids\n\n    final HashPartitioner hashPartitioner = new HashPartitioner(4);\n\n    // Use integer comparison for comparing prefixes (which are partition ids, in this case)\n    final PrefixComparator prefixComparator = PrefixComparators.LONG;\n\n    UnsafeInMemorySorter sorter = new UnsafeInMemorySorter(\n            consumer,memoryManager,recordComparator,\n            prefixComparator, dataToSort.length,\n            shouldUseRadixSort(),true);\n\n    // Given a page of records, insert those records into the sorter one-by-one:\n    position = dataPage.getBaseOffset();\n    System.out.println(\"(0)address = \" + position);\n\n    for (int i = 0; i < dataToSort.length; i++) {\n\n      if (!sorter.hasSpaceForAnotherRecord()) {\n        sorter.expandPointerArray(consumer.allocateLongArray(sorter.getMemoryUsage() / 8 * 2));\n      }\n\n      // position now points to the start of a record (which holds its length).\n      final int recordLength = Platform.getInt(baseObject,position);\n\n      final long address = memoryManager.encodePageNumberAndOffset(dataPage,position);\n\n\n      final String str = getStringFromDataPage(baseObject,position+4,recordLength);\n\n      final int partitionId = hashPartitioner.getPartition(str);\n      System.out.println(\"(\" + partitionId + \",\" + str + \")\");\n\n      sorter.insertRecord(address,partitionId);\n\n      position += 4 + recordLength;\n    }\n\n\n\n    final UnsafeSorterIterator iter = sorter.getSortedIterator();\n\n    int iterLength = 0;\n    long prevPrefix = -1;\n\n    Arrays.sort(dataToSort);\n\n\n\n    while (iter.hasNext()) {\n      iter.loadNext();\n\n      final String str = getStringFromDataPage(iter.getBaseObject(), iter.getBaseOffset(), iter.getRecordLength());\n\n      final long keyPrefix = iter.getKeyPrefix();\n\n      assertThat(str, isIn(Arrays.asList(dataToSort)));\n      assertThat(keyPrefix, greaterThanOrEqualTo(prevPrefix));\n\n      prevPrefix = keyPrefix;\n\n      iterLength++;\n    }\n\n\n\n    assertEquals(dataToSort.length, iterLength);\n  }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/storage/BlockManagerTest.java",
    "content": "package io.mycat.memory.unsafe.storage;\n\nimport com.google.common.io.Closeables;\nimport io.mycat.memory.unsafe.utils.MycatPropertyConf;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.*;\n\n/**\n * Created by zagnix on 2016/6/4.\n */\npublic class BlockManagerTest {\n    private static final Logger logger = LoggerFactory.getLogger(BlockManagerTest.class);\n\n    @Test\n    public  void testNewDiskBlockManager() throws IOException {\n        MycatPropertyConf conf = new MycatPropertyConf();\n        SerializerManager serializerManager = new SerializerManager();\n        DataNodeDiskManager blockManager = new DataNodeDiskManager(conf,true,serializerManager);\n        DataNodeFileManager diskBlockManager = blockManager.diskBlockManager();\n        /**\n         * 生成一个文本文件\n         */\n        File file = diskBlockManager.getFile(\"mycat1\");\n        FileOutputStream fos = new FileOutputStream(file);\n        BufferedOutputStream bos = new  BufferedOutputStream(fos);\n\n        bos.write(\"KOKKKKKK\".getBytes());\n        bos.flush();\n        bos.close();\n        fos.close();\n\n\n        /**\n         * 读刚刚写入的文件\n         */\n        File file1 = diskBlockManager.getFile(\"mycat1\");\n        FileInputStream ios = new  FileInputStream(file1);\n\n        BufferedInputStream bin = new BufferedInputStream(ios);\n        byte[] str = new byte[\"KOKKKKKK\".getBytes().length];\n        int size =  bin.read(str);\n        bin.close();\n        ios.close();\n\n        Assert.assertEquals(\"KOKKKKKK\",new String(str));\n\n\n\n        File file2 = diskBlockManager.getFile(\"mycat1\");\n\n        DiskRowWriter writer = blockManager.\n                getDiskWriter(null,file2,DummySerializerInstance.INSTANCE,1024*1024);\n        byte [] writeBuffer = new byte[4];\n        int v =4;\n        writeBuffer[0] = (byte)(v >>> 24);\n        writeBuffer[1] = (byte)(v >>> 16);\n        writeBuffer[2] = (byte)(v >>>  8);\n        writeBuffer[3] = (byte)(v >>>  0);\n        writer.write(writeBuffer,0,4);\n\n\n        writer.write(\"you are ok? 1111111111111\".getBytes(),0,\"you are ok? 1111111111111\".getBytes().length);\n        writer.write(\"you are ok? 1111111111111\".getBytes(),0,\"you are ok? 1111111111111\".getBytes().length);\n        writer.write(\"you are ok? 1111111111111\".getBytes(),0,\"you are ok? 1111111111111\".getBytes().length);\n        writer.write(\"you are ok? 1111111111111\".getBytes(),0,\"you are ok? 1111111111111\".getBytes().length);\n        writer.write(\"you are ok? 1111111111111\".getBytes(),0,\"you are ok? 1111111111111\".getBytes().length);\n        writer.write(\"you are ok? 1111111111111\".getBytes(),0,\"you are ok? 1111111111111\".getBytes().length);\n        writer.write(\"you are ok? 1111111111111\".getBytes(),0,\"you are ok? 1111111111111\".getBytes().length);\n        writer.write(\"you are ok? 1111111111111\".getBytes(),0,\"you are ok? 1111111111111\".getBytes().length);\n\n        writer.close();\n\n\n        try {\n            Thread.sleep(100);\n        } catch (InterruptedException e) {\n            logger.error(e.getMessage());\n        }\n\n        assert (file2.length() > 0);\n        final BufferedInputStream bs = new BufferedInputStream(new FileInputStream(file2));\n        try {\n            InputStream in = serializerManager.wrapForCompression(null,bs);\n            DataInputStream  din= new DataInputStream(in);\n            int numRecords = din.readInt();\n            Assert.assertEquals(4,numRecords);\n            din.close();\n            in.close();\n            bs.close();\n\n        } catch (IOException e) {\n            Closeables.close(bs, /* swallowIOException = */ true);\n            throw e;\n        }\n\n    }\n\n    @Test\n    public  void testNewDiskBlockWriter(){\n        MycatPropertyConf conf = new MycatPropertyConf();\n        SerializerManager serializerManager = new SerializerManager();\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/storage/SerializerManagerTest.java",
    "content": "package io.mycat.memory.unsafe.storage;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\n/**\n * Created by zagnix on 2016/6/4.\n */\npublic class SerializerManagerTest {\n    @Test\n    public  void testNewSerializerManager() throws IOException {\n        SerializerManager serializerManager = new SerializerManager();\n        final int[] value = new int[1];\n        OutputStream s = serializerManager.wrapForCompression(null, new OutputStream() {\n            @Override\n            public void write(int b) throws IOException {\n                     value[0] = b;\n            }\n        });\n\n        s.write(10);\n        Assert.assertEquals(10,value[0]);\n\n        InputStream in = serializerManager.wrapForCompression(null, new InputStream() {\n            @Override\n            public int read() throws IOException {\n                return 10;\n            }\n        });\n        Assert.assertEquals(10,in.read());\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/types/CalendarIntervalSuite.java",
    "content": "/*\n* Licensed to the Apache Software Foundation (ASF) under one or more\n* contributor license agreements.  See the NOTICE file distributed with\n* this work for additional information regarding copyright ownership.\n* The ASF licenses this file to You under the Apache License, Version 2.0\n* (the \"License\"); you may not use this file except in compliance with\n* the License.  You may obtain a copy of the License at\n*\n*    http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\npackage io.mycat.memory.unsafe.types;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\npublic class CalendarIntervalSuite {\n\n  @Test\n  public void equalsTest() {\n    CalendarInterval i1 = new CalendarInterval(3, 123);\n    CalendarInterval i2 = new CalendarInterval(3, 321);\n    CalendarInterval i3 = new CalendarInterval(1, 123);\n    CalendarInterval i4 = new CalendarInterval(3, 123);\n\n    assertNotSame(i1, i2);\n    assertNotSame(i1, i3);\n    assertNotSame(i2, i3);\n    assertEquals(i1, i4);\n  }\n\n  @Test\n  public void toStringTest() {\n    CalendarInterval i;\n\n    i = new CalendarInterval(34, 0);\n    assertEquals(\"interval 2 years 10 months\", i.toString());\n\n    i = new CalendarInterval(-34, 0);\n    assertEquals(\"interval -2 years -10 months\", i.toString());\n\n    i = new CalendarInterval(0, 3 * CalendarInterval.MICROS_PER_WEEK + 13 * CalendarInterval.MICROS_PER_HOUR + 123);\n    assertEquals(\"interval 3 weeks 13 hours 123 microseconds\", i.toString());\n\n    i = new CalendarInterval(0, -3 * CalendarInterval.MICROS_PER_WEEK - 13 * CalendarInterval.MICROS_PER_HOUR - 123);\n    assertEquals(\"interval -3 weeks -13 hours -123 microseconds\", i.toString());\n\n    i = new CalendarInterval(34, 3 * CalendarInterval.MICROS_PER_WEEK + 13 * CalendarInterval.MICROS_PER_HOUR + 123);\n    assertEquals(\"interval 2 years 10 months 3 weeks 13 hours 123 microseconds\", i.toString());\n  }\n\n  @Test\n  public void fromStringTest() {\n    testSingleUnit(\"year\", 3, 36, 0);\n    testSingleUnit(\"month\", 3, 3, 0);\n    testSingleUnit(\"week\", 3, 0, 3 * CalendarInterval.MICROS_PER_WEEK);\n    testSingleUnit(\"day\", 3, 0, 3 * CalendarInterval.MICROS_PER_DAY);\n    testSingleUnit(\"hour\", 3, 0, 3 * CalendarInterval.MICROS_PER_HOUR);\n    testSingleUnit(\"minute\", 3, 0, 3 *CalendarInterval. MICROS_PER_MINUTE);\n    testSingleUnit(\"second\", 3, 0, 3 * CalendarInterval.MICROS_PER_SECOND);\n    testSingleUnit(\"millisecond\", 3, 0, 3 *CalendarInterval. MICROS_PER_MILLI);\n    testSingleUnit(\"microsecond\", 3, 0, 3);\n\n    String input;\n\n    input = \"interval   -5  years  23   month\";\n    CalendarInterval result = new CalendarInterval(-5 * 12 + 23, 0);\n    assertEquals(CalendarInterval.fromString(input), result);\n\n    input = \"interval   -5  years  23   month   \";\n    assertEquals(CalendarInterval.fromString(input), result);\n\n    input = \"  interval   -5  years  23   month   \";\n    assertEquals(CalendarInterval.fromString(input), result);\n\n    // Error cases\n    input = \"interval   3month 1 hour\";\n    assertNull(CalendarInterval.fromString(input));\n\n    input = \"interval 3 moth 1 hour\";\n    assertNull(CalendarInterval.fromString(input));\n\n    input = \"interval\";\n    assertNull(CalendarInterval.fromString(input));\n\n    input = \"int\";\n    assertNull(CalendarInterval.fromString(input));\n\n    input = \"\";\n    assertNull(CalendarInterval.fromString(input));\n\n    input = null;\n    assertNull(CalendarInterval.fromString(input));\n  }\n\n  @Test\n  public void fromYearMonthStringTest() {\n    String input;\n    CalendarInterval i;\n\n    input = \"99-10\";\n    i = new CalendarInterval(99 * 12 + 10, 0L);\n    assertEquals(CalendarInterval.fromYearMonthString(input), i);\n\n    input = \"-8-10\";\n    i = new CalendarInterval(-8 * 12 - 10, 0L);\n    assertEquals(CalendarInterval.fromYearMonthString(input), i);\n\n    try {\n      input = \"99-15\";\n      CalendarInterval.fromYearMonthString(input);\n      fail(\"Expected to throw an exception for the invalid input\");\n    } catch (IllegalArgumentException e) {\n      assertTrue(e.getMessage().contains(\"month 15 outside range\"));\n    }\n  }\n\n  @Test\n  public void fromDayTimeStringTest() {\n    String input;\n    CalendarInterval i;\n\n    input = \"5 12:40:30.999999999\";\n    i = new CalendarInterval(0, 5 * CalendarInterval.MICROS_PER_DAY + 12 * CalendarInterval.MICROS_PER_HOUR +\n      40 *CalendarInterval. MICROS_PER_MINUTE + 30 *CalendarInterval. MICROS_PER_SECOND + 999999L);\n    assertEquals(CalendarInterval.fromDayTimeString(input), i);\n\n    input = \"10 0:12:0.888\";\n    i = new CalendarInterval(0, 10 * CalendarInterval.MICROS_PER_DAY + 12 * CalendarInterval.MICROS_PER_MINUTE);\n    assertEquals(CalendarInterval.fromDayTimeString(input), i);\n\n    input = \"-3 0:0:0\";\n    i = new CalendarInterval(0, -3 * CalendarInterval.MICROS_PER_DAY);\n    assertEquals(CalendarInterval.fromDayTimeString(input), i);\n\n    try {\n      input = \"5 30:12:20\";\n      CalendarInterval.fromDayTimeString(input);\n      fail(\"Expected to throw an exception for the invalid input\");\n    } catch (IllegalArgumentException e) {\n      assertTrue(e.getMessage().contains(\"hour 30 outside range\"));\n    }\n\n    try {\n      input = \"5 30-12\";\n      CalendarInterval.fromDayTimeString(input);\n      fail(\"Expected to throw an exception for the invalid input\");\n    } catch (IllegalArgumentException e) {\n      assertTrue(e.getMessage().contains(\"not match day-time format\"));\n    }\n  }\n\n  @Test\n  public void fromSingleUnitStringTest() {\n    String input;\n    CalendarInterval i;\n\n    input = \"12\";\n    i = new CalendarInterval(12 * 12, 0L);\n    assertEquals(CalendarInterval.fromSingleUnitString(\"year\", input), i);\n\n    input = \"100\";\n    i = new CalendarInterval(0, 100 * CalendarInterval.MICROS_PER_DAY);\n    assertEquals(CalendarInterval.fromSingleUnitString(\"day\", input), i);\n\n    input = \"1999.38888\";\n    i = new CalendarInterval(0, 1999 *CalendarInterval. MICROS_PER_SECOND + 38);\n    assertEquals(CalendarInterval.fromSingleUnitString(\"second\", input), i);\n\n    try {\n      input = String.valueOf(Integer.MAX_VALUE);\n      CalendarInterval.fromSingleUnitString(\"year\", input);\n      fail(\"Expected to throw an exception for the invalid input\");\n    } catch (IllegalArgumentException e) {\n      assertTrue(e.getMessage().contains(\"outside range\"));\n    }\n\n    try {\n      input = String.valueOf(Long.MAX_VALUE / CalendarInterval.MICROS_PER_HOUR + 1);\n      CalendarInterval.fromSingleUnitString(\"hour\", input);\n      fail(\"Expected to throw an exception for the invalid input\");\n    } catch (IllegalArgumentException e) {\n      assertTrue(e.getMessage().contains(\"outside range\"));\n    }\n  }\n\n  @Test\n  public void addTest() {\n    String input = \"interval 3 month 1 hour\";\n    String input2 = \"interval 2 month 100 hour\";\n\n    CalendarInterval interval = CalendarInterval.fromString(input);\n    CalendarInterval interval2 = CalendarInterval.fromString(input2);\n\n    assertEquals(interval.add(interval2), new CalendarInterval(5, 101 * CalendarInterval.MICROS_PER_HOUR));\n\n    input = \"interval -10 month -81 hour\";\n    input2 = \"interval 75 month 200 hour\";\n\n    interval = CalendarInterval.fromString(input);\n    interval2 = CalendarInterval.fromString(input2);\n\n    assertEquals(interval.add(interval2), new CalendarInterval(65, 119 * CalendarInterval.MICROS_PER_HOUR));\n  }\n\n  @Test\n  public void subtractTest() {\n    String input = \"interval 3 month 1 hour\";\n    String input2 = \"interval 2 month 100 hour\";\n\n    CalendarInterval interval = CalendarInterval.fromString(input);\n    CalendarInterval interval2 = CalendarInterval.fromString(input2);\n\n    assertEquals(interval.subtract(interval2), new CalendarInterval(1, -99 * CalendarInterval.MICROS_PER_HOUR));\n\n    input = \"interval -10 month -81 hour\";\n    input2 = \"interval 75 month 200 hour\";\n\n    interval = CalendarInterval.fromString(input);\n    interval2 = CalendarInterval.fromString(input2);\n\n    assertEquals(interval.subtract(interval2), new CalendarInterval(-85, -281 * CalendarInterval.MICROS_PER_HOUR));\n  }\n\n  private static void testSingleUnit(String unit, int number, int months, long microseconds) {\n    String input1 = \"interval \" + number + \" \" + unit;\n    String input2 = \"interval \" + number + \" \" + unit + \"s\";\n    CalendarInterval result = new CalendarInterval(months, microseconds);\n    assertEquals(CalendarInterval.fromString(input1), result);\n    assertEquals(CalendarInterval.fromString(input2), result);\n  }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/memory/unsafe/types/UTF8StringSuite.java",
    "content": "package io.mycat.memory.unsafe.types;\n\n/*\n* Licensed to the Apache Software Foundation (ASF) under one or more\n* contributor license agreements.  See the NOTICE file distributed with\n* this work for additional information regarding copyright ownership.\n* The ASF licenses this file to You under the Apache License, Version 2.0\n* (the \"License\"); you may not use this file except in compliance with\n* the License.  You may obtain a copy of the License at\n*\n*    http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n\n\nimport com.google.common.collect.ImmutableMap;\nimport org.junit.Test;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.Arrays;\nimport java.util.HashMap;\n\nimport static org.junit.Assert.*;\n\n\npublic class UTF8StringSuite {\n\n  private static void checkBasic(String str, int len) throws UnsupportedEncodingException {\n    UTF8String s1 = UTF8String.fromString(str);\n    UTF8String s2 = UTF8String.fromBytes(str.getBytes(\"utf8\"));\n    assertEquals(s1.numChars(), len);\n    assertEquals(s2.numChars(), len);\n\n    assertEquals(s1.toString(), str);\n    assertEquals(s2.toString(), str);\n    assertEquals(s1, s2);\n\n    assertEquals(s1.hashCode(), s2.hashCode());\n\n    assertEquals(0, s1.compareTo(s2));\n\n    assertTrue(s1.contains(s2));\n    assertTrue(s2.contains(s1));\n    assertTrue(s1.startsWith(s1));\n    assertTrue(s1.endsWith(s1));\n  }\n\n  @Test\n  public void basicTest() throws UnsupportedEncodingException {\n    checkBasic(\"\", 0);\n    checkBasic(\"hello\", 5);\n    checkBasic(\"大 千 世 界\", 7);\n  }\n\n  @Test\n  public void emptyStringTest() {\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.fromString(\"\"));\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.fromBytes(new byte[0]));\n    assertEquals(0, UTF8String.EMPTY_UTF8.numChars());\n    assertEquals(0, UTF8String.EMPTY_UTF8.numBytes());\n  }\n\n  @Test\n  public void prefix() {\n    assertTrue(UTF8String.fromString(\"a\").getPrefix() - UTF8String.fromString(\"b\").getPrefix() < 0);\n    assertTrue(UTF8String.fromString(\"ab\").getPrefix() - UTF8String.fromString(\"b\").getPrefix() < 0);\n    assertTrue(\n      UTF8String.fromString(\"abbbbbbbbbbbasdf\").getPrefix() - UTF8String.fromString(\"bbbbbbbbbbbbasdf\").getPrefix() < 0);\n    assertTrue(UTF8String.fromString(\"\").getPrefix() - UTF8String.fromString(\"a\").getPrefix() < 0);\n    assertTrue(UTF8String.fromString(\"你好\").getPrefix() - UTF8String.fromString(\"世界\").getPrefix() > 0);\n\n    byte[] buf1 = {1, 2, 3, 4, 5, 6, 7, 8, 9};\n    byte[] buf2 = {1, 2, 3};\n    UTF8String str1 = UTF8String.fromBytes(buf1, 0, 3);\n    UTF8String str2 = UTF8String.fromBytes(buf1, 0, 8);\n    UTF8String str3 = UTF8String.fromBytes(buf2);\n    assertTrue(str1.getPrefix() - str2.getPrefix() < 0);\n    assertEquals(str1.getPrefix(), str3.getPrefix());\n  }\n\n  @Test\n  public void compareTo() {\n    assertTrue(UTF8String.fromString(\"\").compareTo(UTF8String.fromString(\"a\")) < 0);\n    assertTrue(UTF8String.fromString(\"abc\").compareTo(UTF8String.fromString(\"ABC\")) > 0);\n    assertTrue(UTF8String.fromString(\"abc0\").compareTo(UTF8String.fromString(\"abc\")) > 0);\n    assertTrue(UTF8String.fromString(\"abcabcabc\").compareTo(UTF8String.fromString(\"abcabcabc\")) == 0);\n    assertTrue(UTF8String.fromString(\"aBcabcabc\").compareTo(UTF8String.fromString(\"Abcabcabc\")) > 0);\n    assertTrue(UTF8String.fromString(\"Abcabcabc\").compareTo(UTF8String.fromString(\"abcabcabC\")) < 0);\n    assertTrue(UTF8String.fromString(\"abcabcabc\").compareTo(UTF8String.fromString(\"abcabcabC\")) > 0);\n\n    assertTrue(UTF8String.fromString(\"abc\").compareTo(UTF8String.fromString(\"世界\")) < 0);\n    assertTrue(UTF8String.fromString(\"你好\").compareTo(UTF8String.fromString(\"世界\")) > 0);\n    assertTrue(UTF8String.fromString(\"你好123\").compareTo(UTF8String.fromString(\"你好122\")) > 0);\n  }\n\n  protected static void testUpperandLower(String upper, String lower) {\n    UTF8String us = UTF8String.fromString(upper);\n    UTF8String ls = UTF8String.fromString(lower);\n    assertEquals(ls, us.toLowerCase());\n    assertEquals(us, ls.toUpperCase());\n    assertEquals(us, us.toUpperCase());\n    assertEquals(ls, ls.toLowerCase());\n  }\n\n  @Test\n  public void upperAndLower() {\n    testUpperandLower(\"\", \"\");\n    testUpperandLower(\"0123456\", \"0123456\");\n    testUpperandLower(\"ABCXYZ\", \"abcxyz\");\n    testUpperandLower(\"ЀЁЂѺΏỀ\", \"ѐёђѻώề\");\n    testUpperandLower(\"大千世界 数据砖头\", \"大千世界 数据砖头\");\n  }\n\n  @Test\n  public void titleCase() {\n    assertEquals(UTF8String.fromString(\"\"), UTF8String.fromString(\"\").toTitleCase());\n    assertEquals(UTF8String.fromString(\"Ab Bc Cd\"), UTF8String.fromString(\"ab bc cd\").toTitleCase());\n    assertEquals(UTF8String.fromString(\"Ѐ Ё Ђ Ѻ Ώ Ề\"), UTF8String.fromString(\"ѐ ё ђ ѻ ώ ề\").toTitleCase());\n    assertEquals(UTF8String.fromString(\"大千世界 数据砖头\"), UTF8String.fromString(\"大千世界 数据砖头\").toTitleCase());\n  }\n\n  @Test\n  public void concatTest() {\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.concat());\n    assertNull(UTF8String.concat((UTF8String) null));\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.concat(UTF8String.EMPTY_UTF8));\n    assertEquals(UTF8String.fromString(\"ab\"), UTF8String.concat(UTF8String.fromString(\"ab\")));\n    assertEquals(UTF8String.fromString(\"ab\"), UTF8String.concat(UTF8String.fromString(\"a\"), UTF8String.fromString(\"b\")));\n    assertEquals(UTF8String.fromString(\"abc\"), UTF8String.concat(UTF8String.fromString(\"a\"), UTF8String.fromString(\"b\"), UTF8String.fromString(\"c\")));\n    assertNull(UTF8String.concat(UTF8String.fromString(\"a\"), null, UTF8String.fromString(\"c\")));\n    assertNull(UTF8String.concat(UTF8String.fromString(\"a\"), null, null));\n    assertNull(UTF8String.concat(null, null, null));\n    assertEquals(UTF8String.fromString(\"数据砖头\"), UTF8String.concat(UTF8String.fromString(\"数据\"), UTF8String.fromString(\"砖头\")));\n  }\n\n  @Test\n  public void concatWsTest() {\n    // Returns null if the separator is null\n    assertNull(UTF8String.concatWs(null, (UTF8String) null));\n    assertNull(UTF8String.concatWs(null, UTF8String.fromString(\"a\")));\n\n    // If separator is null, concatWs should skip all null inputs and never return null.\n    UTF8String sep = UTF8String.fromString(\"哈哈\");\n    assertEquals(\n      UTF8String.EMPTY_UTF8,\n      UTF8String.concatWs(sep, UTF8String.EMPTY_UTF8));\n    assertEquals(\n      UTF8String.fromString(\"ab\"),\n      UTF8String.concatWs(sep, UTF8String.fromString(\"ab\")));\n    assertEquals(\n      UTF8String.fromString(\"a哈哈b\"),\n      UTF8String.concatWs(sep, UTF8String.fromString(\"a\"), UTF8String.fromString(\"b\")));\n    assertEquals(\n      UTF8String.fromString(\"a哈哈b哈哈c\"),\n      UTF8String.concatWs(sep, UTF8String.fromString(\"a\"), UTF8String.fromString(\"b\"), UTF8String.fromString(\"c\")));\n    assertEquals(\n      UTF8String.fromString(\"a哈哈c\"),\n      UTF8String.concatWs(sep, UTF8String.fromString(\"a\"), null, UTF8String.fromString(\"c\")));\n    assertEquals(\n      UTF8String.fromString(\"a\"),\n      UTF8String.concatWs(sep, UTF8String.fromString(\"a\"), null, null));\n    assertEquals(\n      UTF8String.EMPTY_UTF8,\n      UTF8String.concatWs(sep, null, null, null));\n    assertEquals(\n      UTF8String.fromString(\"数据哈哈砖头\"),\n      UTF8String.concatWs(sep, UTF8String.fromString(\"数据\"), UTF8String.fromString(\"砖头\")));\n  }\n\n  @Test\n  public void contains() {\n    assertTrue(UTF8String.EMPTY_UTF8.contains(UTF8String.EMPTY_UTF8));\n    assertTrue(UTF8String.fromString(\"hello\").contains(UTF8String.fromString(\"ello\")));\n    assertFalse(UTF8String.fromString(\"hello\").contains(UTF8String.fromString(\"vello\")));\n    assertFalse(UTF8String.fromString(\"hello\").contains(UTF8String.fromString(\"hellooo\")));\n    assertTrue(UTF8String.fromString(\"大千世界\").contains(UTF8String.fromString(\"千世界\")));\n    assertFalse(UTF8String.fromString(\"大千世界\").contains(UTF8String.fromString(\"世千\")));\n    assertFalse(UTF8String.fromString(\"大千世界\").contains(UTF8String.fromString(\"大千世界好\")));\n  }\n\n  @Test\n  public void startsWith() {\n    assertTrue(UTF8String.EMPTY_UTF8.startsWith(UTF8String.EMPTY_UTF8));\n    assertTrue(UTF8String.fromString(\"hello\").startsWith(UTF8String.fromString(\"hell\")));\n    assertFalse(UTF8String.fromString(\"hello\").startsWith(UTF8String.fromString(\"ell\")));\n    assertFalse(UTF8String.fromString(\"hello\").startsWith(UTF8String.fromString(\"hellooo\")));\n    assertTrue(UTF8String.fromString(\"数据砖头\").startsWith(UTF8String.fromString(\"数据\")));\n    assertFalse(UTF8String.fromString(\"大千世界\").startsWith(UTF8String.fromString(\"千\")));\n    assertFalse(UTF8String.fromString(\"大千世界\").startsWith(UTF8String.fromString(\"大千世界好\")));\n  }\n\n  @Test\n  public void endsWith() {\n    assertTrue(UTF8String.EMPTY_UTF8.endsWith(UTF8String.EMPTY_UTF8));\n    assertTrue(UTF8String.fromString(\"hello\").endsWith(UTF8String.fromString(\"ello\")));\n    assertFalse(UTF8String.fromString(\"hello\").endsWith(UTF8String.fromString(\"ellov\")));\n    assertFalse(UTF8String.fromString(\"hello\").endsWith(UTF8String.fromString(\"hhhello\")));\n    assertTrue(UTF8String.fromString(\"大千世界\").endsWith(UTF8String.fromString(\"世界\")));\n    assertFalse(UTF8String.fromString(\"大千世界\").endsWith(UTF8String.fromString(\"世\")));\n    assertFalse(UTF8String.fromString(\"数据砖头\").endsWith(UTF8String.fromString(\"我的数据砖头\")));\n  }\n\n  @Test\n  public void substring() {\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.fromString(\"hello\").substring(0, 0));\n    assertEquals(UTF8String.fromString(\"el\"), UTF8String.fromString(\"hello\").substring(1, 3));\n    assertEquals(UTF8String.fromString(\"数\"), UTF8String.fromString(\"数据砖头\").substring(0, 1));\n    assertEquals(UTF8String.fromString(\"据砖\"), UTF8String.fromString(\"数据砖头\").substring(1, 3));\n    assertEquals(UTF8String.fromString(\"头\"), UTF8String.fromString(\"数据砖头\").substring(3, 5));\n    assertEquals(UTF8String.fromString(\"ߵ梷\"), UTF8String.fromString(\"ߵ梷\").substring(0, 2));\n  }\n\n  @Test\n  public void trims() {\n    assertEquals(UTF8String.fromString(\"hello\"), UTF8String.fromString(\"  hello \").trim());\n    assertEquals(UTF8String.fromString(\"hello \"), UTF8String.fromString(\"  hello \").trimLeft());\n    assertEquals(UTF8String.fromString(\"  hello\"), UTF8String.fromString(\"  hello \").trimRight());\n\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.fromString(\"  \").trim());\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.fromString(\"  \").trimLeft());\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.fromString(\"  \").trimRight());\n\n    assertEquals(UTF8String.fromString(\"数据砖头\"), UTF8String.fromString(\"  数据砖头 \").trim());\n    assertEquals(UTF8String.fromString(\"数据砖头 \"), UTF8String.fromString(\"  数据砖头 \").trimLeft());\n    assertEquals(UTF8String.fromString(\"  数据砖头\"), UTF8String.fromString(\"  数据砖头 \").trimRight());\n\n    assertEquals(UTF8String.fromString(\"数据砖头\"), UTF8String.fromString(\"数据砖头\").trim());\n    assertEquals(UTF8String.fromString(\"数据砖头\"), UTF8String.fromString(\"数据砖头\").trimLeft());\n    assertEquals(UTF8String.fromString(\"数据砖头\"), UTF8String.fromString(\"数据砖头\").trimRight());\n  }\n\n  @Test\n  public void indexOf() {\n    assertEquals(0, UTF8String.EMPTY_UTF8.indexOf(UTF8String.EMPTY_UTF8, 0));\n    assertEquals(-1, UTF8String.EMPTY_UTF8.indexOf(UTF8String.fromString(\"l\"), 0));\n    assertEquals(0, UTF8String.fromString(\"hello\").indexOf(UTF8String.EMPTY_UTF8, 0));\n    assertEquals(2, UTF8String.fromString(\"hello\").indexOf(UTF8String.fromString(\"l\"), 0));\n    assertEquals(3, UTF8String.fromString(\"hello\").indexOf(UTF8String.fromString(\"l\"), 3));\n    assertEquals(-1, UTF8String.fromString(\"hello\").indexOf(UTF8String.fromString(\"a\"), 0));\n    assertEquals(2, UTF8String.fromString(\"hello\").indexOf(UTF8String.fromString(\"ll\"), 0));\n    assertEquals(-1, UTF8String.fromString(\"hello\").indexOf(UTF8String.fromString(\"ll\"), 4));\n    assertEquals(1, UTF8String.fromString(\"数据砖头\").indexOf(UTF8String.fromString(\"据砖\"), 0));\n    assertEquals(-1, UTF8String.fromString(\"数据砖头\").indexOf(UTF8String.fromString(\"数\"), 3));\n    assertEquals(0, UTF8String.fromString(\"数据砖头\").indexOf(UTF8String.fromString(\"数\"), 0));\n    assertEquals(3, UTF8String.fromString(\"数据砖头\").indexOf(UTF8String.fromString(\"头\"), 0));\n  }\n\n  @Test\n  public void substring_index() {\n    assertEquals(UTF8String.fromString(\"www.apache.org\"),\n      UTF8String.fromString(\"www.apache.org\").subStringIndex(UTF8String.fromString(\".\"), 3));\n    assertEquals(UTF8String.fromString(\"www.apache\"),\n      UTF8String.fromString(\"www.apache.org\").subStringIndex(UTF8String.fromString(\".\"), 2));\n    assertEquals(UTF8String.fromString(\"www\"),\n      UTF8String.fromString(\"www.apache.org\").subStringIndex(UTF8String.fromString(\".\"), 1));\n    assertEquals(UTF8String.fromString(\"\"),\n      UTF8String.fromString(\"www.apache.org\").subStringIndex(UTF8String.fromString(\".\"), 0));\n    assertEquals(UTF8String.fromString(\"org\"),\n      UTF8String.fromString(\"www.apache.org\").subStringIndex(UTF8String.fromString(\".\"), -1));\n    assertEquals(UTF8String.fromString(\"apache.org\"),\n      UTF8String.fromString(\"www.apache.org\").subStringIndex(UTF8String.fromString(\".\"), -2));\n    assertEquals(UTF8String.fromString(\"www.apache.org\"),\n      UTF8String.fromString(\"www.apache.org\").subStringIndex(UTF8String.fromString(\".\"), -3));\n    // str is empty string\n    assertEquals(UTF8String.fromString(\"\"),\n      UTF8String.fromString(\"\").subStringIndex(UTF8String.fromString(\".\"), 1));\n    // empty string delim\n    assertEquals(UTF8String.fromString(\"\"),\n      UTF8String.fromString(\"www.apache.org\").subStringIndex(UTF8String.fromString(\"\"), 1));\n    // delim does not exist in str\n    assertEquals(UTF8String.fromString(\"www.apache.org\"),\n      UTF8String.fromString(\"www.apache.org\").subStringIndex(UTF8String.fromString(\"#\"), 2));\n    // delim is 2 chars\n    assertEquals(UTF8String.fromString(\"www||apache\"),\n      UTF8String.fromString(\"www||apache||org\").subStringIndex(UTF8String.fromString(\"||\"), 2));\n    assertEquals(UTF8String.fromString(\"apache||org\"),\n      UTF8String.fromString(\"www||apache||org\").subStringIndex(UTF8String.fromString(\"||\"), -2));\n    // non ascii chars\n    assertEquals(UTF8String.fromString(\"大千世界大\"),\n      UTF8String.fromString(\"大千世界大千世界\").subStringIndex(UTF8String.fromString(\"千\"), 2));\n    // overlapped delim\n    assertEquals(UTF8String.fromString(\"||\"), UTF8String.fromString(\"||||||\").subStringIndex(UTF8String.fromString(\"|||\"), 3));\n    assertEquals(UTF8String.fromString(\"|||\"), UTF8String.fromString(\"||||||\").subStringIndex(UTF8String.fromString(\"|||\"), -4));\n  }\n\n  @Test\n  public void reverse() {\n    assertEquals(UTF8String.fromString(\"olleh\"), UTF8String.fromString(\"hello\").reverse());\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.EMPTY_UTF8.reverse());\n    assertEquals(UTF8String.fromString(\"者行孙\"), UTF8String.fromString(\"孙行者\").reverse());\n    assertEquals(UTF8String.fromString(\"者行孙 olleh\"), UTF8String.fromString(\"hello 孙行者\").reverse());\n  }\n\n  @Test\n  public void repeat() {\n    assertEquals(UTF8String.fromString(\"数d数d数d数d数d\"), UTF8String.fromString(\"数d\").repeat(5));\n    assertEquals(UTF8String.fromString(\"数d\"), UTF8String.fromString(\"数d\").repeat(1));\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.fromString(\"数d\").repeat(-1));\n  }\n\n  @Test\n  public void pad() {\n    assertEquals(UTF8String.fromString(\"hel\"), UTF8String.fromString(\"hello\").lpad(3, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"hello\"), UTF8String.fromString(\"hello\").lpad(5, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"?hello\"), UTF8String.fromString(\"hello\").lpad(6, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"???????hello\"), UTF8String.fromString(\"hello\").lpad(12, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"?????hello\"), UTF8String.fromString(\"hello\").lpad(10, UTF8String.fromString(\"?????\")));\n    assertEquals(UTF8String.fromString(\"???????\"), UTF8String.EMPTY_UTF8.lpad(7, UTF8String.fromString(\"?????\")));\n\n    assertEquals(UTF8String.fromString(\"hel\"), UTF8String.fromString(\"hello\").rpad(3, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"hello\"), UTF8String.fromString(\"hello\").rpad(5, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"hello?\"), UTF8String.fromString(\"hello\").rpad(6, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"hello???????\"), UTF8String.fromString(\"hello\").rpad(12, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"hello?????\"), UTF8String.fromString(\"hello\").rpad(10, UTF8String.fromString(\"?????\")));\n    assertEquals(UTF8String.fromString(\"???????\"), UTF8String.EMPTY_UTF8.rpad(7, UTF8String.fromString(\"?????\")));\n\n    assertEquals(UTF8String.fromString(\"数据砖\"), UTF8String.fromString(\"数据砖头\").lpad(3, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"?数据砖头\"), UTF8String.fromString(\"数据砖头\").lpad(5, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"??数据砖头\"), UTF8String.fromString(\"数据砖头\").lpad(6, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"孙行数据砖头\"), UTF8String.fromString(\"数据砖头\").lpad(6, UTF8String.fromString(\"孙行者\")));\n    assertEquals(UTF8String.fromString(\"孙行者数据砖头\"), UTF8String.fromString(\"数据砖头\").lpad(7, UTF8String.fromString(\"孙行者\")));\n    assertEquals(\n      UTF8String.fromString(\"孙行者孙行者孙行数据砖头\"),\n      UTF8String.fromString(\"数据砖头\").lpad(12, UTF8String.fromString(\"孙行者\")));\n\n    assertEquals(UTF8String.fromString(\"数据砖\"), UTF8String.fromString(\"数据砖头\").rpad(3, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"数据砖头?\"), UTF8String.fromString(\"数据砖头\").rpad(5, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"数据砖头??\"), UTF8String.fromString(\"数据砖头\").rpad(6, UTF8String.fromString(\"????\")));\n    assertEquals(UTF8String.fromString(\"数据砖头孙行\"), UTF8String.fromString(\"数据砖头\").rpad(6, UTF8String.fromString(\"孙行者\")));\n    assertEquals(UTF8String.fromString(\"数据砖头孙行者\"), UTF8String.fromString(\"数据砖头\").rpad(7, UTF8String.fromString(\"孙行者\")));\n    assertEquals(\n      UTF8String.fromString(\"数据砖头孙行者孙行者孙行\"),\n      UTF8String.fromString(\"数据砖头\").rpad(12, UTF8String.fromString(\"孙行者\")));\n\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.fromString(\"数据砖头\").lpad(-10, UTF8String.fromString(\"孙行者\")));\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.fromString(\"数据砖头\").lpad(-10, UTF8String.EMPTY_UTF8));\n    assertEquals(UTF8String.fromString(\"数据砖头\"), UTF8String.fromString(\"数据砖头\").lpad(5, UTF8String.EMPTY_UTF8));\n    assertEquals(UTF8String.fromString(\"数据砖\"), UTF8String.fromString(\"数据砖头\").lpad(3, UTF8String.EMPTY_UTF8));\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.EMPTY_UTF8.lpad(3, UTF8String.EMPTY_UTF8));\n\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.fromString(\"数据砖头\").rpad(-10, UTF8String.fromString(\"孙行者\")));\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.fromString(\"数据砖头\").rpad(-10, UTF8String.EMPTY_UTF8));\n    assertEquals(UTF8String.fromString(\"数据砖头\"), UTF8String.fromString(\"数据砖头\").rpad(5, UTF8String.EMPTY_UTF8));\n    assertEquals(UTF8String.fromString(\"数据砖\"), UTF8String.fromString(\"数据砖头\").rpad(3, UTF8String.EMPTY_UTF8));\n    assertEquals(UTF8String.EMPTY_UTF8, UTF8String.EMPTY_UTF8.rpad(3, UTF8String.EMPTY_UTF8));\n  }\n\n  @Test\n  public void substringSQL() {\n    UTF8String e = UTF8String.fromString(\"example\");\n    assertEquals(e.substringSQL(0, 2), UTF8String.fromString(\"ex\"));\n    assertEquals(e.substringSQL(1, 2), UTF8String.fromString(\"ex\"));\n    assertEquals(e.substringSQL(0, 7), UTF8String.fromString(\"example\"));\n    assertEquals(e.substringSQL(1, 2), UTF8String.fromString(\"ex\"));\n    assertEquals(e.substringSQL(0, 100), UTF8String.fromString(\"example\"));\n    assertEquals(e.substringSQL(1, 100), UTF8String.fromString(\"example\"));\n    assertEquals(e.substringSQL(2, 2), UTF8String.fromString(\"xa\"));\n    assertEquals(e.substringSQL(1, 6), UTF8String.fromString(\"exampl\"));\n    assertEquals(e.substringSQL(2, 100), UTF8String.fromString(\"xample\"));\n    assertEquals(e.substringSQL(0, 0), UTF8String.fromString(\"\"));\n    assertEquals(e.substringSQL(100, 4), UTF8String.EMPTY_UTF8);\n    assertEquals(e.substringSQL(0, Integer.MAX_VALUE), UTF8String.fromString(\"example\"));\n    assertEquals(e.substringSQL(1, Integer.MAX_VALUE), UTF8String.fromString(\"example\"));\n    assertEquals(e.substringSQL(2, Integer.MAX_VALUE), UTF8String.fromString(\"xample\"));\n  }\n\n  @Test\n  public void split() {\n    assertTrue(Arrays.equals(UTF8String.fromString(\"ab,def,ghi\").split(UTF8String.fromString(\",\"), -1),\n      new UTF8String[]{UTF8String.fromString(\"ab\"), UTF8String.fromString(\"def\"), UTF8String.fromString(\"ghi\")}));\n    assertTrue(Arrays.equals(UTF8String.fromString(\"ab,def,ghi\").split(UTF8String.fromString(\",\"), 2),\n      new UTF8String[]{UTF8String.fromString(\"ab\"), UTF8String.fromString(\"def,ghi\")}));\n    assertTrue(Arrays.equals(UTF8String.fromString(\"ab,def,ghi\").split(UTF8String.fromString(\",\"), 2),\n      new UTF8String[]{UTF8String.fromString(\"ab\"), UTF8String.fromString(\"def,ghi\")}));\n  }\n  \n  @Test\n  public void levenshteinDistance() {\n    assertEquals(0, UTF8String.EMPTY_UTF8.levenshteinDistance(UTF8String.EMPTY_UTF8));\n    assertEquals(1, UTF8String.EMPTY_UTF8.levenshteinDistance(UTF8String.fromString(\"a\")));\n    assertEquals(7, UTF8String.fromString(\"aaapppp\").levenshteinDistance(UTF8String.EMPTY_UTF8));\n    assertEquals(1, UTF8String.fromString(\"frog\").levenshteinDistance(UTF8String.fromString(\"fog\")));\n    assertEquals(3, UTF8String.fromString(\"fly\").levenshteinDistance(UTF8String.fromString(\"ant\")));\n    assertEquals(7, UTF8String.fromString(\"elephant\").levenshteinDistance(UTF8String.fromString(\"hippo\")));\n    assertEquals(7, UTF8String.fromString(\"hippo\").levenshteinDistance(UTF8String.fromString(\"elephant\")));\n    assertEquals(8, UTF8String.fromString(\"hippo\").levenshteinDistance(UTF8String.fromString(\"zzzzzzzz\")));\n    assertEquals(1, UTF8String.fromString(\"hello\").levenshteinDistance(UTF8String.fromString(\"hallo\")));\n    assertEquals(4, UTF8String.fromString(\"世界千世\").levenshteinDistance(UTF8String.fromString(\"千a世b\")));\n  }\n\n  @Test\n  public void translate() {\n    assertEquals(\n      UTF8String.fromString(\"1a2s3ae\"),\n      UTF8String.fromString(\"translate\").translate(ImmutableMap.of(\n        'r', '1',\n        'n', '2',\n        'l', '3',\n        't', '\\0'\n      )));\n    assertEquals(\n      UTF8String.fromString(\"translate\"),\n      UTF8String.fromString(\"translate\").translate(new HashMap<Character, Character>()));\n    assertEquals(\n      UTF8String.fromString(\"asae\"),\n      UTF8String.fromString(\"translate\").translate(ImmutableMap.of(\n        'r', '\\0',\n        'n', '\\0',\n        'l', '\\0',\n        't', '\\0'\n      )));\n    assertEquals(\n      UTF8String.fromString(\"aa世b\"),\n      UTF8String.fromString(\"花花世界\").translate(ImmutableMap.of(\n        '花', 'a',\n        '界', 'b'\n      )));\n  }\n\n  @Test\n  public void createBlankString() {\n    assertEquals(UTF8String.fromString(\" \"), UTF8String.blankString(1));\n    assertEquals(UTF8String.fromString(\"  \"), UTF8String.blankString(2));\n    assertEquals(UTF8String.fromString(\"   \"), UTF8String.blankString(3));\n    assertEquals(UTF8String.fromString(\"\"), UTF8String.blankString(0));\n  }\n\n  @Test\n  public void findInSet() {\n    assertEquals(1, UTF8String.fromString(\"ab\").findInSet(UTF8String.fromString(\"ab\")));\n    assertEquals(2, UTF8String.fromString(\"a,b\").findInSet(UTF8String.fromString(\"b\")));\n    assertEquals(3, UTF8String.fromString(\"abc,b,ab,c,def\").findInSet(UTF8String.fromString(\"ab\")));\n    assertEquals(1, UTF8String.fromString(\"ab,abc,b,ab,c,def\").findInSet(UTF8String.fromString(\"ab\")));\n    assertEquals(4, UTF8String.fromString(\",,,ab,abc,b,ab,c,def\").findInSet(UTF8String.fromString(\"ab\")));\n    assertEquals(1, UTF8String.fromString(\",ab,abc,b,ab,c,def\").findInSet(UTF8String.fromString(\"\")));\n    assertEquals(4, UTF8String.fromString(\"数据砖头,abc,b,ab,c,def\").findInSet(UTF8String.fromString(\"ab\")));\n    assertEquals(6, UTF8String.fromString(\"数据砖头,abc,b,ab,c,def\").findInSet(UTF8String.fromString(\"def\")));\n  }\n\n  @Test\n  public void soundex() {\n    assertEquals(UTF8String.fromString(\"Robert\").soundex(), UTF8String.fromString(\"R163\"));\n    assertEquals(UTF8String.fromString(\"Rupert\").soundex(), UTF8String.fromString(\"R163\"));\n    assertEquals(UTF8String.fromString(\"Rubin\").soundex(), UTF8String.fromString(\"R150\"));\n    assertEquals(UTF8String.fromString(\"Ashcraft\").soundex(), UTF8String.fromString(\"A261\"));\n    assertEquals(UTF8String.fromString(\"Ashcroft\").soundex(), UTF8String.fromString(\"A261\"));\n    assertEquals(UTF8String.fromString(\"Burroughs\").soundex(), UTF8String.fromString(\"B620\"));\n    assertEquals(UTF8String.fromString(\"Burrows\").soundex(), UTF8String.fromString(\"B620\"));\n    assertEquals(UTF8String.fromString(\"Ekzampul\").soundex(), UTF8String.fromString(\"E251\"));\n    assertEquals(UTF8String.fromString(\"Example\").soundex(), UTF8String.fromString(\"E251\"));\n    assertEquals(UTF8String.fromString(\"Ellery\").soundex(), UTF8String.fromString(\"E460\"));\n    assertEquals(UTF8String.fromString(\"Euler\").soundex(), UTF8String.fromString(\"E460\"));\n    assertEquals(UTF8String.fromString(\"Ghosh\").soundex(), UTF8String.fromString(\"G200\"));\n    assertEquals(UTF8String.fromString(\"Gauss\").soundex(), UTF8String.fromString(\"G200\"));\n    assertEquals(UTF8String.fromString(\"Gutierrez\").soundex(), UTF8String.fromString(\"G362\"));\n    assertEquals(UTF8String.fromString(\"Heilbronn\").soundex(), UTF8String.fromString(\"H416\"));\n    assertEquals(UTF8String.fromString(\"Hilbert\").soundex(), UTF8String.fromString(\"H416\"));\n    assertEquals(UTF8String.fromString(\"Jackson\").soundex(), UTF8String.fromString(\"J250\"));\n    assertEquals(UTF8String.fromString(\"Kant\").soundex(), UTF8String.fromString(\"K530\"));\n    assertEquals(UTF8String.fromString(\"Knuth\").soundex(), UTF8String.fromString(\"K530\"));\n    assertEquals(UTF8String.fromString(\"Lee\").soundex(), UTF8String.fromString(\"L000\"));\n    assertEquals(UTF8String.fromString(\"Lukasiewicz\").soundex(), UTF8String.fromString(\"L222\"));\n    assertEquals(UTF8String.fromString(\"Lissajous\").soundex(), UTF8String.fromString(\"L222\"));\n    assertEquals(UTF8String.fromString(\"Ladd\").soundex(), UTF8String.fromString(\"L300\"));\n    assertEquals(UTF8String.fromString(\"Lloyd\").soundex(), UTF8String.fromString(\"L300\"));\n    assertEquals(UTF8String.fromString(\"Moses\").soundex(), UTF8String.fromString(\"M220\"));\n    assertEquals(UTF8String.fromString(\"O'Hara\").soundex(), UTF8String.fromString(\"O600\"));\n    assertEquals(UTF8String.fromString(\"Pfister\").soundex(), UTF8String.fromString(\"P236\"));\n    assertEquals(UTF8String.fromString(\"Rubin\").soundex(), UTF8String.fromString(\"R150\"));\n    assertEquals(UTF8String.fromString(\"Robert\").soundex(), UTF8String.fromString(\"R163\"));\n    assertEquals(UTF8String.fromString(\"Rupert\").soundex(), UTF8String.fromString(\"R163\"));\n    assertEquals(UTF8String.fromString(\"Soundex\").soundex(), UTF8String.fromString(\"S532\"));\n    assertEquals(UTF8String.fromString(\"Sownteks\").soundex(), UTF8String.fromString(\"S532\"));\n    assertEquals(UTF8String.fromString(\"Tymczak\").soundex(), UTF8String.fromString(\"T522\"));\n    assertEquals(UTF8String.fromString(\"VanDeusen\").soundex(), UTF8String.fromString(\"V532\"));\n    assertEquals(UTF8String.fromString(\"Washington\").soundex(), UTF8String.fromString(\"W252\"));\n    assertEquals(UTF8String.fromString(\"Wheaton\").soundex(), UTF8String.fromString(\"W350\"));\n\n    assertEquals(UTF8String.fromString(\"a\").soundex(), UTF8String.fromString(\"A000\"));\n    assertEquals(UTF8String.fromString(\"ab\").soundex(), UTF8String.fromString(\"A100\"));\n    assertEquals(UTF8String.fromString(\"abc\").soundex(), UTF8String.fromString(\"A120\"));\n    assertEquals(UTF8String.fromString(\"abcd\").soundex(), UTF8String.fromString(\"A123\"));\n    assertEquals(UTF8String.fromString(\"\").soundex(), UTF8String.fromString(\"\"));\n    assertEquals(UTF8String.fromString(\"123\").soundex(), UTF8String.fromString(\"123\"));\n    assertEquals(UTF8String.fromString(\"世界千世\").soundex(), UTF8String.fromString(\"世界千世\"));\n  }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/migrate/MigrateUtilsTest.java",
    "content": "package io.mycat.migrate;\n\nimport com.google.common.collect.Lists;\nimport io.mycat.migrate.MigrateTask;\nimport io.mycat.migrate.MigrateUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.io.ByteArrayInputStream;\nimport java.util.*;\n\nimport static io.mycat.route.function.PartitionByCRC32PreSlot.Range;\n\n/**\n * Created by magicdoom on 2016/9/16.\n */\npublic class MigrateUtilsTest {\n    @Test\n    public void balanceExpand()\n    {   String table=\"test\";\n        Map<Integer, List<Range>> integerListMap = new TreeMap<>();\n        integerListMap.put(0,Lists.newArrayList(new Range(0,32))) ;\n        integerListMap.put(1,Lists.newArrayList(new Range(33,65))) ;\n        integerListMap.put(2,Lists.newArrayList(new Range(66,99))) ;\n        pringList(\"beforse  balance :\",integerListMap);\n        //dn1=0-32    dn2=33-65  dn3=66-99\n        int totalSlots=100;\n        List<String> oldDataNodes = Lists.newArrayList(\"dn1\",\"dn2\",\"dn3\");\n        List<String> newDataNodes =  Lists.newArrayList(\"dn4\",\"dn5\");\n        Map<String, List<MigrateTask>> tasks= MigrateUtils\n                .balanceExpand(table, integerListMap, oldDataNodes, newDataNodes,totalSlots);\n        for (Map.Entry<String, List<MigrateTask>> stringListEntry : tasks.entrySet()) {\n            String key=stringListEntry.getKey();\n            List<Range> rangeList=new ArrayList<>();\n            List<MigrateTask> value=stringListEntry.getValue();\n            for (MigrateTask task : value) {\n               rangeList.addAll(task.getSlots());\n            }\n            Assert.assertEquals(true,value.size()==2);\n               if(\"dn4\".equals(key)) {\n                Assert.assertEquals(0, rangeList.get(0).start);\n                Assert.assertEquals(12, rangeList.get(0).end);\n                Assert.assertEquals(33, rangeList.get(1).start);\n                Assert.assertEquals(39, rangeList.get(1).end);\n            }  else   if(\"dn5\".equals(key)) {\n                   Assert.assertEquals(40, rangeList.get(0).start);\n                   Assert.assertEquals(45, rangeList.get(0).end);\n                   Assert.assertEquals(66, rangeList.get(1).start);\n                   Assert.assertEquals(79, rangeList.get(1).end);\n               }\n            integerListMap.put(Integer.parseInt(key.substring(2))-1,rangeList);\n        }\n\n        pringList(\"after balance :\",integerListMap);\n\n        System.out.println(\"agin balance .....................\");\n\n\n\n\n         oldDataNodes = Lists.newArrayList(\"dn1\",\"dn2\",\"dn3\",\"dn4\",\"dn5\");\n         newDataNodes =  Lists.newArrayList(\"dn6\",\"dn7\",\"dn8\",\"dn9\");\n        Map<String, List<MigrateTask>> tasks1= MigrateUtils.balanceExpand(table, integerListMap, oldDataNodes, newDataNodes,totalSlots);\n        for (Map.Entry<String, List<MigrateTask>> stringListEntry : tasks1.entrySet()) {\n            String key=stringListEntry.getKey();\n            List<Range> rangeList=new ArrayList<>();\n            List<MigrateTask> value=stringListEntry.getValue();\n            for (MigrateTask task : value) {\n                rangeList.addAll(task.getSlots());\n            }\n            if(\"dn6\".equals(key)) {\n                Assert.assertEquals(13, rangeList.get(0).start);\n                Assert.assertEquals(21, rangeList.get(0).end);\n                Assert.assertEquals(46, rangeList.get(1).start);\n                Assert.assertEquals(48, rangeList.get(1).end);\n            }  else   if(\"dn7\".equals(key)) {\n                Assert.assertEquals(49, rangeList.get(0).start);\n                Assert.assertEquals(54, rangeList.get(0).end);\n                Assert.assertEquals(80, rangeList.get(1).start);\n                Assert.assertEquals(84, rangeList.get(1).end);\n            } else     if(\"dn8\".equals(key)) {\n                Assert.assertEquals(85, rangeList.get(0).start);\n                Assert.assertEquals(88, rangeList.get(0).end);\n                Assert.assertEquals(0, rangeList.get(1).start);\n                Assert.assertEquals(6, rangeList.get(1).end);\n            }  else   if(\"dn9\".equals(key)) {\n                Assert.assertEquals(7, rangeList.get(0).start);\n                Assert.assertEquals(8, rangeList.get(0).end);\n                Assert.assertEquals(40, rangeList.get(1).start);\n                Assert.assertEquals(45, rangeList.get(1).end);\n            }\n\n            integerListMap.put(Integer.parseInt(key.substring(2))-1,rangeList);\n        }\n\n        pringList(\"agin balance :\",integerListMap);\n\n\n        oldDataNodes = Lists.newArrayList(\"dn1\",\"dn2\",\"dn3\",\"dn4\",\"dn5\",\"dn6\",\"dn7\",\"dn8\",\"dn9\");\n        newDataNodes =  Lists.newArrayList(\"dn10\",\"dn11\",\"dn12\",\"dn13\",\"dn14\",\"dn15\",\"dn16\",\"dn17\",\"dn18\",\"dn19\",\"dn20\",\"dn21\",\"dn22\",\"dn23\",\"dn24\",\"dn25\",\"dn26\",\"dn27\",\"dn28\",\"dn29\",\"dn30\",\"dn31\",\"dn32\",\"dn33\",\"dn34\",\"dn35\",\"dn36\",\"dn37\",\"dn38\",\"dn39\",\"dn40\",\"dn41\",\"dn42\",\"dn43\",\"dn44\",\"dn45\",\"dn46\",\"dn47\",\"dn48\",\"dn49\",\"dn50\",\"dn51\",\"dn52\",\"dn53\",\"dn54\",\"dn55\",\"dn56\",\"dn57\",\"dn58\",\"dn59\",\"dn60\",\"dn61\",\"dn62\",\"dn63\",\"dn64\",\"dn65\",\"dn66\",\"dn67\",\"dn68\",\"dn69\",\"dn70\",\"dn71\",\"dn72\",\"dn73\",\"dn74\",\"dn75\",\"dn76\",\"dn77\",\"dn78\",\"dn79\",\"dn80\",\"dn81\",\"dn82\",\"dn83\",\"dn84\",\"dn85\",\"dn86\",\"dn87\",\"dn88\",\"dn89\",\"dn90\",\"dn91\",\"dn92\",\"dn93\",\"dn94\",\"dn95\",\"dn96\",\"dn97\",\"dn98\",\"dn99\",\"dn100\"\n                );\n        Map<String, List<MigrateTask>> tasks2= MigrateUtils.balanceExpand(table, integerListMap, oldDataNodes, newDataNodes,totalSlots);\n        for (Map.Entry<String, List<MigrateTask>> stringListEntry : tasks2.entrySet()) {\n            String key=stringListEntry.getKey();\n            List<Range> rangeList=new ArrayList<>();\n            List<MigrateTask> value=stringListEntry.getValue();\n            for (MigrateTask task : value) {\n                rangeList.addAll(task.getSlots());\n            }\n\n            if(\"dn10\".equals(key)) {\n                Assert.assertEquals(22, rangeList.get(0).start);\n                Assert.assertEquals(22, rangeList.get(0).end);\n            }  else   if(\"dn100\".equals(key)) {\n                Assert.assertEquals(67, rangeList.get(0).start);\n                Assert.assertEquals(67, rangeList.get(0).end);\n            } else     if(\"dn50\".equals(key)) {\n                Assert.assertEquals(69, rangeList.get(0).start);\n                Assert.assertEquals(69, rangeList.get(0).end);\n            }  else   if(\"dn99\".equals(key)) {\n                Assert.assertEquals(66, rangeList.get(0).start);\n                Assert.assertEquals(66, rangeList.get(0).end);\n\n            }\n            integerListMap.put(Integer.parseInt(key.substring(2))-1,rangeList);\n        }\n\n        pringList(\"agin agin balance :\",integerListMap);\n\n    }\n    @Test\n    public void balanceExpand1() {\n        String table = \"test1\";\n        //4=81920-102399\n       // 3=61440-81919\n      //  2=40960-61439\n      //  1=20480-40959\n     //   0=0-20479\n        Map<Integer, List<Range>> integerListMap = new TreeMap<>();\n        integerListMap.put(0, Lists.newArrayList(new Range(0, 20479)));\n        integerListMap.put(1, Lists.newArrayList(new Range(20480, 40959)));\n        integerListMap.put(2, Lists.newArrayList(new Range(40960, 61439)));\n        integerListMap.put(3, Lists.newArrayList(new Range(61440, 81919)));\n        integerListMap.put(4, Lists.newArrayList(new Range(81920, 102399)));\n        pringList(\"beforse  balance :\", integerListMap);\n        //dn1=0-32    dn2=33-65  dn3=66-99\n        int totalSlots = 102400;\n        List<String> oldDataNodes = Lists.newArrayList(\"dn1\", \"dn2\", \"dn3\",\"dn4\", \"dn5\");\n        List<String> newDataNodes = Lists.newArrayList(\"dn6\", \"dn7\", \"dn8\",\"dn9\", \"dn10\");\n        Map<String, List<MigrateTask>> tasks = MigrateUtils\n                .balanceExpand(table, integerListMap, oldDataNodes, newDataNodes, totalSlots);\n\n        List<MigrateTask>  allTaskList=new ArrayList<>();\n\n        for (Map.Entry<String, List<MigrateTask>> stringListEntry : tasks.entrySet()) {\n            String key=stringListEntry.getKey();\n            List<Range> rangeList=new ArrayList<>();\n            List<MigrateTask> value=stringListEntry.getValue();\n            allTaskList.addAll(value);\n            for (MigrateTask task : value) {\n                rangeList.addAll(task.getSlots());\n            }\n\n\n            integerListMap.put(Integer.parseInt(key.substring(2))-1,rangeList);\n        }\n        pringList(\"after  balance :\", integerListMap);\n\n\n      List<String> allNewDataNodes=new ArrayList<>();\n        allNewDataNodes.addAll(oldDataNodes);\n        allNewDataNodes.addAll(newDataNodes);\n        Properties prop = new Properties();\n        prop.put(\"0\",\"0-20479\");\n        prop.put(\"1\",\"20480-40959\");\n        prop.put(\"2\",\"40960-61439\");\n        prop.put(\"3\",\"61440-81919\");\n        prop.put(\"4\",\"81920-102399\");\n        for (MigrateTask migrateTask : allTaskList) {\n            modifyRuleData(prop,migrateTask,allNewDataNodes);\n        }\n\n        System.out.println();\n    }\n\n    private   void modifyRuleData( Properties prop ,MigrateTask task ,List<String> allNewDataNodes){\n        int fromIndex=-1;\n        int toIndex=-1;\n        List<String> dataNodes=   allNewDataNodes;\n        for (int i = 0; i < dataNodes.size(); i++) {\n            String dataNode = dataNodes.get(i);\n            if(dataNode.equalsIgnoreCase(task.getFrom())){\n                fromIndex=i;\n            } else\n            if(dataNode.equalsIgnoreCase(task.getTo())){\n                toIndex=i;\n            }\n        }\n        String from=  prop.getProperty(String.valueOf(fromIndex)) ;\n        String to=  prop.getProperty(String.valueOf(toIndex)) ;\n        String fromRemain=removeRangesRemain(from,task.getSlots());\n        String taskRanges = MigrateUtils.convertRangeListToString(task.getSlots());\n        String newTo=to==null? taskRanges : to+\",\"+taskRanges;\n        prop.setProperty(String.valueOf(fromIndex),fromRemain);\n        prop.setProperty(String.valueOf(toIndex),newTo);\n    }\n\n    private  String removeRangesRemain(String ori,List<Range> rangeList){\n        List<Range> ranges=MigrateUtils.convertRangeStringToList(ori);\n        List<Range> ramain=  MigrateUtils.removeAndGetRemain(ranges,rangeList);\n        return MigrateUtils.convertRangeListToString(ramain);\n    }\n\n    private void pringList(String comm,Map<Integer, List<Range>> integerListMap) {\n        System.out.println(comm);\n        for (Map.Entry<Integer, List<Range>> integerListEntry : integerListMap.entrySet()) {\n            Integer key=integerListEntry.getKey();\n            List<Range> value=integerListEntry.getValue();\n            System.out.println(key+\":\"+listToString(value)+\":\"+MigrateUtils.getCurTotalSize(value));\n        }\n    }\n\n    private String listToString(List<Range> rangeList)\n    {  String rtn=\"\";\n        for (Range range : rangeList) {\n            rtn=rtn+range.start+\"-\"+range.end+\",\";\n        }\n\n        return rtn;\n    }\n\n\n\n    @Test\n    public void removeAndGetRemain(){\n        List<Range> oldRangeList1=Lists.newArrayList(new Range(0,51199));\n        List<Range> newRangeList1=Lists.newArrayList(new Range(0,20479),new Range(20480,30719));\n        List<Range> result1=MigrateUtils.removeAndGetRemain(oldRangeList1,newRangeList1);\n        Assert.assertEquals(1,result1.size());\n        Assert.assertEquals(30720,result1.get(0).start);\n        Assert.assertEquals(51199,result1.get(0).end);\n\n        List<Range> oldRangeList2=Lists.newArrayList(new Range(51200,102399));\n        List<Range> newRangeList2=Lists.newArrayList(new Range(61440,81919),new Range(51200,61439));\n        List<Range> result2=MigrateUtils.removeAndGetRemain(oldRangeList2,newRangeList2);\n        Assert.assertEquals(1,result2.size());\n        Assert.assertEquals(81920,result2.get(0).start);\n        Assert.assertEquals(102399,result2.get(0).end);\n\n    }\n    @Test\n    public void removeAndGetRemain1(){\n        List<Range> oldRangeList1=Lists.newArrayList(new Range(0,0),new Range(1,5),new Range(6,40000),new Range(40001,51199));\n        List<Range> newRangeList1=Lists.newArrayList(new Range(0,3),new Range(20480,30719));\n        List<Range> result1=MigrateUtils.removeAndGetRemain(oldRangeList1,newRangeList1);\n        Assert.assertEquals(4,result1.size());\n        Assert.assertEquals(4,result1.get(0).start);\n        Assert.assertEquals(5,result1.get(0).end);\n        Assert.assertEquals(6,result1.get(1).start);\n        Assert.assertEquals(20479,result1.get(1).end);\n        Assert.assertEquals(30720,result1.get(2).start);\n        Assert.assertEquals(40000,result1.get(2).end);\n        Assert.assertEquals(40001,result1.get(3).start);\n        Assert.assertEquals(51199,result1.get(3).end);\n\n\n\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/model/M1.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.model;\n\nimport java.util.concurrent.BlockingQueue;\n\nimport jsr166y.LinkedTransferQueue;\n\n/**\n * @author mycat\n */\npublic class M1 {\n\n    private long count;\n    private final BlockingQueue<TransferObject> x;\n    private final BlockingQueue<TransferObject> y;\n\n    public M1() {\n        this.x = new LinkedTransferQueue<TransferObject>();\n        this.y = new LinkedTransferQueue<TransferObject>();\n    }\n\n    public long getCount() {\n        return count;\n    }\n\n    public BlockingQueue<TransferObject> getX() {\n        return x;\n    }\n\n    public BlockingQueue<TransferObject> getY() {\n        return y;\n    }\n\n    public void start() {\n        new Thread(new A(), \"A\").start();\n        new Thread(new B(), \"B\").start();\n        new Thread(new C(), \"C\").start();\n    }\n\n    private final class A implements Runnable {\n        @Override\n        public void run() {\n            for (;;) {\n                try {\n                    Thread.sleep(200L);\n                } catch (InterruptedException e) {\n                }\n                for (int i = 0; i < 1000000; i++) {\n                    x.offer(new TransferObject());\n                }\n            }\n        }\n    }\n\n    private final class B implements Runnable {\n        @Override\n        public void run() {\n            TransferObject t = null;\n            for (;;) {\n                try {\n                    t = x.take();\n                } catch (InterruptedException e) {\n                    continue;\n                }\n                t.handle();\n                y.offer(t);\n            }\n        }\n    }\n\n    private final class C implements Runnable {\n        @Override\n        public void run() {\n            TransferObject t = null;\n            for (;;) {\n                try {\n                    t = y.take();\n                } catch (InterruptedException e) {\n                    continue;\n                }\n                t.compelete();\n                count++;\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/model/M1Main.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.model;\n\n/**\n * @author mycat\n */\npublic class M1Main {\n\n    public static void main(String[] args) {\n        final M1 m1 = new M1();\n        m1.start();\n\n        new Thread() {\n            @Override\n            public void run() {\n                for (;;) {\n                    long c = m1.getCount();\n                    try {\n                        Thread.sleep(2000L);\n                    } catch (InterruptedException e) {\n                        continue;\n                    }\n                    System.out.println(\"tps:\" + (m1.getCount() - c) / 2);\n                    System.out.println(\"  x:\" + m1.getX().size());\n                    System.out.println(\"  y:\" + m1.getY().size());\n                    System.out.println(\"==============\");\n                }\n            }\n        }.start();\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/model/M2.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.model;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\n\nimport io.mycat.util.ExecutorUtil;\nimport jsr166y.LinkedTransferQueue;\n\n/**\n * @author mycat\n */\npublic class M2 {\n    private long count;\n    private final ThreadPoolExecutor x;\n    private final BlockingQueue<TransferObject> y;\n\n    public M2() {\n        this.x = ExecutorUtil.create(\"B\", 1);\n        this.y = new LinkedTransferQueue<TransferObject>();\n    }\n\n    public long getCount() {\n        return count;\n    }\n\n    public ThreadPoolExecutor getX() {\n        return x;\n    }\n\n    public BlockingQueue<TransferObject> getY() {\n        return y;\n    }\n\n    public void start() {\n        new Thread(new A(), \"A\").start();\n        new Thread(new C(), \"C\").start();\n    }\n\n    private final class A implements Runnable {\n        @Override\n        public void run() {\n            for (;;) {\n                try {\n                    Thread.sleep(200L);\n                } catch (InterruptedException e) {\n                }\n                for (int i = 0; i < 1000000; i++) {\n                    final TransferObject t = new TransferObject();\n                    x.execute(new Runnable() {\n                        @Override\n                        public void run() {\n                            t.handle();\n                            y.offer(t);\n                        }\n                    });\n                }\n            }\n        }\n    }\n\n    private final class C implements Runnable {\n        @Override\n        public void run() {\n            TransferObject t = null;\n            for (;;) {\n                try {\n                    t = y.take();\n                } catch (InterruptedException e) {\n                    continue;\n                }\n                t.compelete();\n                count++;\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/model/M2Main.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.model;\n\n/**\n * @author mycat\n */\npublic class M2Main {\n\n    public static void main(String[] args) {\n        final M2 m2 = new M2();\n        m2.start();\n\n        new Thread() {\n            @Override\n            public void run() {\n                for (;;) {\n                    long c = m2.getCount();\n                    try {\n                        Thread.sleep(2000L);\n                    } catch (InterruptedException e) {\n                        continue;\n                    }\n                    System.out.println(\"tps:\" + (m2.getCount() - c) / 2);\n                    System.out.println(\"  x:\" + m2.getX().getQueue().size());\n                    System.out.println(\"  y:\" + m2.getY().size());\n                    System.out.println(\"==============\");\n                }\n            }\n        }.start();\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/model/TransferObject.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.model;\n\n/**\n * @author mycat\n */\npublic class TransferObject {\n    long handleCount;\n    long compeleteCount;\n\n    public void handle() {\n        handleCount++;\n    }\n\n    public void compelete() {\n        compeleteCount++;\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/mpp/TestSorter.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.mpp;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport io.mycat.util.ByteUtil;\n\npublic class TestSorter {\n\n\t@Test\n\tpublic void testDecimal() {\n\t\tString d1 = \"-1223.000\";\n\t\tbyte[] d1b = d1.getBytes();\n\t\tAssert.assertEquals(true, -1223.0 == ByteUtil.getDouble(d1b));\n\t\td1b = \"-99999.890\".getBytes();\n\t\tAssert.assertEquals(true, -99999.890 == ByteUtil.getDouble(d1b));\n\t\t// 221346.000\n\t\tbyte[] data2 = new byte[] { 50, 50, 49, 51, 52, 54, 46, 48, 48, 48 };\n\t\tAssert.assertEquals(true, 221346.000 == ByteUtil.getDouble(data2));\n\t\t// 1234567890\n\t\tbyte[] data3 = new byte[] { 49, 50, 51, 52, 53, 54, 55, 56, 57, 48 };\n\t\tAssert.assertEquals(true, 1234567890 == ByteUtil.getInt(data3));\n\n\t\t// 0123456789\n\t\tbyte[] data4 = new byte[] { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 };\n\t\tAssert.assertEquals(true, 123456789 == ByteUtil.getInt(data4));\n\t}\n\n\t@Test\n\tpublic void testNumberCompare() {\n\t\tbyte[] b1 = \"0\".getBytes();\n\t\tbyte[] b2 = \"0\".getBytes();\n\t\tAssert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2) == 0);\n\n\t\tb1 = \"0\".getBytes();\n\t\tb2 = \"1\".getBytes();\n\t\tAssert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2)< 0);\n\t\t\n\t\tb1 = \"10\".getBytes();\n\t\tb2 = \"1\".getBytes();\n\t\tAssert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2)> 0);\n\t\t\n\t\tb1 = \"100.0\".getBytes();\n\t\tb2 = \"100.0\".getBytes();\n\t\tAssert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2)==0);\n\t\t\n\t\tb1 = \"100.000\".getBytes();\n\t\tb2 = \"100.0\".getBytes();\n\t\tAssert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2)>0);\n\t\t\n\t\tb1 = \"-100.000\".getBytes();\n\t\tb2 = \"-100.0\".getBytes();\n\t\tAssert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2)<0);\n\t\t\n\t\tb1 = \"-100.001\".getBytes();\n\t\tb2 = \"-100.0\".getBytes();\n\t\tAssert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2)<0);\n\t\t\n\t\tb1 = \"-100.001\".getBytes();\n\t\tb2 = \"100.0\".getBytes();\n\t\tAssert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2)<0);\n\t\t\n\t\tb1 = \"90\".getBytes();\n\t\tb2 = \"10000\".getBytes();\n\t\tAssert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2)<0);\n\t\tb1 = \"-90\".getBytes();\n\t\tb2 = \"-10000\".getBytes();\n\t\tAssert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2)>0);\n\t\t\n\t\tb1 = \"98\".getBytes();\n\t\tb2 = \"98000\".getBytes();\n\t\tAssert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2)<0);\n\t\t\n\t\tb1 = \"-98\".getBytes();\n\t\tb2= \"-98000\".getBytes();\n\t\tAssert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2)>0);\n\t\t\n\t\tb1=\"12002585786\".getBytes();\n        b2=\"12002585785\".getBytes();\n        Assert.assertEquals(true, ByteUtil.compareNumberByte(b1, b2)>0);\n\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/mysql/MySQLMessageTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.mysql;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport io.mycat.backend.mysql.MySQLMessage;\n\n/**\n * @author mycat\n */\npublic class MySQLMessageTest {\n\n    @Test\n    public void testReadBytesWithNull() {\n        byte[] bytes = new byte[] { 1, 2, 3, 0, 5 };\n        MySQLMessage message = new MySQLMessage(bytes);\n        byte[] ab = message.readBytesWithNull();\n        Assert.assertEquals(3, ab.length);\n        Assert.assertEquals(4, message.position());\n    }\n\n    @Test\n    public void testReadBytesWithNull2() {\n        byte[] bytes = new byte[] { 0, 1, 2, 3, 0, 5 };\n        MySQLMessage message = new MySQLMessage(bytes);\n        byte[] ab = message.readBytesWithNull();\n        Assert.assertEquals(0, ab.length);\n        Assert.assertEquals(1, message.position());\n    }\n\n    @Test\n    public void testReadBytesWithNull3() {\n        byte[] bytes = new byte[] {};\n        MySQLMessage message = new MySQLMessage(bytes);\n        byte[] ab = message.readBytesWithNull();\n        Assert.assertEquals(0, ab.length);\n        Assert.assertEquals(0, message.position());\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/mysql/ResultSetPacketParse.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.mysql;\n\nimport io.mycat.util.FormatUtil;\nimport io.mycat.util.SplitUtil;\n\n/**\n * @author mycat\n */\npublic class ResultSetPacketParse {\n\n    public static String parse(String src) {\n        String[] sa = SplitUtil.split(src, ',', true);\n        StringBuilder s = new StringBuilder();\n        for (int i = 0; i < sa.length;) {\n            int length = Byte.parseByte(sa[i++]) & 0xff;\n            length |= (Byte.parseByte(sa[i++]) & 0xff) << 8;\n            length |= (Byte.parseByte(sa[i++]) & 0xff) << 16;\n            s.append(\"Length=\").append(FormatUtil.format(length, 3)).append(',');\n            s.append(\"Id=\").append(Byte.parseByte(sa[i++])).append(':');\n            for (int x = 0; x < length; x++) {\n                s.append(' ').append(sa[i++]);\n            }\n            s.append('\\n');\n        }\n        return s.toString();\n    }\n\n    static String s = \"1, 0, 0, 1, 1, 68, 0, 0, 2, 3, 100, 101, 102, 22, 99, 111, 98, 97, 114, 95, 116, 101, 115, 116, 95, 99, 111, 110, 110, 95, 98, 105, 110, 100, 95, 49, 2, 116, 49, 2, 116, 49, 10, 114, 101, 97, 100, 69, 114, 114, 67, 111, 108, 10, 114, 101, 97, 100, 69, 114, 114, 67, 111, 108, 12, 63, 0, 11, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 3, -2, 0, 0, 34, 0, 4, 0, 0, 4, 3, 49, 50, 51, 46, 0, 0, 5, -1, 30, 4, 85, 110, 107, 110, 111, 119, 110, 32, 99, 111, 108, 117, 109, 110, 32, 39, 114, 101, 97, 100, 69, 114, 114, 67, 111, 108, 39, 32, 105, 110, 32, 39, 102, 105, 101, 108, 100, 32, 108, 105, 115, 116, 39\";\n    static String s2 = \"1, 0, 0, 1, 1, 68, 0, 0, 2, 3, 100, 101, 102, 22, 99, 111, 98, 97, 114, 95, 116, 101, 115, 116, 95, 99, 111, 110, 110, 95, 98, 105, 110, 100, 95, 49, 2, 116, 49, 2, 116, 49, 10, 114, 101, 97, 100, 69, 114, 114, 67, 111, 108, 10, 114, 101, 97, 100, 69, 114, 114, 67, 111, 108, 12, 63, 0, 11, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 3, -2, 0, 0, 34, 0, 4, 0, 0, 4, 3, 49, 50, 51, 4, 0, 0, 5, 3, 49, 50, 51, 46, 0, 0, 6, -1, 30, 4, 85, 110, 107, 110, 111, 119, 110, 32, 99, 111, 108, 117, 109, 110, 32, 39, 114, 101, 97, 100, 69, 114, 114, 67, 111, 108, 39, 32, 105, 110, 32, 39, 102, 105, 101, 108, 100, 32, 108, 105, 115, 116, 39\";\n    static String s3 = \"1, 0, 0, 1, 1, 46, 0, 0, 1, -1, 30, 4, 85, 110, 107, 110, 111, 119, 110, 32, 99, 111, 108, 117, 109, 110, 32, 39, 114, 101, 97, 100, 69, 114, 114, 67, 111, 108, 39, 32, 105, 110, 32, 39, 102, 105, 101, 108, 100, 32, 108, 105, 115, 116, 39\";\n    static String s4 = \"1, 0, 0, 1, 1, 68, 0, 0, 2, 3, 100, 101, 102, 22, 99, 111, 98, 97, 114, 95, 116, 101, 115, 116, 95, 99, 111, 110, 110, 95, 98, 105, 110, 100, 95, 49, 2, 116, 49, 2, 116, 49, 10, 114, 101, 97, 100, 69, 114, 114, 67, 111, 108, 10, 114, 101, 97, 100, 69, 114, 114, 67, 111, 108, 12, 63, 0, 11, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 3, -2, 0, 0, 34, 0, 4, 0, 0, 4, 3, 49, 50, 51, 4, 0, 0, 5, 3, 49, 50, 51, 46, 0, 0, 6, -1, 30, 4, 85, 110, 107, 110, 111, 119, 110, 32, 99, 111, 108, 117, 109, 110, 32, 39, 114, 101, 97, 100, 69, 114, 114, 67, 111, 108, 39, 32, 105, 110, 32, 39, 102, 105, 101, 108, 100, 32, 108, 105, 115, 116, 39\";\n    static String s5 = \"1, 0, 0, 1, 1, 1, 0, 0, 2, 1, 46, 0, 0, 3, -1, 30, 4, 85, 110, 107, 110, 111, 119, 110, 32, 99, 111, 108, 117, 109, 110, 32, 39, 114, 101, 97, 100, 69, 114, 114, 67, 111, 108, 39, 32, 105, 110, 32, 39, 102, 105, 101, 108, 100, 32, 108, 105, 115, 116, 39\";\n\n    public static void main(String[] args) {\n        System.out.println(ResultSetPacketParse.parse(s));\n        System.out.println(ResultSetPacketParse.parse(s2));\n        System.out.println(ResultSetPacketParse.parse(s3));\n        System.out.println(ResultSetPacketParse.parse(s4));\n        System.out.println(ResultSetPacketParse.parse(s5));\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/parser/ManagerParserTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.parser;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport io.mycat.route.parser.ManagerParse;\nimport io.mycat.route.parser.ManagerParseClear;\nimport io.mycat.route.parser.ManagerParseReload;\nimport io.mycat.route.parser.ManagerParseRollback;\nimport io.mycat.route.parser.ManagerParseShow;\nimport io.mycat.route.parser.ManagerParseStop;\n\n/**\n * @author mycat\n */\npublic class ManagerParserTest {\n\n    @Test\n    public void testIsSelect() {\n        Assert.assertEquals(ManagerParse.SELECT, 0xff & ManagerParse.parse(\"select * from offer limit 1\"));\n        Assert.assertEquals(ManagerParse.SELECT, 0xff & ManagerParse.parse(\"SELECT * FROM OFFER LIMIT 1\"));\n        Assert.assertEquals(ManagerParse.SELECT, 0xff & ManagerParse.parse(\"SELECT * FROM OFFER limit 1\"));\n    }\n\n    @Test\n    public void testIsSet() {\n        Assert.assertEquals(ManagerParse.SET, ManagerParse.parse(\"set names utf8\"));\n        Assert.assertEquals(ManagerParse.SET, ManagerParse.parse(\"SET NAMES UTF8\"));\n        Assert.assertEquals(ManagerParse.SET, ManagerParse.parse(\"set NAMES utf8\"));\n    }\n\n    @Test\n    public void testIsShow() {\n        Assert.assertEquals(ManagerParse.SHOW, 0xff & ManagerParse.parse(\"show databases\"));\n        Assert.assertEquals(ManagerParse.SHOW, 0xff & ManagerParse.parse(\"SHOW DATABASES\"));\n        Assert.assertEquals(ManagerParse.SHOW, 0xff & ManagerParse.parse(\"SHOW databases\"));\n    }\n\n    @Test\n    public void testShowCommand() {\n        Assert.assertEquals(ManagerParseShow.COMMAND, ManagerParseShow.parse(\"show @@command\", 5));\n        Assert.assertEquals(ManagerParseShow.COMMAND, ManagerParseShow.parse(\"SHOW @@COMMAND\", 5));\n        Assert.assertEquals(ManagerParseShow.COMMAND, ManagerParseShow.parse(\"show @@COMMAND\", 5));\n    }\n\n    @Test\n    public void testShowConnection() {\n        Assert.assertEquals(ManagerParseShow.CONNECTION, ManagerParseShow.parse(\"show @@connection\", 5));\n        Assert.assertEquals(ManagerParseShow.CONNECTION, ManagerParseShow.parse(\"SHOW @@CONNECTION\", 5));\n        Assert.assertEquals(ManagerParseShow.CONNECTION, ManagerParseShow.parse(\"show @@CONNECTION\", 5));\n    }\n\n    @Test\n    public void testShowConnectionSQL() {\n        Assert.assertEquals(ManagerParseShow.CONNECTION_SQL, ManagerParseShow.parse(\"show @@connection.sql\", 5));\n        Assert.assertEquals(ManagerParseShow.CONNECTION_SQL, ManagerParseShow.parse(\"SHOW @@CONNECTION.SQL\", 5));\n        Assert.assertEquals(ManagerParseShow.CONNECTION_SQL, ManagerParseShow.parse(\"show @@CONNECTION.Sql\", 5));\n    }\n\n    @Test\n    public void testShowDatabase() {\n        Assert.assertEquals(ManagerParseShow.DATABASE, ManagerParseShow.parse(\"show @@database\", 5));\n        Assert.assertEquals(ManagerParseShow.DATABASE, ManagerParseShow.parse(\"SHOW @@DATABASE\", 5));\n        Assert.assertEquals(ManagerParseShow.DATABASE, ManagerParseShow.parse(\"show @@DATABASE\", 5));\n    }\n\n    @Test\n    public void testShowDataNode() {\n        Assert.assertEquals(ManagerParseShow.DATANODE, ManagerParseShow.parse(\"show @@datanode\", 5));\n        Assert.assertEquals(ManagerParseShow.DATANODE, ManagerParseShow.parse(\"SHOW @@DATANODE\", 5));\n        Assert.assertEquals(ManagerParseShow.DATANODE, ManagerParseShow.parse(\"show @@DATANODE\", 5));\n        Assert.assertEquals(ManagerParseShow.DATANODE, ManagerParseShow.parse(\"show @@DATANODE   \", 5));\n        Assert.assertEquals(ManagerParseShow.DATANODE_WHERE,\n                0xff & ManagerParseShow.parse(\"show @@DATANODE WHERE SCHEMA=1\", 5));\n        Assert.assertEquals(ManagerParseShow.DATANODE_WHERE,\n                0xff & ManagerParseShow.parse(\"show @@DATANODE WHERE schema =1\", 5));\n        Assert.assertEquals(ManagerParseShow.DATANODE_WHERE,\n                0xff & ManagerParseShow.parse(\"show @@DATANODE WHERE SCHEMA= 1\", 5));\n    }\n\n    @Test\n    public void testShowDataSource() {\n        Assert.assertEquals(ManagerParseShow.DATASOURCE, ManagerParseShow.parse(\"show @@datasource\", 5));\n        Assert.assertEquals(ManagerParseShow.DATASOURCE, ManagerParseShow.parse(\"SHOW @@DATASOURCE\", 5));\n        Assert.assertEquals(ManagerParseShow.DATASOURCE, ManagerParseShow.parse(\" show  @@DATASOURCE \", 5));\n        Assert.assertEquals(ManagerParseShow.DATASOURCE, ManagerParseShow.parse(\" show  @@DATASOURCE   \", 5));\n        Assert.assertEquals(ManagerParseShow.DATASOURCE_WHERE,\n                0xff & ManagerParseShow.parse(\" show  @@DATASOURCE where datanode = 1\", 5));\n        Assert.assertEquals(ManagerParseShow.DATASOURCE_WHERE,\n                0xff & ManagerParseShow.parse(\" show  @@DATASOURCE where datanode=1\", 5));\n        Assert.assertEquals(ManagerParseShow.DATASOURCE_WHERE,\n                0xff & ManagerParseShow.parse(\" show  @@DATASOURCE WHERE datanode = 1\", 5));\n        Assert.assertEquals(ManagerParseShow.DATASOURCE_WHERE,\n                0xff & ManagerParseShow.parse(\" show  @@DATASOURCE where DATAnode= 1 \", 5));\n    }\n\n    @Test\n    public void testShowHelp() {\n        Assert.assertEquals(ManagerParseShow.HELP, ManagerParseShow.parse(\"show @@help\", 5));\n        Assert.assertEquals(ManagerParseShow.HELP, ManagerParseShow.parse(\"SHOW @@HELP\", 5));\n        Assert.assertEquals(ManagerParseShow.HELP, ManagerParseShow.parse(\"show @@HELP\", 5));\n    }\n\n    @Test\n    public void testShowHeartbeat() {\n        Assert.assertEquals(ManagerParseShow.HEARTBEAT, ManagerParseShow.parse(\"show @@heartbeat\", 5));\n        Assert.assertEquals(ManagerParseShow.HEARTBEAT, ManagerParseShow.parse(\"SHOW @@hearTBeat \", 5));\n        Assert.assertEquals(ManagerParseShow.HEARTBEAT, ManagerParseShow.parse(\"  show   @@HEARTBEAT  \", 6));\n    }\n\n    @Test\n    public void testShowParser() {\n        Assert.assertEquals(ManagerParseShow.PARSER, ManagerParseShow.parse(\"show @@parser\", 5));\n        Assert.assertEquals(ManagerParseShow.PARSER, ManagerParseShow.parse(\"SHOW @@PARSER\", 5));\n        Assert.assertEquals(ManagerParseShow.PARSER, ManagerParseShow.parse(\"show @@PARSER\", 5));\n    }\n\n    @Test\n    public void testShowProcessor() {\n        Assert.assertEquals(ManagerParseShow.PROCESSOR, ManagerParseShow.parse(\"show @@processor\", 5));\n        Assert.assertEquals(ManagerParseShow.PROCESSOR, ManagerParseShow.parse(\"SHOW @@PROCESSOR\", 5));\n        Assert.assertEquals(ManagerParseShow.PROCESSOR, ManagerParseShow.parse(\"show @@PROCESSOR\", 5));\n    }\n\n    @Test\n    public void testShowRouter() {\n        Assert.assertEquals(ManagerParseShow.ROUTER, ManagerParseShow.parse(\"show @@router\", 5));\n        Assert.assertEquals(ManagerParseShow.ROUTER, ManagerParseShow.parse(\"SHOW @@ROUTER\", 5));\n        Assert.assertEquals(ManagerParseShow.ROUTER, ManagerParseShow.parse(\"show @@ROUTER\", 5));\n    }\n\n    @Test\n    public void testShowServer() {\n        Assert.assertEquals(ManagerParseShow.SERVER, ManagerParseShow.parse(\"show @@server\", 5));\n        Assert.assertEquals(ManagerParseShow.SERVER, ManagerParseShow.parse(\"SHOW @@SERVER\", 5));\n        Assert.assertEquals(ManagerParseShow.SERVER, ManagerParseShow.parse(\"show @@SERVER\", 5));\n    }\n\n    @Test\n    public void testShowThreadPool() {\n        Assert.assertEquals(ManagerParseShow.THREADPOOL, ManagerParseShow.parse(\"show @@threadPool\", 5));\n        Assert.assertEquals(ManagerParseShow.THREADPOOL, ManagerParseShow.parse(\"SHOW @@THREADPOOL\", 5));\n        Assert.assertEquals(ManagerParseShow.THREADPOOL, ManagerParseShow.parse(\"show @@THREADPOOL\", 5));\n    }\n\n    @Test\n    public void testShowBackend() {\n        Assert.assertEquals(ManagerParseShow.BACKEND, ManagerParseShow.parse(\"show @@backend\", 5));\n        Assert.assertEquals(ManagerParseShow.BACKEND, ManagerParseShow.parse(\"SHOW @@BACkend;\", 5));\n        Assert.assertEquals(ManagerParseShow.BACKEND, ManagerParseShow.parse(\"show @@BACKEND \", 5));\n    }\n\n    @Test\n    public void testShowTimeCurrent() {\n        Assert.assertEquals(ManagerParseShow.TIME_CURRENT, ManagerParseShow.parse(\"show @@time.current\", 5));\n        Assert.assertEquals(ManagerParseShow.TIME_CURRENT, ManagerParseShow.parse(\"SHOW @@TIME.CURRENT\", 5));\n        Assert.assertEquals(ManagerParseShow.TIME_CURRENT, ManagerParseShow.parse(\"show @@TIME.current\", 5));\n    }\n\n    @Test\n    public void testShowTimeStartUp() {\n        Assert.assertEquals(ManagerParseShow.TIME_STARTUP, ManagerParseShow.parse(\"show @@time.startup\", 5));\n        Assert.assertEquals(ManagerParseShow.TIME_STARTUP, ManagerParseShow.parse(\"SHOW @@TIME.STARTUP\", 5));\n        Assert.assertEquals(ManagerParseShow.TIME_STARTUP, ManagerParseShow.parse(\"show @@TIME.startup\", 5));\n    }\n\n    @Test\n    public void testShowVersion() {\n        Assert.assertEquals(ManagerParseShow.VERSION, ManagerParseShow.parse(\"show @@version\", 5));\n        Assert.assertEquals(ManagerParseShow.VERSION, ManagerParseShow.parse(\"SHOW @@VERSION\", 5));\n        Assert.assertEquals(ManagerParseShow.VERSION, ManagerParseShow.parse(\"show @@VERSION\", 5));\n    }\n\n    @Test\n    public void testShowSQL() {\n        Assert.assertEquals(ManagerParseShow.SQL, ManagerParseShow.parse(\"show @@sql where id = -1079800749\", 5));\n        Assert.assertEquals(ManagerParseShow.SQL, ManagerParseShow.parse(\"SHOW @@SQL WHERE ID = -1079800749\", 5));\n        Assert.assertEquals(ManagerParseShow.SQL, ManagerParseShow.parse(\"show @@Sql WHERE ID = -1079800749\", 5));\n        Assert.assertEquals(ManagerParseShow.SQL, ManagerParseShow.parse(\"show @@sql where id=-1079800749\", 5));\n        Assert.assertEquals(ManagerParseShow.SQL, ManagerParseShow.parse(\"show @@sql where id   =-1079800749 \", 5));\n    }\n\n    @Test\n    public void testShowSQLDetail() {\n        Assert.assertEquals(ManagerParseShow.SQL_DETAIL,\n                ManagerParseShow.parse(\"show @@sql.detail where id = -1079800749\", 5));\n        Assert.assertEquals(ManagerParseShow.SQL_DETAIL,\n                ManagerParseShow.parse(\"SHOW @@SQL.DETAIL WHERE ID = -1079800749\", 5));\n        Assert.assertEquals(ManagerParseShow.SQL_DETAIL,\n                ManagerParseShow.parse(\"show @@SQL.DETAIL WHERE ID = -1079800749\", 5));\n        Assert.assertEquals(ManagerParseShow.SQL_DETAIL,\n                ManagerParseShow.parse(\"show @@sql.detail where id=1079800749 \", 5));\n        Assert.assertEquals(ManagerParseShow.SQL_DETAIL,\n                ManagerParseShow.parse(\"show @@sql.detail where id= -1079800749\", 5));\n    }\n\n    @Test\n    public void testShowSQLExecute() {\n        Assert.assertEquals(ManagerParseShow.SQL_EXECUTE, ManagerParseShow.parse(\"show @@sql.execute\", 5));\n        Assert.assertEquals(ManagerParseShow.SQL_EXECUTE, ManagerParseShow.parse(\"SHOW @@SQL.EXECUTE\", 5));\n        Assert.assertEquals(ManagerParseShow.SQL_EXECUTE, ManagerParseShow.parse(\"show @@SQL.EXECUTE\", 5));\n    }\n\n    @Test\n    public void testShowSQLSlow() {\n        Assert.assertEquals(ManagerParseShow.SQL_SLOW, ManagerParseShow.parse(\"show @@sql.slow\", 5));\n        Assert.assertEquals(ManagerParseShow.SQL_SLOW, ManagerParseShow.parse(\"SHOW @@SQL.SLOW\", 5));\n        Assert.assertEquals(ManagerParseShow.SQL_SLOW, ManagerParseShow.parse(\"SHOW @@sql.slow\", 5));\n    }\n\n    @Test\n    public void testShowVariables() {\n        Assert.assertEquals(ManagerParseShow.VARIABLES, ManagerParseShow.parse(\"show variables\", 5));\n        Assert.assertEquals(ManagerParseShow.VARIABLES, ManagerParseShow.parse(\"SHOW VARIABLES\", 5));\n        Assert.assertEquals(ManagerParseShow.VARIABLES, ManagerParseShow.parse(\"show VARIABLES\", 5));\n    }\n\n    @Test\n    public void testShowCollation() {\n        Assert.assertEquals(ManagerParseShow.COLLATION, ManagerParseShow.parse(\"show collation\", 5));\n        Assert.assertEquals(ManagerParseShow.COLLATION, ManagerParseShow.parse(\"SHOW COLLATION\", 5));\n        Assert.assertEquals(ManagerParseShow.COLLATION, ManagerParseShow.parse(\"show COLLATION\", 5));\n    }\n\n    @Test\n    public void testSwitchPool() {\n        Assert.assertEquals(ManagerParse.SWITCH, 0xff & ManagerParse.parse(\"switch @@pool offer2$0-2\"));\n        Assert.assertEquals(ManagerParse.SWITCH, 0xff & ManagerParse.parse(\"SWITCH @@POOL offer2$0-2\"));\n        Assert.assertEquals(ManagerParse.SWITCH, 0xff & ManagerParse.parse(\"switch @@pool offer2$0-2 :2\"));\n    }\n\n    @Test\n    public void testComment() {\n        Assert.assertEquals(ManagerParse.SWITCH, 0xff & ManagerParse.parse(\"/* abc */switch @@pool offer2$0-2\"));\n        Assert.assertEquals(ManagerParse.SHOW, 0xff & ManagerParse.parse(\" /** 111**/Show @@help\"));\n        Assert.assertEquals(ManagerParse.SELECT, 0xff & ManagerParse.parse(\" /***/ select * from t \"));\n    }\n\n    @Test\n    public void testShowWhitComment() {\n        Assert.assertEquals(ManagerParseShow.VARIABLES,\n                ManagerParseShow.parse(\" /** 111**/show variables\", \" /** 111**/show\".length()));\n        Assert.assertEquals(ManagerParseShow.VARIABLES,\n                ManagerParseShow.parse(\" /**111**/ SHOW VARIABLES\", \" /** 111**/show\".length()));\n        Assert.assertEquals(ManagerParseShow.VARIABLES,\n                ManagerParseShow.parse(\" /**111**/ SHOW variables\", \" /** 111**/show\".length()));\n    }\n\n    @Test\n    public void testStop() {\n        Assert.assertEquals(ManagerParse.STOP, 0xff & ManagerParse.parse(\"stop @@\"));\n        Assert.assertEquals(ManagerParse.STOP, 0xff & ManagerParse.parse(\" STOP \"));\n    }\n\n    @Test\n    public void testStopHeartBeat() {\n        Assert.assertEquals(ManagerParseStop.HEARTBEAT, ManagerParseStop.parse(\"stop @@heartbeat ds:1000\", 4));\n        Assert.assertEquals(ManagerParseStop.HEARTBEAT, ManagerParseStop.parse(\" STOP  @@HEARTBEAT ds:1000\", 5));\n        Assert.assertEquals(ManagerParseStop.HEARTBEAT, ManagerParseStop.parse(\" STOP  @@heartbeat ds:1000\", 5));\n    }\n\n    @Test\n    public void testReload() {\n        Assert.assertEquals(ManagerParse.RELOAD, 0xff & ManagerParse.parse(\"reload @@\"));\n        Assert.assertEquals(ManagerParse.RELOAD, 0xff & ManagerParse.parse(\" RELOAD \"));\n    }\n\n    @Test\n    public void testReloadConfig() {\n        Assert.assertEquals(ManagerParseReload.CONFIG, ManagerParseReload.parse(\"reload @@config\", 7));\n        Assert.assertEquals(ManagerParseReload.CONFIG, ManagerParseReload.parse(\" RELOAD  @@CONFIG \", 7));\n        Assert.assertEquals(ManagerParseReload.CONFIG, ManagerParseReload.parse(\" RELOAD  @@config \", 7));\n    }\n\n    @Test\n    public void testReloadRoute() {\n        Assert.assertEquals(ManagerParseReload.ROUTE, ManagerParseReload.parse(\"reload @@route\", 7));\n        Assert.assertEquals(ManagerParseReload.ROUTE, ManagerParseReload.parse(\" RELOAD  @@ROUTE \", 7));\n        Assert.assertEquals(ManagerParseReload.ROUTE, ManagerParseReload.parse(\" RELOAD  @@route \", 7));\n    }\n\n    @Test\n    public void testReloadUser() {\n        Assert.assertEquals(ManagerParseReload.USER, ManagerParseReload.parse(\"reload @@user\", 7));\n        Assert.assertEquals(ManagerParseReload.USER, ManagerParseReload.parse(\" RELOAD  @@USER \", 7));\n        Assert.assertEquals(ManagerParseReload.USER, ManagerParseReload.parse(\" RELOAD  @@user \", 7));\n    }\n\n    @Test\n    public void testRollback() {\n        Assert.assertEquals(ManagerParse.ROLLBACK, 0xff & ManagerParse.parse(\"rollback @@\"));\n        Assert.assertEquals(ManagerParse.ROLLBACK, 0xff & ManagerParse.parse(\" ROLLBACK \"));\n    }\n\n    @Test\n    public void testOnOff() {\n        Assert.assertEquals(ManagerParse.ONLINE, ManagerParse.parse(\"online \"));\n        Assert.assertEquals(ManagerParse.ONLINE, ManagerParse.parse(\" Online\"));\n        Assert.assertEquals(ManagerParse.OTHER, ManagerParse.parse(\" Online2\"));\n        Assert.assertEquals(ManagerParse.OTHER, ManagerParse.parse(\"Online2 \"));\n        Assert.assertEquals(ManagerParse.OFFLINE, ManagerParse.parse(\" Offline\"));\n        Assert.assertEquals(ManagerParse.OFFLINE, ManagerParse.parse(\"offLine\\t\"));\n        Assert.assertEquals(ManagerParse.OTHER, ManagerParse.parse(\"onLin\"));\n        Assert.assertEquals(ManagerParse.OTHER, ManagerParse.parse(\" onlin\"));\n    }\n\n    @Test\n    public void testRollbackConfig() {\n        Assert.assertEquals(ManagerParseRollback.CONFIG, ManagerParseRollback.parse(\"rollback @@config\", 8));\n        Assert.assertEquals(ManagerParseRollback.CONFIG, ManagerParseRollback.parse(\" ROLLBACK  @@CONFIG \", 9));\n        Assert.assertEquals(ManagerParseRollback.CONFIG, ManagerParseRollback.parse(\" ROLLBACK  @@config \", 9));\n    }\n\n    @Test\n    public void testRollbackUser() {\n        Assert.assertEquals(ManagerParseRollback.USER, ManagerParseRollback.parse(\"rollback @@user\", 9));\n        Assert.assertEquals(ManagerParseRollback.USER, ManagerParseRollback.parse(\" ROLLBACK  @@USER \", 9));\n        Assert.assertEquals(ManagerParseRollback.USER, ManagerParseRollback.parse(\" ROLLBACK  @@user \", 9));\n    }\n\n    @Test\n    public void testRollbackRoute() {\n        Assert.assertEquals(ManagerParseRollback.ROUTE, ManagerParseRollback.parse(\"rollback @@route\", 9));\n        Assert.assertEquals(ManagerParseRollback.ROUTE, ManagerParseRollback.parse(\" ROLLBACK  @@ROUTE \", 9));\n        Assert.assertEquals(ManagerParseRollback.ROUTE, ManagerParseRollback.parse(\" ROLLBACK  @@route \", 9));\n    }\n\n    @Test\n    public void testGetWhere() {\n        Assert.assertEquals(\"123\", ManagerParseShow.getWhereParameter(\"where id = 123\"));\n        Assert.assertEquals(\"datanode\", ManagerParseShow.getWhereParameter(\"where datanode =    datanode\"));\n        Assert.assertEquals(\"schema\", ManagerParseShow.getWhereParameter(\"where schema =schema   \"));\n    }\n\n    @Test\n    public void testShowSlowSchema() {\n        Assert.assertEquals(ManagerParseShow.SLOW_SCHEMA,\n                0xff & ManagerParseShow.parse(\"show @@slow where schema=a\", 5));\n        Assert.assertEquals(ManagerParseShow.SLOW_SCHEMA,\n                0xff & ManagerParseShow.parse(\"  SHOW @@SLOW   WHERE SCHEMA=B\", 6));\n        Assert.assertEquals(ManagerParseShow.SLOW_SCHEMA,\n                0xff & ManagerParseShow.parse(\" show @@slow  WHERE  SCHEMA  = a \", 5));\n    }\n\n    @Test\n    public void testShowSlowDataNode() {\n        Assert.assertEquals(ManagerParseShow.SLOW_DATANODE,\n                0xff & ManagerParseShow.parse(\"show @@slow where datanode= a\", 5));\n        Assert.assertEquals(ManagerParseShow.SLOW_DATANODE,\n                0xff & ManagerParseShow.parse(\"SHOW @@SLOW WHERE DATANODE= A\", 5));\n        Assert.assertEquals(ManagerParseShow.SLOW_DATANODE,\n                0xff & ManagerParseShow.parse(\" show @@SLOW where DATANODE= b \", 5));\n    }\n\n    @Test\n    public void testclearSlowSchema() {\n        Assert.assertEquals(ManagerParseClear.SLOW_SCHEMA,\n                0xff & ManagerParseClear.parse(\"clear @@slow where schema=s\", 5));\n        Assert.assertEquals(ManagerParseClear.SLOW_SCHEMA,\n                0xff & ManagerParseClear.parse(\"CLEAR @@SLOW WHERE SCHEMA= S\", 5));\n        Assert.assertEquals(ManagerParseClear.SLOW_SCHEMA,\n                0xff & ManagerParseClear.parse(\"CLEAR @@slow where SCHEMA= s\", 5));\n    }\n\n    @Test\n    public void testclearSlowDataNode() {\n        Assert.assertEquals(ManagerParseClear.SLOW_DATANODE,\n                0xff & ManagerParseClear.parse(\"clear @@slow where datanode=d\", 5));\n        Assert.assertEquals(ManagerParseClear.SLOW_DATANODE,\n                0xff & ManagerParseClear.parse(\"CLEAR @@SLOW WHERE DATANODE= D\", 5));\n        Assert.assertEquals(ManagerParseClear.SLOW_DATANODE,\n                0xff & ManagerParseClear.parse(\"clear @@SLOW where  DATANODE= d\", 5));\n    }\n    @Test\n    public void testHeartBearDetail() {\n        Assert.assertEquals(ManagerParseShow.HEARTBEAT_DETAIL,\n                0xff & ManagerParseShow.parse(\"show @@heartbeat.detail where name=master\",5)); \n    }\n    @Test\n    public void testSynStatus() {\n        Assert.assertEquals(ManagerParseShow.DATASOURCE_SYNC,\n                0xff & ManagerParseShow.parse(\"show @@datasource.synstatus\",5)); \n    }\n    @Test\n    public void testSynDetail() {\n        Assert.assertEquals(ManagerParseShow.DATASOURCE_SYNC_DETAIL,\n                0xff & ManagerParseShow.parse(\"show @@datasource.syndetail where name=slave\",5)); \n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/parser/ManagerParserTestPerf.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.parser;\n\nimport io.mycat.route.parser.ManagerParse;\n\n/**\n * @author mycat\n */\npublic class ManagerParserTestPerf {\n\n    public void testPerformance() {\n        for (int i = 0; i < 250000; i++) {\n            ManagerParse.parse(\"show databases\");\n            ManagerParse.parse(\"set autocommit=1\");\n            ManagerParse.parse(\" show  @@datasource \");\n            ManagerParse.parse(\"select id,name,value from t\");\n        }\n    }\n\n    public void testPerformanceWhere() {\n        for (int i = 0; i < 500000; i++) {\n            ManagerParse.parse(\" show  @@datasource where datanode = 1\");\n            ManagerParse.parse(\" show  @@datanode where schema = 1\");\n        }\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/parser/Performance.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.parser;\n\n/**\n * @author mycat\n */\npublic interface Performance {\n    String SQL_BENCHMARK_SELECT = \" seLEcT id, member_id , image_path  \\t , image_size , STATUS,   gmt_modified from    wp_image wheRe \\t\\t\\n id =  ? AND member_id\\t=\\t-123.456\";\n    // String SQL_BENCHMARK_SELECT =\n    // \"select ID, GMT_CREATE, GMT_MODIFIED, INBOX_FOLDER_ID, MESSAGE_ID,             FEEDBACK_TYPE, TARGET_ID,               TRADE_ID, SUBJECT, SENDER_ID, SENDER_TYPE,              S_DISPLAY_NAME, SENDER_STATUS, RECEIVER_ID, RECEIVER_TYPE,              R_DISPLAY_NAME, RECEIVER_STATUS, SPAM_STATUS, REPLY_STATUS,             ATTACHMENT_STATUS,              SENDER_COUNTRY,                 RECEIVER_COUNTRY,APP_FROM,APP_TO,APP_SOURCE,SENDER_VACOUNT,RECEIVER_VACOUNT,            DISTRIBUTE_STATUS,ORG_RECEIVER_ID,CUSTOMER_ID,OPERATOR_ID,OPERATOR_NAME,FOLLOW_STATUS,DELETE_STATUS,FOLLOW_TIME,BATCH_COUNT             from MESSAGE_REC_RECORD                 where RECEIVER_VACOUNT          =? and ID = ?\";\n    String SQL_BENCHMARK_EXPR_SELECT = \"( seLect id, member_id , image_path  \\t , image_size , STATUS,   gmt_modified from    wp_image where \\t\\t\\n id =  ? and member_id\\t=\\t?)\";\n}"
  },
  {
    "path": "src/test/java/io/mycat/parser/ServerParseTest.java",
    "content": "package io.mycat.parser;\n\nimport junit.framework.Assert;\n\nimport org.junit.Test;\n\nimport io.mycat.server.parser.ServerParse;\n\npublic class ServerParseTest {\n\t/**\n\t * public static final int OTHER = -1;\n\tpublic static final int BEGIN = 1;\n\tpublic static final int COMMIT = 2;\n\tpublic static final int DELETE = 3;\n\tpublic static final int INSERT = 4;\n\tpublic static final int REPLACE = 5;\n\tpublic static final int ROLLBACK = 6;\n\tpublic static final int SELECT = 7;\n\tpublic static final int SET = 8;\n\tpublic static final int SHOW = 9;\n\tpublic static final int START = 10;\n\tpublic static final int UPDATE = 11;\n\tpublic static final int KILL = 12;\n\tpublic static final int SAVEPOINT = 13;\n\tpublic static final int USE = 14;\n\tpublic static final int EXPLAIN = 15;\n\tpublic static final int KILL_QUERY = 16;\n\tpublic static final int HELP = 17;\n\tpublic static final int MYSQL_CMD_COMMENT = 18;\n\tpublic static final int MYSQL_COMMENT = 19;\n\tpublic static final int CALL = 20;\n\tpublic static final int DESCRIBE = 21;\n\t */\n\n\t@Test\n\tpublic void testDesc() {\n\t\tString sql = \"desc a\";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.DESCRIBE, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testDescribe() {\n\t\tString sql = \"describe a\";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.DESCRIBE, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testDelete() {\n\t\tString sql = \"delete from a where id = 1\";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.DELETE, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testInsert() {\n\t\tString sql = \"insert into a(name) values ('zhangsan')\";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.INSERT, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testReplace() {\n\t\tString sql = \"replace into t(id, update_time) select 1, now();  \";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.REPLACE, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testSet() {\n\t\tString sql = \"SET @var_name = 'value';  \";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.SET, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testShow() {\n\t\tString sql = \"show full tables\";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.SHOW, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testStart() {\n\t\tString sql = \"start \";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.START, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testUpdate() {\n\t\tString sql = \"update a set name='wdw' where id = 1\";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.UPDATE, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testKill() {\n\t\tString sql = \"kill 1\";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.KILL, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testSavePoint() {\n\t\tString sql = \"SAVEPOINT \";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.SAVEPOINT, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testUse() {\n\t\tString sql = \"use db1 \";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.USE, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testExplain() {\n\t\tString sql = \"explain select * from a \";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.EXPLAIN, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testKillQuery() {\n\t\tString sql = \"kill query 1102 \";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.KILL_QUERY, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testHelp() {\n\t\tString sql = \"help contents \";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.HELP, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testMysqlCmdComment() {\n\t\t\n\t}\n\t\n\t@Test\n\tpublic void testMysqlComment() {\n\t\t\n\t}\n\t\n\t@Test\n\tpublic void testCall() {\n\t\tString sql = \"CALL demo_in_parameter(@p_in); \";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.CALL, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testRollback() {\n\t\tString sql = \"rollback; \";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.ROLLBACK, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testSelect() {\n\t\tString sql = \"select * from a\";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.SELECT, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testBegin() {\n\t\tString sql = \"begin\";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.BEGIN, sqlType);\n\t}\n\t\n\t@Test\n\tpublic void testCommit() {\n\t\tString sql = \"COMMIT 'nihao'\";\n\t\tint result = ServerParse.parse(sql);\n\t\tint sqlType = result & 0xff;\n\t\tAssert.assertEquals(ServerParse.COMMIT, sqlType);\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/parser/ServerParserTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.parser;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.server.parser.ServerParseSelect;\nimport io.mycat.server.parser.ServerParseSet;\nimport io.mycat.server.parser.ServerParseShow;\nimport io.mycat.server.parser.ServerParseStart;\n\n/**\n * @author mycat\n */\npublic class ServerParserTest {\n\n    @Test\n    public void testIsBegin() {\n        Assert.assertEquals(ServerParse.BEGIN, ServerParse.parse(\"begin\"));\n        Assert.assertEquals(ServerParse.BEGIN, ServerParse.parse(\"BEGIN\"));\n        Assert.assertEquals(ServerParse.BEGIN, ServerParse.parse(\"BegIn\"));\n    }\n\n    @Test\n    public void testIsCommit() {\n        Assert.assertEquals(ServerParse.COMMIT, ServerParse.parse(\"commit\"));\n        Assert.assertEquals(ServerParse.COMMIT, ServerParse.parse(\"COMMIT\"));\n        Assert.assertEquals(ServerParse.COMMIT, ServerParse.parse(\"cOmmiT \"));\n    }\n    \n\n    @Test\n    public void testComment() {\n        Assert.assertEquals(ServerParse.MYSQL_CMD_COMMENT, ServerParse.parse(\"/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */\"));\n        Assert.assertEquals(ServerParse.MYSQL_CMD_COMMENT, ServerParse.parse(\"/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */\"));\n        Assert.assertEquals(ServerParse.MYSQL_CMD_COMMENT, ServerParse.parse(\"/*!40101 SET @saved_cs_client     = @@character_set_client */\"));\n   \n        Assert.assertEquals(ServerParse.MYSQL_COMMENT, ServerParse.parse(\"/*SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */\"));\n        Assert.assertEquals(ServerParse.MYSQL_COMMENT, ServerParse.parse(\"/*SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */\"));\n        Assert.assertEquals(ServerParse.MYSQL_COMMENT, ServerParse.parse(\"/*SET @saved_cs_client     = @@character_set_client */\"));\n    }\n\n    @Test\n    public void testMycatComment() {\n        Assert.assertEquals(ServerParse.SELECT, 0xff & ServerParse.parse(\"/*#mycat:schema=DN1*/SELECT ...\"));\n        Assert.assertEquals(ServerParse.UPDATE, 0xff & ServerParse.parse(\"/*#mycat: schema = DN1 */ UPDATE ...\"));\n        Assert.assertEquals(ServerParse.DELETE, 0xff & ServerParse.parse(\"/*#mycat: sql = SELECT id FROM user */ DELETE ...\"));\n    }\n\n    @Test\n    public void testOldMycatComment() {\n        Assert.assertEquals(ServerParse.SELECT, 0xff & ServerParse.parse(\"/*!mycat:schema=DN1*/SELECT ...\"));\n        Assert.assertEquals(ServerParse.UPDATE, 0xff & ServerParse.parse(\"/*!mycat: schema = DN1 */ UPDATE ...\"));\n        Assert.assertEquals(ServerParse.DELETE, 0xff & ServerParse.parse(\"/*!mycat: sql = SELECT id FROM user */ DELETE ...\"));\n    }\n\n    @Test\n    public void testIsDelete() {\n        Assert.assertEquals(ServerParse.DELETE, ServerParse.parse(\"delete ...\"));\n        Assert.assertEquals(ServerParse.DELETE, ServerParse.parse(\"DELETE ...\"));\n        Assert.assertEquals(ServerParse.DELETE, ServerParse.parse(\"DeletE ...\"));\n    }\n\n    @Test\n    public void testIsInsert() {\n        Assert.assertEquals(ServerParse.INSERT, ServerParse.parse(\"insert ...\"));\n        Assert.assertEquals(ServerParse.INSERT, ServerParse.parse(\"INSERT ...\"));\n        Assert.assertEquals(ServerParse.INSERT, ServerParse.parse(\"InserT ...\"));\n    }\n\n    @Test\n    public void testIsReplace() {\n        Assert.assertEquals(ServerParse.REPLACE, ServerParse.parse(\"replace ...\"));\n        Assert.assertEquals(ServerParse.REPLACE, ServerParse.parse(\"REPLACE ...\"));\n        Assert.assertEquals(ServerParse.REPLACE, ServerParse.parse(\"rEPLACe ...\"));\n    }\n\n    @Test\n    public void testIsRollback() {\n        Assert.assertEquals(ServerParse.ROLLBACK, ServerParse.parse(\"rollback\"));\n        Assert.assertEquals(ServerParse.ROLLBACK, ServerParse.parse(\"ROLLBACK\"));\n        Assert.assertEquals(ServerParse.ROLLBACK, ServerParse.parse(\"rolLBACK \"));\n    }\n\n    @Test\n    public void testIsSelect() {\n        Assert.assertEquals(ServerParse.SELECT, 0xff & ServerParse.parse(\"select ...\"));\n        Assert.assertEquals(ServerParse.SELECT, 0xff & ServerParse.parse(\"SELECT ...\"));\n        Assert.assertEquals(ServerParse.SELECT, 0xff & ServerParse.parse(\"sELECt ...\"));\n    }\n\n    @Test\n    public void testIsSet() {\n        Assert.assertEquals(ServerParse.SET, 0xff & ServerParse.parse(\"set ...\"));\n        Assert.assertEquals(ServerParse.SET, 0xff & ServerParse.parse(\"SET ...\"));\n        Assert.assertEquals(ServerParse.SET, 0xff & ServerParse.parse(\"sEt ...\"));\n    }\n\n    @Test\n    public void testIsShow() {\n        Assert.assertEquals(ServerParse.SHOW, 0xff & ServerParse.parse(\"show ...\"));\n        Assert.assertEquals(ServerParse.SHOW, 0xff & ServerParse.parse(\"SHOW ...\"));\n        Assert.assertEquals(ServerParse.SHOW, 0xff & ServerParse.parse(\"sHOw ...\"));\n    }\n\n    @Test\n    public void testIsStart() {\n        Assert.assertEquals(ServerParse.START, 0xff & ServerParse.parse(\"start ...\"));\n        Assert.assertEquals(ServerParse.START, 0xff & ServerParse.parse(\"START ...\"));\n        Assert.assertEquals(ServerParse.START, 0xff & ServerParse.parse(\"stART ...\"));\n    }\n\n    @Test\n    public void testIsUpdate() {\n        Assert.assertEquals(ServerParse.UPDATE, ServerParse.parse(\"update ...\"));\n        Assert.assertEquals(ServerParse.UPDATE, ServerParse.parse(\"UPDATE ...\"));\n        Assert.assertEquals(ServerParse.UPDATE, ServerParse.parse(\"UPDate ...\"));\n    }\n\n    @Test\n    public void testIsShowDatabases() {\n        Assert.assertEquals(ServerParseShow.DATABASES, ServerParseShow.parse(\"show databases\", 4));\n        Assert.assertEquals(ServerParseShow.DATABASES, ServerParseShow.parse(\"SHOW DATABASES\", 4));\n        Assert.assertEquals(ServerParseShow.DATABASES, ServerParseShow.parse(\"SHOW databases \", 4));\n    }\n\n    @Test\n    public void testIsShowDataSources() {\n        Assert.assertEquals(ServerParseShow.DATASOURCES, ServerParseShow.parse(\"show datasources\", 4));\n        Assert.assertEquals(ServerParseShow.DATASOURCES, ServerParseShow.parse(\"SHOW DATASOURCES\", 4));\n        Assert.assertEquals(ServerParseShow.DATASOURCES, ServerParseShow.parse(\"  SHOW   DATASOURCES  \", 6));\n    }\n\n    @Test\n    public void testShowMycatStatus() {\n        Assert.assertEquals(ServerParseShow.MYCAT_STATUS, ServerParseShow.parse(\"show mycat_status\", 4));\n        Assert.assertEquals(ServerParseShow.MYCAT_STATUS, ServerParseShow.parse(\"show mycat_status \", 4));\n        Assert.assertEquals(ServerParseShow.MYCAT_STATUS, ServerParseShow.parse(\" SHOW MYCAT_STATUS\", \" SHOW\".length()));\n        Assert.assertEquals(ServerParseShow.OTHER, ServerParseShow.parse(\" show mycat_statu\", \" SHOW\".length()));\n        Assert.assertEquals(ServerParseShow.OTHER, ServerParseShow.parse(\" show mycat_status2\", \" SHOW\".length()));\n        Assert.assertEquals(ServerParseShow.OTHER, ServerParseShow.parse(\"Show mycat_status2 \", \"SHOW\".length()));\n    }\n\n    @Test\n    public void testShowMycatCluster() {\n        Assert.assertEquals(ServerParseShow.MYCAT_CLUSTER, ServerParseShow.parse(\"show mycat_cluster\", 4));\n        Assert.assertEquals(ServerParseShow.MYCAT_CLUSTER, ServerParseShow.parse(\"Show mycat_CLUSTER \", 4));\n        Assert.assertEquals(ServerParseShow.MYCAT_CLUSTER, ServerParseShow.parse(\" show  MYCAT_cluster\", 5));\n        Assert.assertEquals(ServerParseShow.OTHER, ServerParseShow.parse(\" show mycat_clust\", 5));\n        Assert.assertEquals(ServerParseShow.OTHER, ServerParseShow.parse(\" show mycat_cluster2\", 5));\n        Assert.assertEquals(ServerParseShow.OTHER, ServerParseShow.parse(\"Show mycat_cluster9 \", 4));\n    }\n\n    @Test\n    public void testIsShowOther() {\n        Assert.assertEquals(ServerParseShow.OTHER, ServerParseShow.parse(\"show ...\", 4));\n        Assert.assertEquals(ServerParseShow.OTHER, ServerParseShow.parse(\"SHOW ...\", 4));\n        Assert.assertEquals(ServerParseShow.OTHER, ServerParseShow.parse(\"SHOW ... \", 4));\n    }\n\n    @Test\n    public void testIsSetAutocommitOn() {\n        Assert.assertEquals(ServerParseSet.AUTOCOMMIT_ON, ServerParseSet.parse(\"set autocommit=1\", 3));\n        Assert.assertEquals(ServerParseSet.AUTOCOMMIT_ON, ServerParseSet.parse(\"set autoCOMMIT = 1\", 3));\n        Assert.assertEquals(ServerParseSet.AUTOCOMMIT_ON, ServerParseSet.parse(\"SET AUTOCOMMIT=on\", 3));\n        Assert.assertEquals(ServerParseSet.AUTOCOMMIT_ON, ServerParseSet.parse(\"set autoCOMMIT = ON\", 3));\n    }\n    @Test\n    public void testIsSetAutocommitOn2() {\n        Assert.assertEquals(ServerParseSet.AUTOCOMMIT_ON, ServerParseSet.parse(\"set @@session.autoCOMMIT = ON\", 3));\n    }\n    @Test\n    public void testIsSetAutocommitOff() {\n        Assert.assertEquals(ServerParseSet.AUTOCOMMIT_OFF, ServerParseSet.parse(\"set autocommit=0\", 3));\n        Assert.assertEquals(ServerParseSet.AUTOCOMMIT_OFF, ServerParseSet.parse(\"SET AUTOCOMMIT= 0\", 3));\n        Assert.assertEquals(ServerParseSet.AUTOCOMMIT_OFF, ServerParseSet.parse(\"set autoCOMMIT =OFF\", 3));\n        Assert.assertEquals(ServerParseSet.AUTOCOMMIT_OFF, ServerParseSet.parse(\"set autoCOMMIT = off\", 3));\n        Assert.assertEquals(ServerParseSet.AUTOCOMMIT_OFF, ServerParseSet.parse(\"set @@session.autocommit  =OFF\", 3));\n    }\n    @Test\n    public void testIsSetAutocommitOff2() {\n        Assert.assertEquals(ServerParseSet.AUTOCOMMIT_OFF, ServerParseSet.parse(\"set @@session.autocommit  =OFF\", 3));\n    }\n\n    @Test\n    public void testIsSetNames() {\n        Assert.assertEquals(ServerParseSet.NAMES, 0xff & ServerParseSet.parse(\"set names utf8\", 3));\n        Assert.assertEquals(ServerParseSet.NAMES, 0xff & ServerParseSet.parse(\"SET NAMES UTF8\", 3));\n        Assert.assertEquals(ServerParseSet.NAMES, 0xff & ServerParseSet.parse(\"set NAMES utf8\", 3));\n    }\n\n    @Test\n    public void testIsCharacterSetResults() {\n        Assert.assertEquals(ServerParseSet.CHARACTER_SET_RESULTS,\n                0xff & ServerParseSet.parse(\"SET character_set_results  = NULL\", 3));\n        Assert.assertEquals(ServerParseSet.CHARACTER_SET_RESULTS,\n                0xff & ServerParseSet.parse(\"SET CHARACTER_SET_RESULTS= NULL\", 3));\n        Assert.assertEquals(ServerParseSet.CHARACTER_SET_RESULTS,\n                0xff & ServerParseSet.parse(\"Set chARActer_SET_RESults =  NULL\", 3));\n        Assert.assertEquals(ServerParseSet.CHARACTER_SET_CONNECTION,\n                0xff & ServerParseSet.parse(\"Set chARActer_SET_Connection =  NULL\", 3));\n        Assert.assertEquals(ServerParseSet.CHARACTER_SET_CLIENT,\n                0xff & ServerParseSet.parse(\"Set chARActer_SET_client =  NULL\", 3));\n    }\n\n    @Test\n    public void testIsSetOther() {\n        Assert.assertEquals(ServerParseSet.OTHER, ServerParseSet.parse(\"set ...\", 3));\n        Assert.assertEquals(ServerParseSet.OTHER, ServerParseSet.parse(\"SET ...\", 3));\n        Assert.assertEquals(ServerParseSet.OTHER, ServerParseSet.parse(\"sEt ...\", 3));\n    }\n\n    @Test\n    public void testIsKill() {\n        Assert.assertEquals(ServerParse.KILL, 0xff & ServerParse.parse(\" kill  ...\"));\n        Assert.assertEquals(ServerParse.KILL, 0xff & ServerParse.parse(\"kill 111111 ...\"));\n        Assert.assertEquals(ServerParse.KILL, 0xff & ServerParse.parse(\"KILL  1335505632\"));\n    }\n\n    @Test\n    public void testIsKillQuery() {\n        Assert.assertEquals(ServerParse.KILL_QUERY, 0xff & ServerParse.parse(\" kill query ...\"));\n        Assert.assertEquals(ServerParse.KILL_QUERY, 0xff & ServerParse.parse(\"kill   query 111111 ...\"));\n        Assert.assertEquals(ServerParse.KILL_QUERY, 0xff & ServerParse.parse(\"KILL QUERY 1335505632\"));\n    }\n\n    @Test\n    public void testIsSavepoint() {\n        Assert.assertEquals(ServerParse.SAVEPOINT, ServerParse.parse(\" savepoint  ...\"));\n        Assert.assertEquals(ServerParse.SAVEPOINT, ServerParse.parse(\"SAVEPOINT \"));\n        Assert.assertEquals(ServerParse.SAVEPOINT, ServerParse.parse(\" SAVEpoint   a\"));\n    }\n\n    @Test\n    public void testIsUse() {\n        Assert.assertEquals(ServerParse.USE, 0xff & ServerParse.parse(\" use  ...\"));\n        Assert.assertEquals(ServerParse.USE, 0xff & ServerParse.parse(\"USE \"));\n        Assert.assertEquals(ServerParse.USE, 0xff & ServerParse.parse(\" Use   a\"));\n    }\n\n    @Test\n    public void testIsStartTransaction() {\n        Assert.assertEquals(ServerParseStart.TRANSACTION, ServerParseStart.parse(\" start transaction  ...\", 6));\n        Assert.assertEquals(ServerParseStart.TRANSACTION, ServerParseStart.parse(\"START TRANSACTION\", 5));\n        Assert.assertEquals(ServerParseStart.TRANSACTION, ServerParseStart.parse(\" staRT   TRANSaction  \", 6));\n    }\n\n    @Test\n    public void testIsSelectVersionComment() {\n        Assert.assertEquals(ServerParseSelect.VERSION_COMMENT,\n                ServerParseSelect.parse(\" select @@version_comment  \", 7));\n        Assert.assertEquals(ServerParseSelect.VERSION_COMMENT, ServerParseSelect.parse(\"SELECT @@VERSION_COMMENT\", 6));\n        Assert.assertEquals(ServerParseSelect.VERSION_COMMENT,\n                ServerParseSelect.parse(\" selECT    @@VERSION_comment  \", 7));\n    }\n\n    @Test\n    public void testIsSelectVersion() {\n        Assert.assertEquals(ServerParseSelect.VERSION, ServerParseSelect.parse(\" select version ()  \", 7));\n        Assert.assertEquals(ServerParseSelect.VERSION, ServerParseSelect.parse(\"SELECT VERSION(  )\", 6));\n        Assert.assertEquals(ServerParseSelect.VERSION, ServerParseSelect.parse(\" selECT    VERSION()  \", 7));\n    }\n\n    @Test\n    public void testIsSelectDatabase() {\n        Assert.assertEquals(ServerParseSelect.DATABASE, ServerParseSelect.parse(\" select database()  \", 7));\n        Assert.assertEquals(ServerParseSelect.DATABASE, ServerParseSelect.parse(\"SELECT DATABASE()\", 6));\n        Assert.assertEquals(ServerParseSelect.DATABASE, ServerParseSelect.parse(\" selECT    DATABASE()  \", 7));\n    }\n\n    @Test\n    public void testIsSelectUser() {\n        Assert.assertEquals(ServerParseSelect.USER, ServerParseSelect.parse(\" select user()  \", 7));\n        Assert.assertEquals(ServerParseSelect.USER, ServerParseSelect.parse(\"SELECT USER()\", 6));\n        Assert.assertEquals(ServerParseSelect.USER, ServerParseSelect.parse(\" selECT    USER()  \", 7));\n    }\n\n    @Test\n    public void testTxReadUncommitted() {\n        Assert.assertEquals(ServerParseSet.TX_READ_UNCOMMITTED,\n                ServerParseSet.parse(\"  SET SESSION TRANSACTION ISOLATION LEVEL READ  UNCOMMITTED  \", \"  SET\".length()));\n        Assert.assertEquals(ServerParseSet.TX_READ_UNCOMMITTED,\n                ServerParseSet.parse(\" set session transaction isolation level read  uncommitted  \", \" SET\".length()));\n        Assert.assertEquals(ServerParseSet.TX_READ_UNCOMMITTED,\n                ServerParseSet.parse(\" set session transaCTION ISOLATION LEvel read  uncommitteD \", \" SET\".length()));\n    }\n\n    @Test\n    public void testTxReadCommitted() {\n        Assert.assertEquals(ServerParseSet.TX_READ_COMMITTED,\n                ServerParseSet.parse(\"  SET SESSION TRANSACTION ISOLATION LEVEL READ  COMMITTED  \", \"  SET\".length()));\n        Assert.assertEquals(ServerParseSet.TX_READ_COMMITTED,\n                ServerParseSet.parse(\" set session transaction isolation level read  committed  \", \" SET\".length()));\n        Assert.assertEquals(ServerParseSet.TX_READ_COMMITTED,\n                ServerParseSet.parse(\" set session transaCTION ISOLATION LEVel read  committed \", \" SET\".length()));\n    }\n\n    @Test\n    public void testTxReadOnly() {\n        Assert.assertEquals(ServerParseSet.TX_READONLY,\n            ServerParseSet.parse(\"  SET SESSION TRANSACTION READ ONLY  \", \"  SET\".length()));\n        Assert.assertEquals(ServerParseSet.TX_READONLY,\n            ServerParseSet.parse(\" set session transaction read only  \", \" SET\".length()));\n        Assert.assertEquals(ServerParseSet.TX_READONLY,\n            ServerParseSet.parse(\" set session transaCTION Read Only \", \" SET\".length()));\n    }\n\n    @Test\n    public void testSqlSelectLimit() {\n        Assert.assertEquals(ServerParseSet.SQL_SELECT_LIMIT,\n            ServerParseSet.parse(\"  SET SQL_SELECT_LIMIT=1  \", \"  SET\".length()));\n        Assert.assertEquals(ServerParseSet.SQL_SELECT_LIMIT,\n            ServerParseSet.parse(\" set sql_select_limit=1  \", \" SET\".length()));\n        Assert.assertEquals(ServerParseSet.SQL_SELECT_LIMIT,\n            ServerParseSet.parse(\" Set Sql_Select_Limit=1 \", \" SET\".length()));\n    }\n\n    @Test\n    public void testTxReadWrite() {\n        Assert.assertEquals(ServerParseSet.TX_READWRITE,\n            ServerParseSet.parse(\"  SET SESSION TRANSACTION READ WRITE  \", \"  SET\".length()));\n        Assert.assertEquals(ServerParseSet.TX_READWRITE,\n            ServerParseSet.parse(\" set session transaction read write  \", \" SET\".length()));\n        Assert.assertEquals(ServerParseSet.TX_READWRITE,\n            ServerParseSet.parse(\" set session transaCTION Read Write \", \" SET\".length()));\n    }\n\n    @Test\n    public void testTxRepeatedRead() {\n        Assert.assertEquals(ServerParseSet.TX_REPEATED_READ,\n                ServerParseSet.parse(\"  SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE   READ  \", \"  SET\".length()));\n        Assert.assertEquals(ServerParseSet.TX_REPEATED_READ,\n                ServerParseSet.parse(\" set session transaction isolation level repeatable   read  \", \" SET\".length()));\n        Assert.assertEquals(ServerParseSet.TX_REPEATED_READ,\n                ServerParseSet.parse(\" set session transaction isOLATION LEVEL REPEatable   read \", \" SET\".length()));\n    }\n\n    @Test\n    public void testTxSerializable() {\n        Assert.assertEquals(ServerParseSet.TX_SERIALIZABLE,\n                ServerParseSet.parse(\"  SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE  \", \"  SET\".length()));\n        Assert.assertEquals(ServerParseSet.TX_SERIALIZABLE,\n                ServerParseSet.parse(\" set session transaction   isolation level serializable  \", \" SET\".length()));\n        Assert.assertEquals(ServerParseSet.TX_SERIALIZABLE,\n                ServerParseSet.parse(\" set session   transaction  isOLATION LEVEL SERIAlizable \", \" SET\".length()));\n    }\n\n    @Test\n    public void testIdentity() {\n        String stmt = \"select @@identity\";\n        int indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterIdentity(stmt, stmt.indexOf('i'));\n        Assert.assertEquals(stmt.length(), indexAfterLastInsertIdFunc);\n        Assert.assertEquals(ServerParseSelect.IDENTITY, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select  @@identity as id\";\n        Assert.assertEquals(ServerParseSelect.IDENTITY, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select  @@identitY  id\";\n        Assert.assertEquals(ServerParseSelect.IDENTITY, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select  /*foo*/@@identitY  id\";\n        Assert.assertEquals(ServerParseSelect.IDENTITY, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select/*foo*/ @@identitY  id\";\n        Assert.assertEquals(ServerParseSelect.IDENTITY, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select/*foo*/ @@identitY As id\";\n        Assert.assertEquals(ServerParseSelect.IDENTITY, ServerParseSelect.parse(stmt, 6));\n\n        stmt = \"select  @@identity ,\";\n        Assert.assertEquals(ServerParseSelect.OTHER, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select  @@identity as, \";\n        Assert.assertEquals(ServerParseSelect.OTHER, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select  @@identity as id  , \";\n        Assert.assertEquals(ServerParseSelect.OTHER, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select  @@identity ass id   \";\n        Assert.assertEquals(ServerParseSelect.OTHER, ServerParseSelect.parse(stmt, 6));\n\n    }\n\n    @Test\n    public void testLastInsertId() {\n        String stmt = \" last_insert_iD()\";\n        int indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(stmt.length(), indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_iD ()\";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(stmt.length(), indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_iD ( /**/ )\";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(stmt.length(), indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_iD (  )  \";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(stmt.lastIndexOf(')') + 1, indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_id(  )\";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(stmt.lastIndexOf(')') + 1, indexAfterLastInsertIdFunc);\n        stmt = \"last_iNsert_id(  ) \";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(stmt.lastIndexOf(')') + 1, indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_iD\";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(-1, indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_i     \";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(-1, indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_i    d \";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(-1, indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_id (     \";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(-1, indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_id(  d)     \";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(-1, indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_id(  ) d    \";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(stmt.lastIndexOf(')') + 1, indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_id(d)\";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(-1, indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_id(#\\r\\nd) \";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(-1, indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_id(#\\n\\r) \";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(stmt.lastIndexOf(')') + 1, indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_id (#\\n\\r)\";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(stmt.lastIndexOf(')') + 1, indexAfterLastInsertIdFunc);\n        stmt = \" last_insert_id(#\\n\\r)\";\n        indexAfterLastInsertIdFunc = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, stmt.indexOf('l'));\n        Assert.assertEquals(stmt.lastIndexOf(')') + 1, indexAfterLastInsertIdFunc);\n\n        stmt = \"select last_insert_id(#\\n\\r)\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r) as id\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r) as `id`\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r) as 'id'\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r)  id\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r)  `id`\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r)  'id'\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r) a\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        // NOTE: this should be invalid, ignore this bug\n        stmt = \"select last_insert_id(#\\n\\r) as\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r) asd\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        // NOTE: this should be invalid, ignore this bug\n        stmt = \"select last_insert_id(#\\n\\r) as 777\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        // NOTE: this should be invalid, ignore this bug\n        stmt = \"select last_insert_id(#\\n\\r)  777\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r)as `77``7`\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r)ass\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r)as 'a'\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r)as 'a\\\\''\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r)as 'a'''\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r)as 'a\\\"'\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 6));\n        stmt = \"   select last_insert_id(#\\n\\r) As 'a\\\"'\";\n        Assert.assertEquals(ServerParseSelect.LAST_INSERT_ID, ServerParseSelect.parse(stmt, 9));\n\n        stmt = \"select last_insert_id(#\\n\\r)as 'a\\\"\\\\'\";\n        Assert.assertEquals(ServerParseSelect.OTHER, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r)as `77``7` ,\";\n        Assert.assertEquals(ServerParseSelect.OTHER, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r)as `77`7`\";\n        Assert.assertEquals(ServerParseSelect.OTHER, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r) as,\";\n        Assert.assertEquals(ServerParseSelect.OTHER, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r) ass a\";\n        Assert.assertEquals(ServerParseSelect.OTHER, ServerParseSelect.parse(stmt, 6));\n        stmt = \"select last_insert_id(#\\n\\r) as 'a\";\n        Assert.assertEquals(ServerParseSelect.OTHER, ServerParseSelect.parse(stmt, 6));\n    }\n    \n    @Test\n    public void testLockTable() {\n    \tAssert.assertEquals(ServerParse.LOCK, ServerParse.parse(\"lock tables ttt write;\"));\n    \tAssert.assertEquals(ServerParse.LOCK, ServerParse.parse(\" lock tables ttt read;\"));\n    \tAssert.assertEquals(ServerParse.LOCK, ServerParse.parse(\"lock tables\"));\n    }\n\n    @Test\n    public void testUnlockTable() {\n    \tAssert.assertEquals(ServerParse.UNLOCK, ServerParse.parse(\"unlock tables\"));\n    \tAssert.assertEquals(ServerParse.UNLOCK, ServerParse.parse(\" unlock\t tables\"));\n    }\n    \n    @Test\n    public void testSetXAOn() {\n    \tAssert.assertEquals(ServerParseSet.XA_FLAG_ON, ServerParseSet.parse(\"set xa=on\", 3));\n    \tAssert.assertEquals(ServerParseSet.XA_FLAG_ON, ServerParseSet.parse(\"set xa = on\", 3));\n    \tAssert.assertEquals(ServerParseSet.XA_FLAG_ON, ServerParseSet.parse(\"set xa \\t\\n\\r = \\t\\n\\r on\", 3));\n    }\n    \n    @Test\n    public void testSetXAOff() {\n    \tAssert.assertEquals(ServerParseSet.XA_FLAG_OFF, ServerParseSet.parse(\"set xa=off\", 3));\n    \tAssert.assertEquals(ServerParseSet.XA_FLAG_OFF, ServerParseSet.parse(\"set xa = off\", 3));\n    \tAssert.assertEquals(ServerParseSet.XA_FLAG_OFF, ServerParseSet.parse(\"set xa \\t\\n\\r = \\t\\n\\r off\", 3));\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/parser/ServerParserTestPerf.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.parser;\n\nimport io.mycat.server.parser.ServerParseSet;\n\n/**\n * @author mycat\n */\npublic final class ServerParserTestPerf {\n\n    private static void parseSetPerf() {\n        // ServerParse.parse(\"show databases\");\n        // ServerParseSet.parse(\"set autocommit=1\");\n        // ServerParseSet.parse(\"set names=1\");\n        ServerParseSet.parse(\"SET character_set_results = NULL\", 4);\n        // ServerParse.parse(\"select id,name,value from t\");\n        // ServerParse.parse(\"select * from offer where member_id='abc'\");\n    }\n\n    public static void main(String[] args) {\n        parseSetPerf();\n        int count = 10000000;\n\n        System.currentTimeMillis();\n        long t1 = System.currentTimeMillis();\n        for (int i = 0; i < count; i++) {\n            parseSetPerf();\n        }\n        long t2 = System.currentTimeMillis();\n\n        // print time\n        System.out.println(\"take:\" + ((t2 - t1) * 1000 * 1000) / count + \" ns.\");\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/parser/TestEscapeProcess.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights\n * reserved. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. This\n * code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation. This code is distributed in the hope that it will\n * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General\n * Public License version 2 for more details (a copy is included in the LICENSE\n * file that accompanied this code). You should have received a copy of the GNU\n * General Public License version 2 along with this work; if not, write to the\n * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n * 02110-1301 USA. Any questions about this component can be directed to it's\n * project Web address https://code.google.com/p/opencloudb/.\n */\npackage io.mycat.parser;\n\nimport static org.junit.Assert.assertEquals;\nimport io.mycat.MycatServer;\nimport io.mycat.server.interceptor.impl.DefaultSqlInterceptor;\n\nimport org.junit.Test;\n\npublic class TestEscapeProcess {\n\n\tString sql = \"insert  into t_uud_user_account(USER_ID,USER_NAME,PASSWORD,CREATE_TIME,STATUS,NICK_NAME,USER_ICON_URL,USER_ICON_URL2,USER_ICON_URL3,ACCOUNT_TYPE) \"\n\t\t\t+ \"values (2488899998,'u\\\\'aa\\\\'\\\\'a''aa','af8f9dffa5d420fbc249141645b962ee','2013-12-01 00:00:00',0,NULL,NULL,NULL,NULL,1)\";\n\n\tString sqlret = \"insert  into t_uud_user_account(USER_ID,USER_NAME,PASSWORD,CREATE_TIME,STATUS,NICK_NAME,USER_ICON_URL,USER_ICON_URL2,USER_ICON_URL3,ACCOUNT_TYPE) \"\n\t\t\t+ \"values (2488899998,'u''aa''''a''aa','af8f9dffa5d420fbc249141645b962ee','2013-12-01 00:00:00',0,NULL,NULL,NULL,NULL,1)\";\n\n\tString starWithEscapeSql = \"\\\\insert  into t_uud_user_account(USER_ID,USER_NAME,PASSWORD,CREATE_TIME,STATUS,NICK_NAME,USER_ICON_URL,USER_ICON_URL2,USER_ICON_URL3,ACCOUNT_TYPE) \"\n\t\t\t+ \"values (2488899998,'u\\\\'aa\\\\'\\\\'a''aa','af8f9dffa5d420fbc249141645b962ee','2013-12-01 00:00:00',0,NULL,NULL,NULL,NULL,1)\\\\\";\n\n\tString starWithEscapeSqlret = \"\\\\insert  into t_uud_user_account(USER_ID,USER_NAME,PASSWORD,CREATE_TIME,STATUS,NICK_NAME,USER_ICON_URL,USER_ICON_URL2,USER_ICON_URL3,ACCOUNT_TYPE) \"\n\t\t\t+ \"values (2488899998,'u''aa''''a''aa','af8f9dffa5d420fbc249141645b962ee','2013-12-01 00:00:00',0,NULL,NULL,NULL,NULL,1)\\\\\";\n\n\t\n\t@Test\n\tpublic void testEscapeProcess() {\n\t\tMycatServer.getInstance().getConfig().getSystem().setDefaultSqlParser(\"fdbparser\");\n\t\tString sqlProcessed = DefaultSqlInterceptor.processEscape(sql);\n\t\tassertEquals(sqlProcessed, sqlret);\n\t\tString sqlProcessed1 = DefaultSqlInterceptor\n\t\t\t\t.processEscape(starWithEscapeSql);\n\t\tassertEquals(sqlProcessed1, starWithEscapeSqlret);\n\t}\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/parser/druid/DruidSelectParserTest.java",
    "content": "package io.mycat.parser.druid;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\n\nimport io.mycat.route.parser.druid.impl.DruidSelectParser;\n\n/**\n * Created by Hash Zhang on 2016/4/29.\n * Modified by Hash Zhang on 2016/5/25 add testGroupByWithViewAlias.\n */\npublic class DruidSelectParserTest {\n    DruidSelectParser druidSelectParser = new DruidSelectParser();\n\n    /**\n     * 此方法检测DruidSelectParser的buildGroupByCols方法是否修改了函数列\n     * 因为select的函数列并不做alias处理，\n     * 所以在groupby也对函数列不做修改\n     *\n     * @throws NoSuchMethodException\n     * @throws InvocationTargetException\n     * @throws IllegalAccessException\n     */\n    @Test\n    public void testGroupByWithAlias() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {\n        String functionColumn = \"DATE_FORMAT(h.times,'%b %d %Y %h:%i %p')\";\n        Object result = invokeGroupBy(functionColumn);\n        Assert.assertEquals(functionColumn, ((String[]) result)[0]);\n    }\n\n    /**\n     * 此方法检测DruidSelectParser对于子查询别名的全局解析\n     *\n     * @throws NoSuchMethodException\n     * @throws InvocationTargetException\n     * @throws IllegalAccessException\n     */\n    @Test\n    public void testGroupByWithViewAlias() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {\n        String functionColumn = \"select id from (select h.id from hotnews h  union select h.title from hotnews h ) as t1 group by t1.id;\";\n        Object result = invokeGroupBy(functionColumn);\n        Assert.assertEquals(functionColumn, ((String[]) result)[0]);\n    }\n\n    public Object invokeGroupBy(String functionColumn) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {\n        Map<String, String> aliaColumns = new TreeMap<>();\n\t\tSQLIdentifierExpr sqlExpr = new SQLIdentifierExpr(functionColumn);// mock(SQLIdentifierExpr.class);\n\t\tSQLIdentifierExpr expr = new SQLIdentifierExpr(functionColumn); // mock(SQLIdentifierExpr.class);\n        List<SQLExpr> groupByItems = new ArrayList<>();\n        groupByItems.add(sqlExpr);\n\t\t// when((sqlExpr).getName()).thenReturn(functionColumn);\n        Class c = DruidSelectParser.class;\n        Method method = c.getDeclaredMethod(\"buildGroupByCols\", new Class[]{List.class, Map.class});\n        method.setAccessible(true);\n        return  method.invoke(druidSelectParser, groupByItems, aliaColumns);\n    }\n\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/parser/druid/DruidSequenceHandlerTest.java",
    "content": "package io.mycat.parser.druid;\n\nimport static junit.framework.Assert.assertEquals;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.junit.Test;\n\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.parser.druid.DruidSequenceHandler;\n\n/**\n * 获取MYCAT SEQ 表名。\n */\npublic class DruidSequenceHandlerTest {\n\n\t@Test\n\tpublic void test() {\n\t\tDruidSequenceHandler handler = new DruidSequenceHandler(SystemConfig.SEQUENCEHANDLER_LOCALFILE,SystemConfig.SEQUENCEHANDLER_PATTERN);\n\t\t\n\t\tString sql = \"select next value for mycatseq_xxxx\".toUpperCase();\n\t\tString tableName = handler.getTableName(sql);\n\t\tassertEquals(tableName, \"XXXX\");\n\n\n        sql = \" insert into test(id,sid)values(next value for MYCATSEQ_TEST,1)\".toUpperCase();\n         tableName = handler.getTableName(sql);\n        assertEquals(tableName, \"TEST\");\n\n        sql = \" insert into test(id,sid)values(next value for MYCATSEQ_TEST       ,1)\".toUpperCase();\n        tableName = handler.getTableName(sql);\n        assertEquals(tableName, \"TEST\");\n\n        sql = \" insert into test(id)values(next value for MYCATSEQ_TEST  )\".toUpperCase();\n        tableName = handler.getTableName(sql);\n        assertEquals(tableName, \"TEST\");\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tDruidSequenceHandler handler = new DruidSequenceHandler(SystemConfig.SEQUENCEHANDLER_LOCALFILE,SystemConfig.SEQUENCEHANDLER_PATTERN);\n\t\t\n\t\tString sql = \"/* APPLICATIONNAME=DBEAVER 3.3.2 - MAIN CONNECTION */ SELECT NEXT VALUE FOR MYCATSEQ_XXXX\".toUpperCase();\n\t\tString tableName = handler.getTableName(sql);\n\t\tassertEquals(tableName, \"XXXX\");\n\t}\n\n    public static void main(String[] args)\n    {\n        String patten=\"(?:(\\\\s*next\\\\s+value\\\\s+for\\\\s*MYCATSEQ_(\\\\w+))(,|\\\\)|\\\\s)*)+\";\n        Pattern pattern = Pattern.compile(patten,Pattern.CASE_INSENSITIVE);\n         String sql=\"insert into test(id,sid)values(    next    value    for        MYCATSEQ_TEST ,1)\";\n          Matcher matcher = pattern.matcher(sql);\n        System.out.println(matcher.find());\n        System.out.println(matcher.group(1));\n        System.out.println(matcher.group(2));\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/parser/druid/DruidUpdateParserTest.java",
    "content": "package io.mycat.parser.druid;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.parser.druid.impl.DruidUpdateParser;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.sql.SQLNonTransientException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author Hash Zhang\n * @version 1.0.0\n * @date 2016/7/7\n */\n\npublic class DruidUpdateParserTest {\n    /**\n     * 测试单表更新分片字段\n     * @throws NoSuchMethodException\n     */\n    @Test\n    public void testUpdateShardColumn() throws NoSuchMethodException{\n        throwExceptionParse(\"update hotnews set id = 1 where name = 234;\", true);\n        throwExceptionParse(\"update hotnews set id = 1 where id = 3;\", true);\n        throwExceptionParse(\"update hotnews set id = 1, name = '123' where id = 1 and name = '234'\", false);\n        throwExceptionParse(\"update hotnews set id = 1, name = '123' where id = 1 or name = '234'\", true);\n        throwExceptionParse(\"update hotnews set id = 'A', name = '123' where id = 'A' and name = '234'\", false);\n        throwExceptionParse(\"update hotnews set id = 'A', name = '123' where id = 'A' or name = '234'\", true);\n        throwExceptionParse(\"update hotnews set id = 1.5, name = '123' where id = 1.5 and name = '234'\", false);\n        throwExceptionParse(\"update hotnews set id = 1.5, name = '123' where id = 1.5 or name = '234'\", true);\n\n        throwExceptionParse(\"update hotnews set id = 1, name = '123' where name = '234' and (id = 1 or age > 3)\", true);\n        throwExceptionParse(\"update hotnews set id = 1, name = '123' where id = 1 and (name = '234' or age > 3)\", false);\n\n        // 子查询，特殊的运算符between等情况\n        throwExceptionParse(\"update hotnews set id = 1, name = '123' where id = 1 and name in (select name from test)\", false);\n        throwExceptionParse(\"update hotnews set id = 1, name = '123' where name = '123' and id in (select id from test)\", true);\n        throwExceptionParse(\"update hotnews set id = 1, name = '123' where id between 1 and 3\", true);\n        throwExceptionParse(\"update hotnews set id = 1, name = '123' where id between 1 and 3 and name = '234'\", true);\n        throwExceptionParse(\"update hotnews set id = 1, name = '123' where id between 1 and 3 or name = '234'\", true);\n        throwExceptionParse(\"update hotnews set id = 1, name = '123' where id = 1 and name between '124' and '234'\", false);\n    }\n\n    /**\n     * 测试单表别名更新分片字段\n     * @throws NoSuchMethodException\n     */\n    @Test\n    public void testAliasUpdateShardColumn() throws NoSuchMethodException{\n        throwExceptionParse(\"update hotnews h set h.id = 1 where h.name = 234;\", true);\n        throwExceptionParse(\"update hotnews h set h.id = 1 where h.id = 3;\", true);\n        throwExceptionParse(\"update hotnews h set h.id = 1, h.name = '123' where h.id = 1 and h.name = '234'\", false);\n        throwExceptionParse(\"update hotnews h set h.id = 1, h.name = '123' where h.id = 1 or h.name = '234'\", true);\n        throwExceptionParse(\"update hotnews h set h.id = 'A', h.name = '123' where h.id = 'A' and h.name = '234'\", false);\n        throwExceptionParse(\"update hotnews h set h.id = 'A', h.name = '123' where h.id = 'A' or h.name = '234'\", true);\n        throwExceptionParse(\"update hotnews h set h.id = 1.5, h.name = '123' where h.id = 1.5 and h.name = '234'\", false);\n        throwExceptionParse(\"update hotnews h set h.id = 1.5, h.name = '123' where h.id = 1.5 or h.name = '234'\", true);\n\n        throwExceptionParse(\"update hotnews h set id = 1, h.name = '123' where h.id = 1 and h.name = '234'\", false);\n        throwExceptionParse(\"update hotnews h set h.id = 1, h.name = '123' where id = 1 or h.name = '234'\", true);\n\n        throwExceptionParse(\"update hotnews h set h.id = 1, h.name = '123' where h.name = '234' and (h.id = 1 or h.age > 3)\", true);\n        throwExceptionParse(\"update hotnews h set h.id = 1, h.name = '123' where h.id = 1 and (h.name = '234' or h.age > 3)\", false);\n    }\n\n    public void throwExceptionParse(String sql, boolean throwException) throws NoSuchMethodException {\n        MySqlStatementParser parser = new MySqlStatementParser(sql);\n        List<SQLStatement> statementList = parser.parseStatementList();\n        SQLStatement sqlStatement = statementList.get(0);\n        MySqlUpdateStatement update = (MySqlUpdateStatement) sqlStatement;\n        SchemaConfig schemaConfig = mock(SchemaConfig.class);\n        Map<String, TableConfig> tables = mock(Map.class);\n        TableConfig tableConfig = mock(TableConfig.class);\n        String tableName = \"hotnews\";\n        when((schemaConfig).getTables()).thenReturn(tables);\n        when(tables.get(tableName)).thenReturn(tableConfig);\n        when(tableConfig.getParentTC()).thenReturn(null);\n        RouteResultset routeResultset = new RouteResultset(sql, 11);\n        Class c = DruidUpdateParser.class;\n        Method method = c.getDeclaredMethod(\"confirmShardColumnNotUpdated\", new Class[]{SQLUpdateStatement.class, SchemaConfig.class, String.class, String.class, String.class, RouteResultset.class});\n        method.setAccessible(true);\n        try {\n            method.invoke(c.newInstance(), update, schemaConfig, tableName, \"ID\", \"\", routeResultset);\n            if (throwException) {\n                System.out.println(\"未抛异常，解析通过则不对！\");\n                Assert.assertTrue(false);\n            } else {\n                System.out.println(\"未抛异常，解析通过，此情况分片字段可能在update语句中但是实际不会被更新\");\n                Assert.assertTrue(true);\n            }\n        } catch (Exception e) {\n            if (throwException) {\n                System.out.println(e.getCause().getClass());\n                Assert.assertTrue(e.getCause() instanceof SQLNonTransientException);\n                System.out.println(\"抛异常原因为SQLNonTransientException则正确\");\n            } else {\n                System.out.println(\"抛异常，需要检查\");\n                Assert.assertTrue(false);\n            }\n        }\n    }\n\n    /*\n    * 添加一个static方法用于打印一个SQL的where子句，比如这样的一条SQL:\n    * update mytab t set t.ptn_col = 'A', col1 = 3 where ptn_col = 'A' and (col1 = 4 or col2 > 5);\n    * where子句的语法树如下\n    *                  AND\n    *              /        \\\n    *             =          OR\n    *          /   \\       /    \\\n    *     ptn_col 'A'    =       >\n    *                  /  \\    /   \\\n    *               col1  4  col2   5\n    * 其输出如下，(按层输出，并且每层最后输出下一层的节点数目)\n    * BooleanAnd\t\t\tNum of nodes in next level: 2\n    * Equality\tBooleanOr\t\t\tNum of nodes in next level: 4\n    * ptn_col\t'A'\tEquality\tEquality\t\t\tNum of nodes in next level: 4\n    * col1\t4\tcol2\t5\t\t\tNum of nodes in next level: 0\n    *\n    * 因为大部分的update的where子句都比较简单，按层次打印应该足够清晰，未来可以完全按照逻辑打印类似上面的整棵树结构\n     */\n    public static void printWhereClauseAST(SQLExpr sqlExpr) {\n        // where子句的AST sqlExpr可以通过 MySqlUpdateStatement.getWhere(); 获得\n        if (sqlExpr == null)\n            return;\n        ArrayList<SQLExpr> exprNode = new ArrayList<>();\n        int i = 0, curLevel = 1, nextLevel = 0;\n        SQLExpr iterExpr;\n        exprNode.add(sqlExpr);\n        while (true) {\n            iterExpr = exprNode.get(i++);\n            if (iterExpr == null)\n                break;\n\n            if (iterExpr instanceof SQLBinaryOpExpr) {\n                System.out.print(((SQLBinaryOpExpr) iterExpr).getOperator());\n            } else {\n                System.out.print(iterExpr.toString());\n            }\n            System.out.print(\"\\t\");\n            curLevel--;\n\n            if (iterExpr instanceof SQLBinaryOpExpr) {\n                if (((SQLBinaryOpExpr) iterExpr).getLeft() != null) {\n                    exprNode.add(((SQLBinaryOpExpr) iterExpr).getLeft());\n                    nextLevel++;\n                }\n                if (((SQLBinaryOpExpr) iterExpr).getRight() != null) {\n                    exprNode.add(((SQLBinaryOpExpr) iterExpr).getRight());\n                    nextLevel++;\n                }\n            }\n            if (curLevel == 0) {\n                System.out.println(\"\\t\\tNum of nodes in next level: \" + nextLevel);\n                curLevel = nextLevel;\n                nextLevel = 0;\n            }\n            if (exprNode.size() == i)\n                break;\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/parser/druid/MycatSchemaStatVisitorTest.java",
    "content": "package io.mycat.parser.druid;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Test;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\nimport com.alibaba.druid.stat.TableStat.Condition;\n\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\nimport junit.framework.Assert;\n\n/**\n * TODO: 增加描述\n *\n * @author user\n * @date 2015-6-2 下午5:50:25\n * @version 0.1.0\n * @copyright wonhigh.cn\n */\npublic class MycatSchemaStatVisitorTest {\n\n\t/**\n\t * 从注解中解析 mycat schema\n\t */\n\t@Test\n\tpublic void test4() {\n\t\tString sql = \"/*!mycat:schema = helper1 */update adm_task a set a.remark = 'test' where id=1\";\n\t\tAssert.assertEquals(getSchema(sql), \"helper1.\");\n\t\tsql = \"/*!mycat:schema=helper1*/update adm_task a set a.remark = 'test' where id=1\";\n\t\tAssert.assertEquals(getSchema(sql), \"helper1.\");\n\t\tsql = \"/*!mycat:schema=  helper1*/update adm_task a set a.remark = 'test' where id=1\";\n\t\tAssert.assertEquals(getSchema(sql), \"helper1.\");\n\t\tSystem.out.println(getSchema(sql));\n\t}\n\n\n\t/**\n\t * 3层嵌套or语句\n\t */\n\t@Test\n\tpublic void test1() {\n\t\tString sql = \"select id from travelrecord \"\n    \t\t\t+ \" where id = 1 and ( fee=3 or days=5 or (traveldate = '2015-05-04 00:00:07.375' \"\n    \t\t\t+ \" and (user_id=2 or fee=days or fee = 0))) and id=2\" ;\n\t\tList<List<Condition>> list = getConditionList(sql);\n\t\tAssert.assertEquals(list.size(), 5);\n\t\tAssert.assertEquals(list.get(0).size(), 2);\n\t\tAssert.assertEquals(list.get(1).size(), 2);\n\t\tAssert.assertEquals(list.get(2).size(), 3);\n\t\tAssert.assertEquals(list.get(3).size(), 4);\n\t\tAssert.assertEquals(list.get(4).size(), 3);\n\n\t\tAssert.assertEquals(list.get(0).get(0).toString(), \"travelrecord.days = 5\");\n\t\tAssert.assertEquals(list.get(0).get(1).toString(), \"travelrecord.id = (1, 2)\");\n\n\t\tAssert.assertEquals(list.get(1).get(0).toString(), \"travelrecord.fee = 3\");\n\t\tAssert.assertEquals(list.get(1).get(1).toString(), \"travelrecord.id = (1, 2)\");\n\n\t\tAssert.assertEquals(list.get(2).get(0).toString(), \"travelrecord.fee = 0\");\n\t\tAssert.assertEquals(list.get(2).get(1).toString(), \"travelrecord.traveldate = 2015-05-04 00:00:07.375\");\n\t\tAssert.assertEquals(list.get(2).get(2).toString(), \"travelrecord.id = (1, 2)\");\n\n\t\tAssert.assertEquals(list.get(3).get(0).toString(), \"travelrecord.fee =\");\n\t\tAssert.assertEquals(list.get(3).get(1).toString(), \"travelrecord.days =\");\n\t\tAssert.assertEquals(list.get(3).get(2).toString(), \"travelrecord.traveldate = 2015-05-04 00:00:07.375\");\n\t\tAssert.assertEquals(list.get(3).get(3).toString(), \"travelrecord.id = (1, 2)\");\n\n\t\tAssert.assertEquals(list.get(4).get(0).toString(), \"travelrecord.user_id = 2\");\n\t\tAssert.assertEquals(list.get(4).get(1).toString(), \"travelrecord.traveldate = 2015-05-04 00:00:07.375\");\n\t\tAssert.assertEquals(list.get(4).get(2).toString(), \"travelrecord.id = (1, 2)\");\n\n\t\tSystem.out.println(list.size());\n\t}\n\n\t/**\n\t * 1层嵌套or语句\n\t */\n\t@Test\n\tpublic void test2() {\n\t\tString sql = \"select id from travelrecord \"\n    \t\t\t+ \" where id = 1 and ( fee=3 or days=5 or name = 'zhangsan')\" ;\n\t\tList<List<Condition>> list = getConditionList(sql);\n\n\t\tAssert.assertEquals(list.size(), 3);\n\t\tAssert.assertEquals(list.get(0).size(), 2);\n\t\tAssert.assertEquals(list.get(1).size(), 2);\n\t\tAssert.assertEquals(list.get(2).size(), 2);\n\n\n\t\tAssert.assertEquals(list.get(0).get(0).toString(), \"travelrecord.name = zhangsan\");\n\t\tAssert.assertEquals(list.get(0).get(1).toString(), \"travelrecord.id = 1\");\n\n\t\tAssert.assertEquals(list.get(1).get(0).toString(), \"travelrecord.days = 5\");\n\t\tAssert.assertEquals(list.get(1).get(1).toString(), \"travelrecord.id = 1\");\n\n\t\tAssert.assertEquals(list.get(2).get(0).toString(), \"travelrecord.fee = 3\");\n\t\tAssert.assertEquals(list.get(2).get(1).toString(), \"travelrecord.id = 1\");\n\t}\n\n\t/**\n\t * 1层嵌套or语句\n\t */\n\t@Test\n\tpublic void test3() {\n\t\tString sql = \"select id from travelrecord \"\n    \t\t\t+ \" where id = 1 and fee=3 or days=5 or name = 'zhangsan'\" ;\n\t\tList<List<Condition>> list = getConditionList(sql);\n\n\t\tAssert.assertEquals(list.size(), 3);\n\n\t\tAssert.assertEquals(list.get(0).size(), 1);\n\t\tAssert.assertEquals(list.get(1).size(), 1);\n\t\tAssert.assertEquals(list.get(2).size(), 2);\n\n\t\tAssert.assertEquals(list.get(0).get(0).toString(), \"travelrecord.name = zhangsan\");\n\n\t\tAssert.assertEquals(list.get(1).get(0).toString(), \"travelrecord.days = 5\");\n\n\t\tAssert.assertEquals(list.get(2).get(0).toString(), \"travelrecord.id = 1\");\n\t\tAssert.assertEquals(list.get(2).get(1).toString(), \"travelrecord.fee = 3\");\n\t}\n\n\tString sql = \"select count(*) count from (select *\\r\\n\"\n      + \"          from (select b.sales_count,\\r\\n\" + \"                       b.special_type,\\r\\n\"\n      + \"                       a.prod_offer_id offer_id,\\r\\n\"\n      + \"                       a.alias_name as offer_name,\\r\\n\"\n      + \"                       (select c.attr_value_name\\r\\n\"\n      + \"                          from attr_value c\\r\\n\"\n      + \"                         where c.attr_id = '994001448'\\r\\n\"\n      + \"                           and c.attr_value = b.special_type) as attr_value_name,\\r\\n\"\n      + \"                       a.offer_type offer_kind,\\r\\n\"\n      + \"                       a.offer_comments,\\r\\n\" + \"                       a.is_ge\\r\\n\"\n      + \"                  from prod_offer a, special_offer b\\r\\n\"\n      + \"                 where a.prod_offer_id = b.prod_offer_id\\r\\n\"\n      + \"                   and (a.offer_type = '11' or a.offer_type = '10')\\r\\n\"\n      + \"                   and (b.region_id = '731' or b.region_id = '10000000')\\r\\n\"\n      + \"                   and a.status_cd = '10'\\r\\n\"\n      + \"                   and b.special_type = '0'\\r\\n\" + \"                union all\\r\\n\"\n      + \"                select b.sales_count,\\r\\n\" + \"                       b.special_type,\\r\\n\"\n      + \"                       a.prod_offer_id offer_id,\\r\\n\"\n      + \"                       a.alias_name as offer_name,\\r\\n\"\n      + \"                       (select c.attr_value_name\\r\\n\"\n      + \"                          from attr_value c\\r\\n\"\n      + \"                         where c.attr_id = '994001448'\\r\\n\"\n      + \"                           and c.attr_value = b.special_type) as attr_value_name,\\r\\n\"\n      + \"                       a.offer_type offer_kind,\\r\\n\"\n      + \"                       a.offer_comments,\\r\\n\" + \"                       a.is_ge\\r\\n\"\n      + \"                  from prod_offer a, special_offer b\\r\\n\"\n      + \"                 where a.prod_offer_id = b.prod_offer_id\\r\\n\"\n      + \"                   and (a.offer_type = '11' or a.offer_type = '10')\\r\\n\"\n      + \"                   and (b.region_id = '731' or b.region_id = '10000000')\\r\\n\"\n      + \"                   and a.status_cd = '10'\\r\\n\"\n      + \"                   and b.special_type = '1'\\r\\n\"\n      + \"                   and exists (select 1\\r\\n\"\n      + \"                          from prod_offer_channel l\\r\\n\"\n      + \"                         where a.prod_offer_id = l.prod_offer_id\\r\\n\"\n      + \"                           and l.channel_id = '11')\\r\\n\"\n      + \"                   and not exists\\r\\n\" + \"                 (select 1\\r\\n\"\n      + \"                          from product_offer_cat ml\\r\\n\"\n      + \"                         where ml.prod_offer_id = a.prod_offer_id\\r\\n\"\n      + \"                           and ml.offer_cat_type = '89')\\r\\n\"\n      + \"                   and (exists (select 1\\r\\n\"\n      + \"                                  from sales_restrication\\r\\n\"\n      + \"                                 where object_id = a.prod_offer_id\\r\\n\"\n      + \"                                   and domain_id = '1965868'\\r\\n\"\n      + \"                                   and restrication_flag = '0'\\r\\n\"\n      + \"                                   and domain_type = '19F'\\r\\n\"\n      + \"                                   and state = '00A') or exists\\r\\n\"\n      + \"                        (select 1\\r\\n\"\n      + \"                           from sales_restrication\\r\\n\"\n      + \"                          where object_id = a.prod_offer_id\\r\\n\"\n      + \"                            and domain_id = '843073100000000'\\r\\n\"\n      + \"                            and restrication_flag = '0'\\r\\n\"\n      + \"                            and domain_type = '19E'\\r\\n\"\n      + \"                            and state = '00A') or exists\\r\\n\"\n      + \"                        (select 1\\r\\n\"\n      + \"                           from sales_restrication\\r\\n\"\n      + \"                          where object_id = a.prod_offer_id\\r\\n\"\n      + \"                            and domain_id = '1965868'\\r\\n\"\n      + \"                            and restrication_flag = '0'\\r\\n\"\n      + \"                            and domain_type = '19X'\\r\\n\"\n      + \"                            and state = '00A'\\r\\n\"\n      + \"                            and (max_value = 1 or min_value = 1)\\r\\n\"\n      + \"                            and extended_field = '731') or exists\\r\\n\"\n      + \"                        (select 1\\r\\n\"\n      + \"                           from sales_restrication\\r\\n\"\n      + \"                          where object_id = a.prod_offer_id\\r\\n\"\n      + \"                            and domain_id = concat('-', '11')\\r\\n\"\n      + \"                            and restrication_flag = '0'\\r\\n\"\n      + \"                            and domain_type = '19F'\\r\\n\"\n      + \"                            and state = '00A') or not exists\\r\\n\"\n      + \"                        (select 1\\r\\n\"\n      + \"                           from sales_restrication\\r\\n\"\n      + \"                          where object_id = a.prod_offer_id\\r\\n\"\n      + \"                            and restrication_flag = '0'\\r\\n\"\n      + \"                            and (domain_type in ('19F', '19E') or\\r\\n\"\n      + \"                                (domain_type = '19X' and\\r\\n\"\n      + \"                                extended_field = '731' and\\r\\n\"\n      + \"                                (max_value = 1 or min_value = 1)))\\r\\n\"\n      + \"                            and state = '00A'))\\r\\n\"\n      + \"                   and not exists (select 1\\r\\n\"\n      + \"                          from sales_restrication\\r\\n\"\n      + \"                         where object_id = a.prod_offer_id\\r\\n\"\n      + \"                           and domain_id = '1965868'\\r\\n\"\n      + \"                           and restrication_flag = '1'\\r\\n\"\n      + \"                           and domain_type = '19F'\\r\\n\"\n      + \"                           and state = '00A')\\r\\n\"\n      + \"                   and not exists (select 1\\r\\n\"\n      + \"                          from sales_restrication\\r\\n\"\n      + \"                         where object_id = a.prod_offer_id\\r\\n\"\n      + \"                           and domain_id = '843073100000000'\\r\\n\"\n      + \"                           and restrication_flag = '1'\\r\\n\"\n      + \"                           and domain_type = '19E'\\r\\n\"\n      + \"                           and state = '00A')\\r\\n\"\n      + \"                   and not exists\\r\\n\" + \"                 (select 1\\r\\n\"\n      + \"                          from sales_restrication\\r\\n\"\n      + \"                         where object_id = a.prod_offer_id\\r\\n\"\n      + \"                           and domain_id = '1965868'\\r\\n\"\n      + \"                           and restrication_flag = '1'\\r\\n\"\n      + \"                           and domain_type = '19X'\\r\\n\"\n      + \"                           and state = '00A'\\r\\n\"\n      + \"                           and (min_value = 1 or max_value = 1)\\r\\n\"\n      + \"                           and extended_field = '731')\\r\\n\"\n      + \"                   and not exists (select 1\\r\\n\"\n      + \"                          from sales_restrication\\r\\n\"\n      + \"                         where object_id = a.prod_offer_id\\r\\n\"\n      + \"                           and domain_id = concat('-', '11')\\r\\n\"\n      + \"                           and restrication_flag = '1'\\r\\n\"\n      + \"                           and domain_type = '19F'\\r\\n\"\n      + \"                           and state = '00A')\\r\\n\" + \"                   and exists\\r\\n\"\n      + \"                 (select 1\\r\\n\" + \"                          from prod_offer_region v1\\r\\n\"\n      + \"                         where v1.prod_offer_id = a.prod_offer_id\\r\\n\"\n      + \"                           and (v1.common_region_id = '731' or\\r\\n\"\n      + \"                               v1.common_region_id = '10000000' or\\r\\n\"\n      + \"                               v1.common_region_id = '73101'))) t\\r\\n\"\n      + \"         order by t.sales_count desc)\";\n\n\t/**\n   * 8层以上 嵌套or语句\n   */\n  @Test\n  public void test5() {\n    List<List<Condition>> list = getConditionList(sql);\n\n    Assert.assertTrue(list.size() < 100);\n  }\n\n\tprivate String getSchema(String sql) {\n\t\tSQLStatementParser parser =null;\n\t\tparser = new MySqlStatementParser(sql);\n\n\t\tMycatSchemaStatVisitor visitor = null;\n\t\tSQLStatement statement = null;\n\t\t//解析出现问题统一抛SQL语法错误\n\t\ttry {\n\t\t\tstatement = parser.parseStatement();\n            visitor = new MycatSchemaStatVisitor();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\tstatement.accept(visitor);\n\n\n\t\treturn visitor.getCurrentTable();\n\t}\n\n\tprivate List<List<Condition>> getConditionList(String sql) {\n\t\tSQLStatementParser parser =null;\n\t\tparser = new MySqlStatementParser(sql);\n\n\t\tMycatSchemaStatVisitor visitor = null;\n\t\tSQLStatement statement = null;\n\t\t//解析出现问题统一抛SQL语法错误\n\t\ttry {\n\t\t\tstatement = parser.parseStatement();\n            visitor = new MycatSchemaStatVisitor();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\tstatement.accept(visitor);\n\n\t\tList<List<Condition>> mergedConditionList = new ArrayList<List<Condition>>();\n\t\tif(visitor.hasOrCondition()) {//包含or语句\n\t\t\t//TODO\n\t\t\t//根据or拆分\n\t\t\tmergedConditionList = visitor.splitConditions();\n\t\t} else {//不包含OR语句\n\t\t\tmergedConditionList.add(visitor.getConditions());\n\t\t}\n\n\t\treturn mergedConditionList;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/parser/primitive/TestFunctionParser.java",
    "content": "package io.mycat.parser.primitive;\n\nimport io.mycat.route.parser.primitive.FunctionParser;\nimport io.mycat.route.parser.primitive.Model.Function;\nimport junit.framework.Assert;\nimport org.junit.Test;\n\nimport java.sql.SQLNonTransientException;\n\n/**\n * @author Hash Zhang\n * @version 1.0.0\n * @date 2016/7/26\n */\npublic class TestFunctionParser {\n\n    @Test\n    public void testMultiFunctions() throws SQLNonTransientException {\n        Assert.assertEquals(\"[arg1, a.t]\",testFunctionParse(\"function1(arg1,a.t)\"));\n        Assert.assertEquals(\"[arg1, a.t]\",testFunctionParse(\"function1(arg1,a.t,\\\"ast(,)\\\")\"));\n        Assert.assertEquals(\"[arg1, a.t, c.t, x]\",testFunctionParse(\"function1(arg1,a.t,\\\"ast(,)\\\",\\\",\\\",function2(c.t,function3(x)))\"));\n        Assert.assertEquals(\"[arg1, a.t, c.t, x]\",testFunctionParse(\"function1(arg1,a.t,\\\"ast(,)\\\",\\\",\\\",function2(c.t,\\\"(,)\\\",function3(function4(x))))\"));\n    }\n\n    public String testFunctionParse(String function) throws SQLNonTransientException {\n        Function function1 = FunctionParser.parseFunction(function);\n        return FunctionParser.getFields(function1).toString();\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/parser/util/PairUtilTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.parser.util;\n\nimport junit.framework.TestCase;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport io.mycat.route.parser.util.Pair;\nimport io.mycat.route.parser.util.PairUtil;\n\n/**\n * @author mycat\n */\npublic class PairUtilTest extends TestCase {\n\n    @Test\n    public void testSequenceSlicing() {\n        Assert.assertEquals(new Pair<Integer, Integer>(0, 2), PairUtil.sequenceSlicing(\"2\"));\n        Assert.assertEquals(new Pair<Integer, Integer>(1, 2), PairUtil.sequenceSlicing(\"1: 2\"));\n        Assert.assertEquals(new Pair<Integer, Integer>(1, 0), PairUtil.sequenceSlicing(\" 1 :\"));\n        Assert.assertEquals(new Pair<Integer, Integer>(-1, 0), PairUtil.sequenceSlicing(\"-1: \"));\n        Assert.assertEquals(new Pair<Integer, Integer>(-1, 0), PairUtil.sequenceSlicing(\" -1:0\"));\n        Assert.assertEquals(new Pair<Integer, Integer>(0, 0), PairUtil.sequenceSlicing(\" :\"));\n    }\n\n    @Test\n    public void splitIndexTest() {\n        String src1 = \"offer_group[10]\";\n        Pair<String, Integer> pair1 = PairUtil.splitIndex(src1, '[', ']');\n        Assert.assertEquals(\"offer_group\", pair1.getKey());\n        Assert.assertEquals(Integer.valueOf(10), pair1.getValue());\n\n        String src2 = \"offer_group\";\n        Pair<String, Integer> pair2 = PairUtil.splitIndex(src2, '[', ']');\n        Assert.assertEquals(\"offer_group\", pair2.getKey());\n        Assert.assertEquals(Integer.valueOf(-1), pair2.getValue());\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/AbstractMultiTreadBatchTester.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.io.FileInputStream;\nimport java.sql.SQLException;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.Properties;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.atomic.AtomicLong;\n\npublic abstract class AbstractMultiTreadBatchTester {\n\tSimpleConPool conPool;\n\tprotected AtomicLong finshiedCount = new AtomicLong();\n\tprotected AtomicLong failedCount = new AtomicLong();\n\tprotected int threadCount = 0;// 线程数\n\tprotected String url;\n\tprotected String user;\n\tprotected String password;\n\tprotected ExecutorService executor;\n\tlong start;\n\tprotected String[] rangeItems;\n\tprotected boolean outputMiddleInf = true;\n\tprotected String sqlFile;\n\n\tpublic boolean parseArgs(String[] args) {\n\t\tif (args.length < 5) {\n\t\t\tSystem.out\n\t\t\t\t\t.println(\"input param,format: [jdbcurl] [user] [password]  [threadpoolsize]  [recordrange or customer sql file] \");\n\t\t\tSystem.out\n\t\t\t\t\t.println(\"jdbc:mysql://localhost:8066/TESTDB test test 10  \\\"0-300M,300M1-600M,600M1-900M\\\" \");\n\t\t\tSystem.out\n\t\t\t\t\t.println(\"jdbc:mysql://localhost:8066/TESTDB test test 10  file=mytempate.sql \");\n\n\t\t\treturn false;\n\t\t}\n\t\turl = args[0];\n\t\tuser = args[1];\n\t\tpassword = args[2];\n\t\tthreadCount = Integer.parseInt(args[3]);\n\t\tif (args[4].contains(\"file=\")) {\n\t\t\tsqlFile = args[4].substring(args[4].indexOf('=') + 1);\n\t\t} else {\n\t\t\trangeItems = args[4].split(\",\");\n\t\t}\n\t\treturn true;\n\n\t}\n\n\tpublic void run(String[] args) throws Exception {\n\t\tif (!this.parseArgs(args)) {\n\t\t\treturn;\n\t\t}\n\t\tstartTest();\n\t\trunAndReport();\n\t}\n\n\tpublic void addFinshed(int count) {\n\t\tfinshiedCount.addAndGet(count);\n\t}\n\n\tpublic ArrayList<Runnable> createJobs(SimpleConPool conPool, long minId,\n\t\t\tlong maxId) throws Exception {\n\t\tlong recordCount = maxId - minId + 1;\n\t\tint batchSize = 1000;\n\t\tlong totalBatch = recordCount / batchSize;\n\t\tArrayList<Runnable> jobs = new ArrayList<Runnable>();\n\t\tfor (int i = 0; i < totalBatch; i++) {\n\t\t\tlong startId = minId + i * batchSize;\n\t\t\tlong endId = (startId + batchSize - 1);\n\t\t\tif (endId >= maxId) {\n\t\t\t\tendId = maxId;\n\t\t\t} else if (i == totalBatch - 1) {\n\t\t\t\tendId = maxId;\n\t\t\t}\n\t\t\tlong myCount = endId - startId + 1;\n\t\t\tRunnable job = createJob(getConPool(), myCount, 100, startId,\n\t\t\t\t\tfinshiedCount, failedCount);\n\t\t\t// System.out.println(\"job record id is \" + startId + \"-\" + endId);\n\t\t\tjobs.add(job);\n\n\t\t}\n\t\treturn jobs;\n\t}\n\n\tpublic abstract Runnable createJob(SimpleConPool conPool2, long myCount,\n\t\t\tint batchSize, long startId, AtomicLong finshiedCount2,\n\t\t\tAtomicLong failedCount2);\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic ArrayList<Runnable>[] createAllJobs() throws Exception {\n\t\tif (sqlFile != null) {// from sql template file\n\t\t\tjava.util.Properties pros = RandomDataValueUtil\n\t\t\t\t\t.loadFromPropertyFile(sqlFile);\n\t\t\tlong total = Long.valueOf(pros.getProperty(\"total\"));\n\t\t\tString sqlTemplate = pros.getProperty(\"sql\");\n\t\t\tString batchSizeStr=pros.getProperty(\"batch\");\n\t\t\tString autocommitStr=pros.getProperty(\"autocommit\");\n\t\t\tboolean autocommit= autocommitStr!=null && Boolean.valueOf(autocommitStr);\n\t\t\tint batchSize=batchSizeStr==null?100:Integer.parseInt(batchSizeStr);\n\t\t\tSystem.out.println(\"total record \"+total+ \" batch size:\"+batchSize+\" autocomit \"+autocommit);\n\t\t\treturn createSQLTemplateJobs(total, sqlTemplate,batchSize,autocommit);\n\n\t\t} else {\n\t\t\tArrayList<Runnable>[] allJobs = new ArrayList[rangeItems.length];\n\t\t\tfor (int i = 0; i < rangeItems.length; i++) {\n\t\t\t\tString[] items = rangeItems[i].split(\"-\");\n\t\t\t\tlong min = parseLong(items[0]);\n\t\t\t\tlong max = parseLong(items[1]);\n\t\t\t\tallJobs[i] = createJobs(conPool, min, max);\n\n\t\t\t}\n\t\t\treturn allJobs;\n\t\t}\n\n\t}\n\n\tprivate ArrayList<Runnable>[] createSQLTemplateJobs(long total,\n\t\t\tString sqlTemplate,int batchSize,boolean autocommit) throws Exception {\n\t\tLinkedList<StringItem> sqlTemplateItems = RandomDataValueUtil\n\t\t\t\t.parselRandVarTemplateString(sqlTemplate);\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArrayList<Runnable>[] allJobs = new ArrayList[1];\n\n\t\tlong totalBatch = total / threadCount;\n\t\tallJobs[0] = new ArrayList<Runnable>((int) threadCount);\n\t\tfor (int i = 0; i < threadCount; i++) {\n\t\t\tallJobs[0].add(new UserTableInsertJob(getConPool(), totalBatch,\n\t\t\t\t\tbatchSize, finshiedCount, failedCount, sqlTemplateItems,autocommit));\n\t\t}\n\t\tif (totalBatch * threadCount < total) {\n\t\t\tallJobs[0].add(new UserTableInsertJob(getConPool(), total\n\t\t\t\t\t- totalBatch * threadCount, batchSize, finshiedCount,\n\t\t\t\t\tfailedCount, sqlTemplateItems,autocommit));\n\t\t}\n\t\treturn allJobs;\n\t}\n\n\tpublic void addFailed(int count) {\n\t\tfailedCount.addAndGet(count);\n\t}\n\n\tpublic SimpleConPool getConPool() throws SQLException,\n\t\t\tClassNotFoundException {\n\t\tif (conPool == null) {\n\t\t\tClass.forName(\"com.mysql.jdbc.Driver\");\n\t\t\tconPool = new SimpleConPool(url, user, password, threadCount);\n\t\t}\n\t\treturn conPool;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void startTest() throws Exception {\n\t\texecutor = Executors.newFixedThreadPool(threadCount);\n\t\tSystem.out.println(\"create jobs ...\");\n\t\tArrayList<Runnable>[] allJobs = createAllJobs();\n\t\tIterator<Runnable>[] itors = new Iterator[allJobs.length];\n\t\tfor (int i = 0; i < allJobs.length; i++) {\n\t\t\titors[i] = allJobs[i].iterator();\n\t\t}\n\t\tSystem.out.println(\"create jobs finished ,begin run test...\");\n\t\tint total = 0;\n\t\tstart = System.currentTimeMillis();\n\t\tboolean finished = false;\n\t\twhile (!finished) {\n\n\t\t\tfinished = true;\n\t\t\tfor (int i = 0; i < itors.length; i++) {\n\t\t\t\tif (itors[i].hasNext()) {\n\t\t\t\t\ttotal++;\n\t\t\t\t\texecutor.execute(itors[i].next());\n\t\t\t\t\tif (finished) {\n\t\t\t\t\t\tfinished = !itors[i].hasNext();\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// executor.execute(job);\n\t\tSystem.out.println(\"success create job count: \" + total\n\t\t\t\t+ \" teset threads: \" + threadCount);\n\t}\n\n\tpublic void runAndReport() throws InterruptedException {\n\t\texecutor.shutdown();\n\n\t\tSimpleDateFormat df = new SimpleDateFormat(\"dd HH:mm:ss\");\n\t\twhile (!executor.isTerminated()) {\n\t\t\tif (outputMiddleInf) {\n\t\t\t\tlong sucess = finshiedCount.get() - failedCount.get();\n\t\t\t\tSystem.out.println(df.format(new Date())\n\t\t\t\t\t\t+ \" finished records :\" + finshiedCount.get()\n\t\t\t\t\t\t+ \" failed:\" + failedCount.get() + \" speed:\" + sucess\n\t\t\t\t\t\t* 1000.0 / (System.currentTimeMillis() - start));\n\t\t\t}\n\t\t\tThread.sleep(1000);\n\t\t}\n\n\t\tlong usedTime = (System.currentTimeMillis() - start) / 1000;\n\t\tSystem.out.println(\"finishend:\" + finshiedCount.get() + \" failed:\"\n\t\t\t\t+ failedCount.get());\n\t\tlong sucess = finshiedCount.get() - failedCount.get();\n\t\tSystem.out.println(\"used time total:\" + usedTime + \"seconds\");\n\t\tSystem.out.println(\"tps:\" + sucess / (usedTime + 0.1));\n\t}\n\n\t/**\n\t * can parse values like 200M ,200K,200M1(2000001)\n\t * \n\t * @param val\n\t * @return\n\t */\n\tprivate static long parseLong(String val) {\n\t\tval = val.toUpperCase();\n\t\tint indx = val.indexOf(\"M\");\n\n\t\tint plus = 10000;\n\t\tif (indx < 0) {\n\t\t\tindx = val.indexOf(\"K\");\n\t\t\tplus = 1000;\n\t\t}\n\t\tif (indx > 0) {\n\t\t\tString longVal = val.substring(0, indx);\n\n\t\t\tlong theVale = Long.parseLong(longVal) * plus;\n\t\t\tString remain = val.substring(indx + 1);\n\t\t\tif (remain.length() > 0) {\n\t\t\t\ttheVale += Integer.parseInt(remain);\n\t\t\t}\n\t\t\treturn theVale;\n\t\t} else {\n\t\t\treturn Long.parseLong(val);\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/GoodsInsertJob.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicLong;\npublic class GoodsInsertJob implements Runnable {\n\tprivate final long endId;\n\tprivate long finsihed;\n\tprivate final int batchSize;\n\tprivate final AtomicLong finshiedCount;\n\tprivate final AtomicLong failedCount;\n\tCalendar date = Calendar.getInstance();\n\tDateFormat datafomat = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tprivate final SimpleConPool conPool;\n\n\n\tpublic GoodsInsertJob(SimpleConPool conPool, long totalRecords,\n\t\t\tint batchSize, long startId, AtomicLong finshiedCount,\n\t\t\tAtomicLong failedCount) {\n\t\tsuper();\n\t\tthis.conPool = conPool;\n\t\tthis.endId = startId + totalRecords - 1;\n\t\tthis.batchSize = batchSize;\n\t\tthis.finsihed = startId;\n\t\tthis.finshiedCount = finshiedCount;\n\t\tthis.failedCount = failedCount;\n\t}\n\n\tprivate int insert(Connection con,List<Map<String, String>> list) throws SQLException {\n\t\tPreparedStatement ps;\n\t\tString sql = \"insert into goods (id,name ,good_type,good_img_url,good_created ,good_desc, price ) values(?,? ,?,?,? ,?, ?)\";\n\t\tps = con.prepareStatement(sql);\n\t\tfor (Map<String, String> map : list) {\n\t\t\tps.setLong(1, Long.parseLong(map.get(\"id\")));\n\t\t\tps.setString(2, (String) map.get(\"name\"));\n\t\t\tps.setShort(3, Short.parseShort(map.get(\"good_type\")));\n\t\t\tps.setString(4, (String) map.get(\"good_img_url\"));\n\t\t\tps.setString(5, (String) map.get(\"good_created\"));\n\t\t\tps.setString(6, (String) map.get(\"good_desc\"));\n\t\t\tps.setDouble(7, Double.parseDouble(map.get(\"price\")));\n\t\t\tps.addBatch();\n\t\t}\n\t\tps.executeBatch();\n\t\treturn list.size();\n\t}\n\n\tprivate List<Map<String, String>> getNextBatch() {\n\t\tif (finsihed >= endId) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tlong end = (finsihed + batchSize) < this.endId ? (finsihed + batchSize)\n\t\t\t\t: endId;\n\t\t// the last batch\n\t\tif (end + batchSize > this.endId) {\n\t\t\tend = this.endId;\n\t\t}\n\t\tList<Map<String, String>> list = new ArrayList<Map<String, String>>(\n\t\t\t\t);\n\t\tfor (long i = finsihed; i < end; i++) {\n\t\t\tMap<String, String> m = new HashMap<String, String>();\n\t\t\tm.put(\"id\", i + \"\");\n\t\t\tm.put(\"name\", \"googs \" + i);\n\t\t\tm.put(\"good_type\", i % 100 + \"\");\n\t\t\tm.put(\"good_img_url\", \"http://openclouddb.org/\" + i);\n\t\t\tm.put(\"good_created\", getRandomDay(i));\n\t\t\tm.put(\"good_desc\", \"best goods \" + i);\n\t\t\tm.put(\"price\", (i + 0.0) % 1000 + \"\");\n\t\t\tlist.add(m);\n\t\t}\n\t\tfinsihed += list.size();\n\t\treturn list;\n\t}\n\n\tprivate String getRandomDay(long i) {\n\t\tint month = Long.valueOf(i % 11 + 1).intValue();\n\t\tint day = Long.valueOf(i % 27 + 1).intValue();\n\n\t\tdate.set(Calendar.MONTH, month);\n\t\tdate.set(Calendar.DAY_OF_MONTH, day);\n\t\treturn datafomat.format(date.getTime());\n\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tConnection con = null;\n\t\ttry {\n\n\t\t\tList<Map<String, String>> batch = getNextBatch();\n\t\t\twhile (!batch.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (con == null || con.isClosed()) {\n\t\t\t\t\t\tcon = conPool.getConnection();\n\t\t\t\t\t\tcon.setAutoCommit(true);\n\t\t\t\t\t}\n\n\t\t\t\t\tinsert(con, batch);\n\t\t\t\t\tfinshiedCount.addAndGet(batch.size());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tfailedCount.addAndGet(batch.size());\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t\tbatch = getNextBatch();\n\t\t\t}\n\t\t} finally {\n\t\t\tif (con != null) {\n\t\t\t\tthis.conPool.returnCon(con);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/RandomDataValueUtil.java",
    "content": "package io.mycat.performance;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Calendar;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Random;\n\n/**\n * genarate random test data \n * such as \n * values ('$date{yyyyMMddHHmmsss-[2014-2015]y}/psn$date{yyyy}s/$int(0-9999)/16767:20725','$char(2,0-99) OPP_$enum(BJ,SH,GZ,SZ)_$int(0-9)',$int(10,11),$int(400,420,500,600,800),$int(0-1000),$int(0-100),Sint(0-10),$int(0-99),'201408040028317067b41c0db-4a93-4360-9eb4-e159d1dbef45',$phone,2,2014071715,2315998,1397,152317998,1395,'0000');\n * @author wuzhih\n *\n */\n/**\n * \n * @author wuzhih\n * \n */\npublic class RandomDataValueUtil {\n\n\t/**\n\t * eval template contains random vars and replace them with real value alues\n\t * (\n\t * '${date(yyyyMMddHHmmsss-[2014-2015]y)}/psn${date(yyyy)}s/${int(0-9999)}/1\n\t * 6 7 6 7 : 2 0 7 2 5 ' , ' $ { s t r i n g ( 2 , 0 - 9 9 )}\n\t * OPP_${enum(BJ,SH,GZ,SZ)}_${int(0-9)}\n\t * ',${int(10,11)},$int(400,420,500,600,800),$int(0-1000),$int(0-100),$int(0-10),$int(0-99),'201408040028317067b41c0db-4a93-4360-9eb4-e159d1\n\t * d b e f 4 5 ' , $ p h o n e , 2 , 2 0 1 4 0 7 1 7 1 5 , 2 3 1 5 9 9 8 , 1\n\t * 3 9 7 , 1 5 2 3 1 7 9 9 8 , 1 3 9 5 , ' 0 0 0 0 ' )\n\t * \n\t * @param templateStr\n\t * @return\n\t * @throws IllegalAccessException\n\t * @throws InstantiationException\n\t */\n\tpublic static LinkedList<StringItem> parselRandVarTemplateString(\n\t\t\tString templateStr) throws Exception {\n\t\tchar[] chars = templateStr.toCharArray();\n\t\tLinkedList<StringItem> stringItems = new LinkedList<StringItem>();\n\t\tint curPos = 0;\n\t\tint prevPattenEndPos = 0;\n\t\twhile (curPos < chars.length) {\n\t\t\tchar c = chars[curPos];\n\t\t\tif (c == '$' && curPos + 1 < chars.length\n\t\t\t\t\t&& chars[curPos + 1] == '{') {\n\t\t\t\tint start = curPos;\n\t\t\t\tcurPos += 2;\n\t\t\t\tint end = -1;\n\t\t\t\tif (curPos < chars.length) {\n\t\t\t\t\tfor (int i = curPos; i < chars.length; i++) {\n\t\t\t\t\t\tif (chars[i] == '}') {\n\t\t\t\t\t\t\tend = i;\n\t\t\t\t\t\t\t// found pattern\n\t\t\t\t\t\t\tif (prevPattenEndPos < start) {// some constant\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// string chars\n\t\t\t\t\t\t\t\tStringItem item = new StringItem();\n\t\t\t\t\t\t\t\titem.initString(templateStr.substring(\n\t\t\t\t\t\t\t\t\t\tprevPattenEndPos, start));\n\t\t\t\t\t\t\t\tstringItems.add(item);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// add variable pattern item\n\t\t\t\t\t\t\tstringItems.add(StringItemFactory\n\t\t\t\t\t\t\t\t\t.parseVarPattern(templateStr.substring(\n\t\t\t\t\t\t\t\t\t\t\tcurPos, end)));\n\t\t\t\t\t\t\tprevPattenEndPos = end + 1;\n\t\t\t\t\t\t\tcurPos = end + 1;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (end == -1) {\n\t\t\t\t\t\t// not found pattern end\n\t\t\t\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\t\t\t\"can't find var patten end pos ,start at \"\n\t\t\t\t\t\t\t\t\t\t+ start);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tcurPos++;\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tcurPos++;\n\t\t\t}\n\t\t}\n\t\t// add last\n\t\tif (prevPattenEndPos < templateStr.length()) {\n\t\t\tStringItem item = new StringItem();\n\t\t\titem.initString(templateStr.substring(prevPattenEndPos,\n\t\t\t\t\ttemplateStr.length()));\n\t\t\tstringItems.add(item);\n\t\t}\n\t\treturn stringItems;\n\t}\n\n\tpublic static Properties loadFromPropertyFile(String sqlFile)\n\t\t\tthrows IOException {\n\t\tjava.util.Properties pros = new Properties();\n\t\tFileInputStream fin = null;\n\t\tfin = new FileInputStream(sqlFile);\n\t\tpros.load(fin);\n\t\tfin.close();\n\t\treturn pros;\n\t}\n\n\tpublic static String evalRandValueString(LinkedList<StringItem> items) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (StringItem item : items) {\n\t\t\tsb.append(item.getValue());\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tString sqlTemplate = \"insert into opp_call (logthread, instanceid,callresult,partner, app_id,api_id,apiversion,format,token , phone,calltype, calldate,callminutes,callcost,ecipcalltime,ecipcallcost ,respcode) values ('${date(yyyyMMddHHmmssSSS-[2014-2015]y)}/psn2002s/${int(0-9999)}/${int(1111-9999)}:20725','${char([0-9]2:2)} OPP_${enum(BJ,SH,WU,GZ)}_1',10,${int(10-999)},${int(10-99)},100,3,15,'${date(yyyyMMddHHmmssSSS-[2014-2015]y}${char([a-f,0-9]8:8)}-${char([a-f,0-9]4:4)}-${char([0-9]4:4)}-9eb4-${char([a-f,0-9]12:12)}',${phone(139-189)},2,${date(yyyyMMddHH-[2014-2015]y},2315998,1397,${date(HHmmssSSS)},${int(100-1000)},'${enum(0000,0001,0002)}');\";\n\t\tSystem.out.println(\"SQL template:\\r\\n\" + sqlTemplate);\n\t\tLinkedList<StringItem> allItems = parselRandVarTemplateString(sqlTemplate);\n\t\t// for (StringItem item : allItems) {\n\t\t// System.out.println(item);\n\t\t// }\n\t\tSystem.out.println(\"Random SQLs \");\n\t\tint total = 5;\n\t\tfor (int i = 0; i < total; i++) {\n\t\t\tSystem.out.println(evalRandValueString(allItems));\n\t\t}\n\t}\n}\n\nclass StringItemFactory {\n\tprivate static final Map<String, Class<? extends StringItem>> strItemsMap = new HashMap<String, Class<? extends StringItem>>();\n\tstatic {\n\t\tstrItemsMap.put(\"date\", DateVarItem.class);\n\t\tstrItemsMap.put(\"int\", IntVarItem.class);\n\t\tstrItemsMap.put(\"char\", CharVarItem.class);\n\t\tstrItemsMap.put(\"enum\", EnumVarItem.class);\n\t\tstrItemsMap.put(\"phone\", PhoneVarItem.class);\n\n\t}\n\n\tpublic static StringItem parseVarPattern(String content)\n\t\t\tthrows InstantiationException, IllegalAccessException {\n\t\tString name = content.substring(0, content.indexOf('('));\n\t\tClass<? extends StringItem> cls = strItemsMap.get(name);\n\t\tif (cls == null) {\n\t\t\tthrow new RuntimeException(\"not find var type of  \" + name);\n\t\t}\n\t\tStringItem obj = cls.newInstance();\n\t\tobj.initString(content);\n\t\treturn obj;\n\n\t}\n\n}\n\nclass PhoneVarItem extends StringItem {\n\n\t//\n\tlong[] rang = { 13900000000L, 19900000000L };\n\n\tpublic void initString(String content) {\n\t\tint start = content.indexOf('(');\n\t\tint end = content.indexOf(')');\n\t\tString range = content.substring(start + 1, end);\n\t\tString[] items = range.split(\"-\");\n\t\trang[0] = Long.valueOf(patchLenth(items[0], 11 - items[0].length()));\n\t\trang[1] = Long.valueOf(patchLenth(items[1], 11 - items[1].length()));\n\t}\n\n\tpublic static String patchLenth(String origin, int patchlen) {\n\t\tStringBuffer sb = new StringBuffer();\n\t\tsb.append(origin);\n\t\tfor (int i = 0; i < patchlen; i++) {\n\t\t\tsb.append('0');\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic String getValue() {\n\t\tlong span = rang[1] - rang[0] + 1;\n\t\treturn Math.abs(rand.nextInt()) % span + rang[0] + \"\";\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"PhoneVarItem [rang=\" + Arrays.toString(rang) + \"]\";\n\t}\n\n}\n\nclass EnumVarItem extends StringItem {\n\t// {enum(BJ,SH,GZ,SZ)\n\tString[] enums = {};\n\n\tpublic void initString(String content) {\n\t\tint start = content.indexOf('(');\n\t\tint end = content.indexOf(')');\n\t\tString range = content.substring(start + 1, end);\n\t\tString[] items = range.split(\",\");\n\t\tenums = items;\n\n\t}\n\n\tpublic String getValue() {\n\t\treturn enums[Math.abs(rand.nextInt()) % enums.length];\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"EnumarItem [enums=\" + Arrays.toString(enums) + \"]\";\n\t}\n\n}\n\nclass CharVarItem extends StringItem {\n\n\tList<char[]> rang = new ArrayList<char[]>();\n\tint minLen = 1;\n\tint maxLen = 256;\n\n\tpublic void initString(String content) {\n\n\t\t// char([a-z]1:3)\n\t\tint start = content.indexOf('[');\n\t\tint end = content.indexOf(']');\n\t\tString[] items = content.substring(start + 1, end).split(\",\");\n\t\tfor (String itemStr : items) {\n\t\t\tif (itemStr.indexOf('-') > 0) {\n\t\t\t\tString[] pair = itemStr.split(\"-\");\n\t\t\t\tchar[] curRange = new char[2];\n\t\t\t\tcurRange[0] = pair[0].charAt(0);\n\t\t\t\tcurRange[1] = pair[1].charAt(0);\n\t\t\t\trang.add(curRange);\n\t\t\t}\n\t\t}\n\t\tint splitPos = content.indexOf(':');\n\t\tif (splitPos > 0) {\n\t\t\tint splitStart = (end == -1) ? content.indexOf('(') : end;\n\t\t\tint splitEnd = content.indexOf(')');\n\t\t\tString[] pair = content.substring(splitStart + 1, splitEnd).split(\n\t\t\t\t\t\":\");\n\t\t\tminLen = Integer.valueOf(pair[0]);\n\t\t\tmaxLen = Integer.valueOf(pair[1]);\n\t\t}\n\n\t}\n\n\tpublic String getValue() {\n\t\tint lenth = Math.abs(rand.nextInt()) % (maxLen - minLen + 1) + minLen;\n\t\tchar[] chars = new char[lenth];\n\t\tfor (int i = 0; i < chars.length; i++) {\n\t\t\tint randInt = Math.abs(rand.nextInt());\n\t\t\tint choise = randInt % rang.size();\n\t\t\tchar[] choisedRange = rang.get(choise);\n\t\t\tchar randChar = (char) (randInt\n\t\t\t\t\t% (choisedRange[1] - choisedRange[0] + 1) + choisedRange[0]);\n\t\t\tchars[i] = randChar;\n\n\t\t}\n\t\treturn new String(chars);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CharVarItem [rang=\" + rang + \", minLen=\" + minLen + \", maxLen=\"\n\t\t\t\t+ maxLen + \"]\";\n\t}\n\n}\n\nclass StringItem {\n\tprotected static final Random rand = new Random();\n\tpublic String content;\n\n\tpublic StringItem() {\n\n\t}\n\n\tpublic String getValue() {\n\t\treturn content;\n\t}\n\n\tpublic void initString(String content) {\n\t\tthis.content = content;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"StringItem [content=\" + content + \"]\";\n\t}\n}\n\nclass IntVarItem extends StringItem {\n\tlong[] rang = { 0, Integer.MAX_VALUE };\n\tlong[] enums = {};\n\tboolean isEnumInt = false;\n\n\tpublic void initString(String content) {\n\t\tint start = content.indexOf('(');\n\t\tint end = content.indexOf(')');\n\t\tif (content.indexOf('-') > 0) {\n\t\t\tString range = content.substring(start + 1, end);\n\t\t\tString[] items = range.split(\"-\");\n\t\t\trang[0] = Integer.valueOf(items[0]);\n\t\t\trang[1] = Integer.valueOf(items[1]);\n\t\t} else {\n\t\t\tisEnumInt = true;\n\t\t\tString range = content.substring(start + 1, end);\n\t\t\tString[] items = range.split(\",\");\n\t\t\tenums = new long[items.length];\n\t\t\tfor (int i = 0; i < enums.length; i++) {\n\t\t\t\tenums[i] = Long.valueOf(items[i]);\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic String getValue() {\n\t\tif (isEnumInt) {\n\t\t\treturn enums[Math.abs(rand.nextInt()) % enums.length] + \"\";\n\t\t} else {\n\t\t\tlong span = rang[1] - rang[0] + 1;\n\t\t\treturn Math.abs(rand.nextInt()) % span + rang[0] + \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"IntVarItem [rang=\" + Arrays.toString(rang) + \", enums=\"\n\t\t\t\t+ Arrays.toString(enums) + \", isEnumInt=\" + isEnumInt + \"]\";\n\t}\n}\n\nclass DateVarItem extends StringItem {\n\tString format;\n\tint[] yearRang = { 1970, 2999 };\n\tint[] monRang = { 1, 12 };\n\tint[] dayRang = { 1, 31 };\n\tint[] hourRang = { 0, 23 };\n\tint[] minuteRang = { 0, 59 };\n\tint[] secondRang = { 0, 59 };\n\tint[] sssRang = { 0, 999 };\n\n\tpublic DateVarItem() {\n\n\t}\n\n\tpublic void initString(String content) {\n\t\tint fmtEndPos = content.indexOf('-');\n\t\tif (fmtEndPos == -1) {\n\t\t\tfmtEndPos = content.indexOf(')');\n\t\t}\n\t\tformat = content.substring(5, fmtEndPos);\n\t\tint yearP = content.indexOf(\"]y\", fmtEndPos);\n\t\tif (yearP > 0) {\n\t\t\tyearRang = getRangeofPattern(content, yearP);\n\t\t}\n\n\t\tint monthP = content.indexOf(\"]M\", fmtEndPos);\n\t\tif (monthP > 0) {\n\t\t\tmonRang = getRangeofPattern(content, monthP);\n\t\t}\n\n\t\tint dayP = content.indexOf(\"]d\", fmtEndPos);\n\t\tif (dayP > 0) {\n\t\t\tdayRang = getRangeofPattern(content, dayP);\n\t\t}\n\t\tint hourP = content.indexOf(\"]H\", fmtEndPos);\n\t\tif (hourP > 0) {\n\t\t\thourRang = getRangeofPattern(content, hourP);\n\t\t}\n\t\tint minuteP = content.indexOf(\"]m\", fmtEndPos);\n\t\tif (minuteP > 0) {\n\t\t\tminuteRang = getRangeofPattern(content, minuteP);\n\t\t}\n\t\tint secondP = content.indexOf(\"]s\", fmtEndPos);\n\t\tif (secondP > 0) {\n\t\t\tsecondRang = getRangeofPattern(content, secondP);\n\t\t}\n\t\tint millisS = content.indexOf(\"]S\", fmtEndPos);\n\t\tif (millisS > 0) {\n\t\t\tsssRang = getRangeofPattern(content, millisS);\n\t\t}\n\t}\n\n\tprivate static final int[] getRangeofPattern(String theString, int endPos) {\n\t\tString subString = theString.substring(0, endPos);\n\t\tint start = subString.lastIndexOf('[');\n\t\tString range = subString.substring(start + 1, endPos);\n\t\tString[] items = range.split(\"-\");\n\t\tint[] values = new int[2];\n\t\tvalues[0] = Integer.valueOf(items[0]);\n\t\tvalues[1] = Integer.valueOf(items[1]);\n\t\treturn values;\n\t}\n\n\tpublic String getValue() {\n\t\tint yearSpan = yearRang[1] - yearRang[0] + 1;\n\t\tint year = Math.abs(rand.nextInt()) % yearSpan + yearRang[0];\n\n\t\tint monthSpan = monRang[1] - monRang[0] + 1;\n\t\tint month = Math.abs(rand.nextInt()) % monthSpan + monRang[0];\n\n\t\tint daySpan = dayRang[1] - dayRang[0] + 1;\n\t\tint day = Math.abs(rand.nextInt()) % daySpan + dayRang[0];\n\n\t\tint hourSpan = hourRang[1] - hourRang[0] + 1;\n\t\tint hour = Math.abs(rand.nextInt()) % hourSpan + hourRang[0];\n\n\t\tint minuteSpan = minuteRang[1] - minuteRang[0] + 1;\n\t\tint minute = Math.abs(rand.nextInt()) % minuteSpan + minuteRang[0];\n\n\t\tint secondSpan = secondRang[1] - secondRang[0] + 1;\n\t\tint second = Math.abs(rand.nextInt()) % secondSpan + secondRang[0];\n\n\t\tint sssSpan = sssRang[1] - sssRang[0] + 1;\n\t\tint sss = Math.abs(rand.nextInt()) % sssSpan + sssRang[0];\n\n\t\tjava.util.Calendar cl = Calendar.getInstance();\n\t\tcl.set(Calendar.YEAR, year);\n\t\tcl.set(Calendar.MONTH, month);\n\t\tcl.set(Calendar.DATE, day);\n\t\tcl.set(Calendar.HOUR_OF_DAY, hour);\n\t\tcl.set(Calendar.MINUTE, minute);\n\t\tcl.set(Calendar.SECOND, second);\n\t\tcl.set(Calendar.MILLISECOND, sss);\n\t\treturn new java.text.SimpleDateFormat(format).format(cl.getTime());\n\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DateVarItem [format=\" + format + \", yearRang=\"\n\t\t\t\t+ Arrays.toString(yearRang) + \", monRang=\"\n\t\t\t\t+ Arrays.toString(monRang) + \", dayRang=\"\n\t\t\t\t+ Arrays.toString(dayRang) + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/performance/SimpleConPool.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\nimport java.util.concurrent.CopyOnWriteArrayList;\n\npublic class SimpleConPool {\n\tprivate final String url;\n\tprivate final String user;\n\tprivate final String password;\n\tprivate CopyOnWriteArrayList<Connection> cons = new CopyOnWriteArrayList<Connection>();\n\n\tpublic SimpleConPool(String url, String user, String password, int maxCon)\n\t\t\tthrows SQLException {\n\t\tsuper();\n\t\tthis.url = url;\n\t\tthis.user = user;\n\t\tthis.password = password;\n\t\tfor (int i = 0; i < maxCon; i++) {\n\t\t\tcons.add(getCon());\n\t\t}\n\t\tSystem.out.println(\"success ful created connections ,total :\" + maxCon);\n\t}\n\n\tpublic void close() {\n\t\tfor (Connection con : this.cons) {\n\t\t\ttry {\n\t\t\t\tif (con != null && !con.isClosed()) {\n\t\t\t\t\tcon.close();\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t\tcons.clear();\n\t}\n\n\tprivate Connection getCon() throws SQLException {\n\t\tConnection theCon = DriverManager.getConnection(url, user, password);\n\t\treturn theCon;\n\t}\n\n\tpublic void returnCon(Connection con) {\n\t\ttry {\n\t\t\tif (con.isClosed()) {\n\t\t\t\tSystem.out.println(\"closed connection ,aband\");\n\t\t\t} else {\n\t\t\t\tthis.cons.add(con);\n\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\tpublic Connection getConnection() throws SQLException {\n\t\tConnection con = null;\n\t\tif (cons.isEmpty()) {\n\t\t\tSystem.out.println(\"warn no connection in pool,create new one\");\n\t\t\tcon = getCon();\n\t\t\treturn con;\n\t\t} else {\n\t\t\tcon = cons.remove(0);\n\t\t}\n\t\tif (con.isClosed()) {\n\t\t\tSystem.out.println(\"warn connection closed ,create new one\");\n\t\t\tcon = getCon();\n\n\t\t}\n\t\treturn con;\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/TestGlobalTableInsertPerf.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * @author shenzhw\n * \n */\npublic class TestGlobalTableInsertPerf  extends AbstractMultiTreadBatchTester{\n\tpublic boolean  parseArgs(String[] args) {\n\t\tif (args.length < 5) {\n\t\t\tSystem.out\n\t\t\t\t\t.println(\"input param,format: [jdbcurl] [user] [password]  [threadpoolsize]  recordcount \");\n\t\t\treturn false;\n\t\t}\n\t\turl = args[0];\n\t\tuser = args[1];\n\t\tpassword = args[2];\n\t\tthreadCount = Integer.parseInt(args[3]);\n\t\trangeItems = new String[]{\"0-\"+Integer.parseInt(args[4])};\n\t\treturn true;\n\n\t}\n\tpublic static void main(String[] args) throws Exception {\n\t       new TestGlobalTableInsertPerf().run(args);\n\t       \n\n\t\t}\n\t@Override\n\tpublic Runnable createJob(SimpleConPool conPool2, long myCount,\n\t\t\tint batch, long startId, AtomicLong finshiedCount2,\n\t\t\tAtomicLong failedCount2) {\n\t\t return new GoodsInsertJob(conPool2,\n\t\t\t\t\tmyCount, batch, startId, finshiedCount, failedCount);\n\t}\n\n\t\n\n\t\n\t\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/TestInsertGlobalSeqPerf.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * @author wuzh\n * \n */\npublic class TestInsertGlobalSeqPerf extends AbstractMultiTreadBatchTester {\n\n\tpublic static void main(String[] args) throws Exception {\n       new TestInsertGlobalSeqPerf().run(args);\n       \n\n\t}\n\n\t@Override\n\tpublic Runnable createJob(SimpleConPool conPool2, long myCount, int batch,\n\t\t\tlong startId, AtomicLong finshiedCount2,\n\t\t\tAtomicLong failedCount2) {\n\t\t  return new TravelRecordGlobalSeqInsertJob(conPool2,\n\t\t\t\t\tmyCount, batch, startId, finshiedCount, failedCount);\n\t}\n\n\t\n\n\t\n\n\n\n\t\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/TestInsertPerf.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * @author wuzh\n * \n */\npublic class TestInsertPerf extends AbstractMultiTreadBatchTester {\n\n\tpublic static void main(String[] args) throws Exception {\n       new TestInsertPerf().run(args);\n       \n\n\t}\n\n\t@Override\n\tpublic Runnable createJob(SimpleConPool conPool2, long myCount, int batch,\n\t\t\tlong startId, AtomicLong finshiedCount2,\n\t\t\tAtomicLong failedCount2) {\n\t\t  return new TravelRecordInsertJob(conPool2,\n\t\t\t\t\tmyCount, batch, startId, finshiedCount, failedCount);\n\t}\n\n\t\n\n\t\n\n\n\n\t\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/TestMaxConnection.java",
    "content": "/* Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\n\npackage io.mycat.performance;\n\nimport java.sql.SQLException;\n\npublic class TestMaxConnection {\n\tpublic static void main(String[] args) {\n\t\tif (args.length < 4) {\n\t\t\tSystem.out\n\t\t\t\t\t.println(\"input param,format: [jdbcurl] [user] [password]  [poolsize] \");\n\t\t\treturn;\n\t\t}\n\t\tString url = args[0];\n\t\tString user = args[1];\n\t\tString password = args[2];\n\t\tInteger poolsize = Integer.parseInt(args[3]);\n\t\tSimpleConPool pool = null;\n        long start=System.currentTimeMillis();\n\t\ttry {\n\t\t\tpool = new SimpleConPool(url, user, password, poolsize);\n\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\tSystem.out.println(\"success create threadpool ,used time \"+(System.currentTimeMillis()-start));\n\t\tint i = 0;\n\t\ttry {\n\t\t\tfor (i = 0; i < poolsize; i++) {\n\t\t\t\tpool.getConnection().createStatement()\n\t\t\t\t\t\t.execute(\"select * from company limit 1\");\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tSystem.out.println(\"exectute  sql err \" + i + \" err:\"\n\t\t\t\t\t+ e.toString());\n\t\t} finally {\n\t\t\tpool.close();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/performance/TestMergeSelectPerf.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * test multi node merge (min,max ,sum ,order by ,limit ) performance\n * \n * @author wuzhi\n * \n */\npublic class TestMergeSelectPerf {\n\n\tprivate static AtomicInteger finshiedCount = new AtomicInteger();\n\tprivate static AtomicInteger failedCount = new AtomicInteger();\n\n\tpublic static void addFinshed(int count) {\n\t\tfinshiedCount.addAndGet(count);\n\t}\n\n\tpublic static void addFailed(int count) {\n\t\tfailedCount.addAndGet(count);\n\t}\n\n\tprivate static Connection getCon(String url, String user, String passwd)\n\t\t\tthrows SQLException {\n\t\tConnection theCon = DriverManager.getConnection(url, user, passwd);\n\t\treturn theCon;\n\t}\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tClass.forName(\"com.mysql.jdbc.Driver\");\n\t\tif (args.length < 5) {\n\t\t\tSystem.out\n\t\t\t\t\t.println(\"input param,format: [jdbcurl] [user] [password]  [threadpoolsize]  [executetimes] \");\n\t\t\treturn;\n\t\t}\n\t\tint threadCount = 0;// 线程数\n\t\tString url = args[0];\n\t\tString user = args[1];\n\t\tString password = args[2];\n\t\tthreadCount = Integer.parseInt(args[3]);\n\t\tint executetimes = Integer.parseInt(args[4]);\n\t\tSystem.out.println(\"concerent threads:\" + threadCount);\n\t\tSystem.out.println(\"execute sql times:\" + executetimes);\n\t\tArrayList<Thread> threads = new ArrayList<Thread>(threadCount);\n\t\tArrayList<TravelRecordMergeJob> jobs = new ArrayList<TravelRecordMergeJob>(\n\t\t\t\tthreadCount);\n\t\tfor (int i = 0; i < threadCount; i++) {\n\t\t\ttry {\n\n\t\t\t\tConnection con = getCon(url, user, password);\n\t\t\t\tSystem.out.println(\"create thread \" + i);\n\t\t\t\tTravelRecordMergeJob job = new TravelRecordMergeJob(con,\n\t\t\t\t\t\texecutetimes, finshiedCount, failedCount);\n\t\t\t\tThread thread = new Thread(job);\n\t\t\t\tthreads.add(thread);\n\t\t\t\tjobs.add(job);\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(\"failed create thread \" + i + \" err \"\n\t\t\t\t\t\t+ e.toString());\n\t\t\t}\n\t\t}\n\t\tSystem.out.println(\"all thread started,waiting finsh...\");\n\t\tSystem.out.println(\"success create thread count: \" + threads.size());\n\t\tfor (Thread thread : threads) {\n\t\t\tthread.start();\n\t\t}\n\t\tlong start=System.currentTimeMillis();\n\t\tSystem.out.println(\"all thread started,waiting finsh...\");\n\t\tboolean notFinished = true;\n\t\twhile (notFinished) {\n\t\t\tnotFinished = false;\n\t\t\tfor (Thread thread : threads) {\n\t\t\t\tif (thread.isAlive()) {\n\t\t\t\t\tnotFinished = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treport(jobs);\n\t\t\tThread.sleep(1000);\n\t\t}\n\t\treport(jobs);\n\t\tSystem.out.println(\"total time :\" +(System.currentTimeMillis()-start)/1000);\n\t\t}\n\t\t\n\t\tpublic static void report(ArrayList<TravelRecordMergeJob> jobs) {\n\t\t\tint tps = 0;\n\t\t\tfor (TravelRecordMergeJob job : jobs) {\n\t\t\t\ttps += job.getTPS();\n\t\t\t}\n\t\t\tSystem.out.println(\"finishend:\" + finshiedCount.get() + \" failed:\"\n\t\t\t\t\t+ failedCount.get());\n\t\t\tSystem.out.println(\"tps:\" +tps);\n\t\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/TestMergeSorter.java",
    "content": "package io.mycat.performance;\n//package org.opencloudb.performance;\n//\n//import java.nio.ByteBuffer;\n//import java.util.Collection;\n//import java.util.HashSet;\n//import java.util.Random;\n//import java.util.Set;\n//\n//import org.opencloudb.mpp.ColMeta;\n//import org.opencloudb.mpp.OrderCol;\n//import org.opencloudb.mpp.tmp.FastRowDataSorter;\n//import org.opencloudb.mpp.tmp.MutilNodeMergeItf;\n//import org.opencloudb.mysql.BufferUtil;\n//import org.opencloudb.net.mysql.RowDataPacket;\n//\n//public class TestMergeSorter {\n//\n//\t// @Test\n//\tpublic static void main(String[] args) {\n//\t\tColMeta colMeta = new ColMeta(0, ColMeta.COL_TYPE_INT);\n//\t\tOrderCol col = new OrderCol(colMeta, OrderCol.COL_ORDER_TYPE_DESC);\n//\t\tOrderCol[] orderCols = { col };\n//\t\tMutilNodeMergeItf sorter = new FastRowDataSorter(orderCols);\n//\t\tbyte idLen = 4;\n//\t\tbyte packId = 0;\n//\t\tint maxCount = 10000;\n//\t\tint bound = maxCount * 2;\n//\t\tRandom rd = new Random();\n//\t\tSet<Integer> set = new HashSet<Integer>();\n//\t\twhile (set.size() < maxCount) {\n//\t\t\tset.add(rd.nextInt(bound));\n//\t\t}\n//\t\tfor (Integer integer : set) {\n//\t\t\tString name = \"name\".concat(String.valueOf(integer));\n//\t\t\tint length = name.length();\n//\t\t\tRowDataPacket row = new RowDataPacket(2);\n//\t\t\tByteBuffer buffer = ByteBuffer.allocate(3 + 1 + 1 + 4 + 1 + length);\n//\t\t\tBufferUtil.writeUB3(buffer, buffer.capacity());// PACKLEN\n//\t\t\tbuffer.put(packId++);// packID\n//\t\t\tbuffer.put(idLen);// LEN\n//\t\t\tBufferUtil.writeInt(buffer, integer);\n//\t\t\tbuffer.put((byte) length);\n//\t\t\tbuffer.put(name.getBytes());\n//\t\t\trow.read(buffer.array());\n//\t\t\tsorter.addRow(row);\n//\t\t}\n//\t\tset.clear();\n//\t\tSystem.gc();\n//\t\tSystem.out.println(\"add finished\");\n//\t\tfor (int i = 0; i < 100; i++) {\n//\t\t\tlong st = System.currentTimeMillis();\n//\t\t\tCollection<RowDataPacket> res = sorter.getResult();\n//\t\t\tlong end = System.currentTimeMillis();// 37.246//15.196\n//\t\t\tSystem.out.println((end - st) / 1000.0);\n//\t\t}\n//\t\t// for (RowDataPacket row : res) {\n//\t\t// byte[] x = row.fieldValues.get(0);\n//\t\t// byte[] name = row.fieldValues.get(1);\n//\t\t// ByteBuffer wrap = ByteBuffer.wrap(x);\n//\t\t// wrap.order(ByteOrder.LITTLE_ENDIAN);\n//\t\t// System.out.println(wrap.getInt()+\",\"+new String(name));\n//\t\t//\n//\t\t// }\n//\t\tsorter.close();\n//\t}\n//}\n"
  },
  {
    "path": "src/test/java/io/mycat/performance/TestRandomDataUtil.java",
    "content": "package io.mycat.performance;\n\nimport java.util.Arrays;\nimport java.util.LinkedList;\n\nimport junit.framework.Assert;\n\nimport org.junit.Test;\n\npublic class TestRandomDataUtil {\n\n\t@Test\n\tpublic void testParselRandVarTemplateString() throws Exception {\n\t\tLinkedList<StringItem>  result=RandomDataValueUtil.parselRandVarTemplateString(\"${date(yyyy-MM-dd HH [2014-2015]y-[1-6]M-[1-31]d-[7-21]H-[0-59]m-[5-33]s-[111-990]S)}\");\n\t\tAssert.assertEquals(true, result.size()==1);\n\t\tDateVarItem item=(DateVarItem) result.get(0);\n\t\tAssert.assertEquals(Arrays.toString(item.dayRang),Arrays.toString(new int[]{1,31}));\n\t\tAssert.assertEquals(Arrays.toString(item.yearRang),Arrays.toString(new int[]{2014,2015}));\n\t\tAssert.assertEquals(Arrays.toString(item.monRang),Arrays.toString(new int[]{1,6}));\n\t\tAssert.assertEquals(Arrays.toString(item.hourRang),Arrays.toString(new int[]{7,21}));\n\t\tAssert.assertEquals(Arrays.toString(item.secondRang),Arrays.toString(new int[]{5,33}));\n\t\tAssert.assertEquals(Arrays.toString(item.sssRang),Arrays.toString(new int[]{111,990}));\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/performance/TestSelectPerf.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\nimport java.text.DecimalFormat;\nimport java.util.LinkedList;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * \n * @author shenzhw\n * \n */\npublic class TestSelectPerf {\n\n\tprivate static AtomicInteger finshiedCount = new AtomicInteger();\n\tprivate static AtomicInteger failedCount = new AtomicInteger();\n\tprivate static LinkedList<StringItem> sqlTemplateItems;\n\tprivate static long minId;\n\tprivate static long maxId;\n\tprivate static int executeTimes;\n\n\tpublic static void addFinshed(int count) {\n\t\tfinshiedCount.addAndGet(count);\n\t}\n\n\tpublic static void addFailed(int count) {\n\t\tfailedCount.addAndGet(count);\n\t}\n\n\tprivate static Connection getCon(String url, String user, String passwd)\n\t\t\tthrows SQLException {\n\t\tConnection theCon = DriverManager.getConnection(url, user, passwd);\n\t\treturn theCon;\n\t}\n\n\tprivate static SelectJob createQueryJob(Connection con) {\n\t\tSelectJob job = null;\n\t\tif (sqlTemplateItems != null) {\n\t\t\tjob = new UserTableSelectJob(con, sqlTemplateItems, executeTimes,\n\t\t\t\t\tfinshiedCount, failedCount);\n\t\t} else {\n\t\t\tjob = new TravelRecordSelectJob(con, minId, maxId, executeTimes,\n\t\t\t\t\tfinshiedCount, failedCount);\n\t\t}\n\t\treturn job;\n\t}\n\n\tprivate static void doTest(String url, String user, String password,\n\t\t\tint threadCount, long minId, long maxId, int executetimes,\n\t\t\tboolean outmidle) {\n\t\tfinal CopyOnWriteArrayList<Thread> threads = new CopyOnWriteArrayList<Thread>();\n\t\tfinal CopyOnWriteArrayList<SelectJob> jobs = new CopyOnWriteArrayList<SelectJob>();\n\t\tfor (int i = 0; i < threadCount; i++) {\n\t\t\ttry {\n\t\t\t\tConnection con = getCon(url, user, password);\n\t\t\t\tSystem.out.println(\"create thread \" + i);\n\t\t\t\tSelectJob job = createQueryJob(con);\n\t\t\t\tThread thread = new Thread((Runnable) job);\n\t\t\t\tthreads.add(thread);\n\t\t\t\tjobs.add(job);\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(\"failed create thread \" + i + \" err \"\n\t\t\t\t\t\t+ e.toString());\n\t\t\t}\n\t\t}\n\t\tSystem.out.println(\"success create thread count: \" + threads.size());\n\t\tfor (Thread thread : threads) {\n\t\t\tthread.start();\n\t\t}\n\t\tSystem.out.println(\"all thread started,waiting finsh...\");\n\t\tlong start = System.currentTimeMillis();\n\t\tboolean notFinished = true;\n\t\tint remainThread = 0;\n\t\twhile (notFinished) {\n\t\t\tnotFinished = false;\n\t\t\tremainThread = 0;\n\t\t\tfor (Thread thread : threads) {\n\t\t\t\tif (thread.isAlive()) {\n\t\t\t\t\tnotFinished = true;\n\t\t\t\t\tremainThread++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (remainThread < threads.size() / 2) {\n\t\t\t\tSystem.out\n\t\t\t\t\t\t.println(\"warning many test threads finished ,qps may NOT Accurate ,alive threads:\"\n\t\t\t\t\t\t\t\t+ remainThread);\n\t\t\t}\n\t\t\tif (outmidle) {\n\t\t\t\treport(jobs);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tThread.sleep(1000);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t\treport(jobs);\n\t\tSystem.out.println(\"finished all,total time :\"\n\t\t\t\t+ (System.currentTimeMillis() - start) / 1000);\n\t}\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tClass.forName(\"com.mysql.jdbc.Driver\");\n\t\tif (args.length < 5) {\n\t\t\tSystem.out\n\t\t\t\t\t.println(\"input param,format: [jdbcurl] [user] [password]  [threadpoolsize]  [executetimes] [minId-maxId|sqlfile] [repeat]\");\n\t\t\tSystem.out\n\t\t\t\t\t.println(\"jdbc:mysql://localhost:8066/TESTDB test test 10  10000 1-1000000  1 \");\n\t\t\tSystem.out\n\t\t\t\t\t.println(\"jdbc:mysql://localhost:8066/TESTDB test test 10  10000 file=mytempate.sql  1\");\n\n\t\t\treturn;\n\t\t}\n\t\tint threadCount = 0;// 线程数\n\t\tString url = args[0];\n\t\tString user = args[1];\n\t\tString password = args[2];\n\t\tthreadCount = Integer.parseInt(args[3]);\n\n\t\tint repeate = 1;\n\n\t\texecuteTimes = Integer.parseInt(args[4]);\n\t\tSystem.out.println(\"execute sql times:\" + executeTimes);\n\t\tString param5 = args[5];\n\t\tif (param5.contains(\"file=\")) {\n\t\t\tString sqlFile = args[5].substring(args[5].indexOf('=') + 1);\n\t\t\tjava.util.Properties pros = RandomDataValueUtil\n\t\t\t\t\t.loadFromPropertyFile(sqlFile);\n\t\t\tString sqlTemplate = pros.getProperty(\"sql\");\n\t\t\tsqlTemplateItems = RandomDataValueUtil\n\t\t\t\t\t.parselRandVarTemplateString(sqlTemplate);\n\t\t} else {\n\t\t\tminId = Integer.parseInt((args[5].split(\"-\"))[0]);\n\t\t\tmaxId = Integer.parseInt((args[5].split(\"-\"))[1]);\n\t\t\tSystem.out.println(\"concerent threads:\" + threadCount);\n\n\t\t\tSystem.out.println(\"maxId:\" + maxId);\n\n\t\t}\n\t\tif (args.length > 6) {\n\t\t\trepeate = Integer.parseInt(args[6]);\n\t\t\tSystem.out.println(\"repeat test times:\" + repeate);\n\t\t}\n\t\tfor (int i = 0; i < repeate; i++) {\n\t\t\ttry {\n\t\t\t\tdoTest(url, user, password, threadCount, minId, maxId,\n\t\t\t\t\t\texecuteTimes, repeate < 2);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic static void report(CopyOnWriteArrayList<SelectJob> jobs) {\n\t\tdouble tps = 0;\n\t\tlong maxTTL = 0;\n\t\tlong minTTL = Integer.MAX_VALUE;\n\t\tlong ttlCount = 0;\n\t\tlong ttlSum = 0;\n\t\tDecimalFormat df = new DecimalFormat(\"0.00\");\n\t\tfor (SelectJob job : jobs) {\n\t\t\tdouble jobTps = job.getTPS();\n\t\t\tif (jobTps > 0) {\n\t\t\t\ttps += job.getTPS();\n\t\t\t\tif (job.getMaxTTL() > maxTTL) {\n\t\t\t\t\tmaxTTL = job.getMaxTTL();\n\t\t\t\t}\n\t\t\t\tif (job.getMinTTL() < minTTL) {\n\t\t\t\t\tminTTL = job.getMinTTL();\n\t\t\t\t}\n\t\t\t\tttlCount += job.getValidTTLCount();\n\t\t\t\tttlSum += job.getValidTTLSum();\n\t\t\t}\n\t\t}\n\t\tdouble avgSum =(ttlCount > 0) ? (ttlSum+0.0) / ttlCount : 0;\n\t\tSystem.out.println(\"finishend:\" + finshiedCount.get() + \" failed:\"\n\t\t\t\t+ failedCount.get() + \" qps:\" + df.format(tps) + \",query time min:\"\n\t\t\t\t+ minTTL + \"ms,max:\" + maxTTL + \"ms,avg:\" + df.format(avgSum) );\n\t}\n}\n\ninterface SelectJob {\n\n\tdouble getTPS();\n\n\tlong getValidTTLSum();\n\n\tlong getValidTTLCount();\n\n\tlong getMinTTL();\n\n\tlong getMaxTTL();\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/TestUpdatePerf.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * @author wuzh\n * \n */\npublic class TestUpdatePerf extends AbstractMultiTreadBatchTester {\n\tprivate int repeats = 1;\n\n\tpublic TestUpdatePerf(int repearts) {\n\t\tthis.repeats = repearts;\n\t\tif (repeats > 1) {\n\t\t\tthis.outputMiddleInf = false;\n\t\t}\n\t}\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tint repeats = 1;\n\t\tif (args.length > 5) {\n\t\t\trepeats = Integer.parseInt(args[5]);\n\t\t}\n\t\tfor (int i = 0; i < repeats; i++) {\n\t\t\tnew TestUpdatePerf(repeats).run(args);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic Runnable createJob(SimpleConPool conPool2, long myCount, int batch,\n\t\t\tlong startId, AtomicLong finshiedCount2,\n\t\t\tAtomicLong failedCount2) {\n\t\treturn new TravelRecordUpdateJob(conPool2, myCount, batch, startId,\n\t\t\t\tfinshiedCount, failedCount);\n\t}\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/TravelRecordGlobalSeqInsertJob.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicLong;\n\npublic class TravelRecordGlobalSeqInsertJob implements Runnable {\n\tprivate final long endId;\n\tprivate long finsihed;\n\tprivate final int batchSize;\n\tprivate final AtomicLong finshiedCount;\n\tprivate final AtomicLong failedCount;\n\tCalendar date = Calendar.getInstance();\n\tDateFormat datafomat = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tprivate final SimpleConPool conPool;\n\n\tpublic TravelRecordGlobalSeqInsertJob(SimpleConPool conPool, long totalRecords,\n\t\t\tint batchSize, long startId, AtomicLong finshiedCount,\n\t\t\tAtomicLong failedCount) {\n\t\tsuper();\n\t\tthis.conPool = conPool;\n\t\tthis.endId = startId + totalRecords - 1;\n\t\tthis.batchSize = batchSize;\n\t\tthis.finsihed = startId;\n\t\tthis.finshiedCount = finshiedCount;\n\t\tthis.failedCount = failedCount;\n\t}\n\n\tprivate int insert(Connection con, List<Map<String, String>> list)\n\t\t\tthrows SQLException {\n\t\tPreparedStatement ps;\n\n\t\tString sql = \"insert into travelrecord (user_id,traveldate,fee,days) values(?,?,?,?,?)\";\n\t\tps = con.prepareStatement(sql);\n\t\tfor (Map<String, String> map : list) {\n\t\t\t//ps.setLong(1, Long.parseLong(map.get(\"id\")));\n\t\t\tps.setString(1, (String) map.get(\"user_id\"));\n\t\t\tps.setString(2, (String) map.get(\"traveldate\"));\n\t\t\tps.setString(3, (String) map.get(\"fee\"));\n\t\t\tps.setString(4, (String) map.get(\"days\"));\n\t\t\tps.addBatch();\n\n\t\t}\n\t\tps.executeBatch();\n\t\tcon.commit();\n\t\tps.clearBatch();\n\t\tps.close();\n\t\treturn list.size();\n\t}\n\n\tprivate List<Map<String, String>> getNextBatch() {\n\t\tif (finsihed >= endId) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tlong end = (finsihed + batchSize) < this.endId ? (finsihed + batchSize)\n\t\t\t\t: endId;\n\t\t// the last batch\n\t\tif (end + batchSize > this.endId) {\n\t\t\tend = this.endId;\n\t\t}\n\t\tList<Map<String, String>> list = new ArrayList<Map<String, String>>(\n\t\t\t\t);\n\t\tfor (long i = finsihed; i <= end; i++) {\n\t\t\tMap<String, String> m = new HashMap<String, String>();\n\t\t\tm.put(\"id\", i + \"\");\n\t\t\tm.put(\"user_id\", \"user \" + i);\n\t\t\tm.put(\"traveldate\", getRandomDay(i));\n\t\t\tm.put(\"fee\", i % 10000 + \"\");\n\t\t\tm.put(\"days\", i % 10000 + \"\");\n\t\t\tlist.add(m);\n\t\t}\n\t\t// System.out.println(\"finsihed :\" + finsihed + \"-\" + end);\n\t\tfinsihed += list.size();\n\t\treturn list;\n\t}\n\n\tprivate String getRandomDay(long i) {\n\t\tint month = Long.valueOf(i % 11 + 1).intValue();\n\t\tint day = Long.valueOf(i % 27 + 1).intValue();\n\n\t\tdate.set(Calendar.MONTH, month);\n\t\tdate.set(Calendar.DAY_OF_MONTH, day);\n\t\treturn datafomat.format(date.getTime());\n\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tConnection con = null;\n\t\ttry {\n\n\t\t\tList<Map<String, String>> batch = getNextBatch();\n\t\t\twhile (!batch.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (con == null || con.isClosed()) {\n\t\t\t\t\t\tcon = conPool.getConnection();\n\t\t\t\t\t\tcon.setAutoCommit(false);\n\t\t\t\t\t}\n\n\t\t\t\t\tinsert(con, batch);\n\t\t\t\t\tfinshiedCount.addAndGet(batch.size());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcon.rollback();\n\t\t\t\t\t} catch (SQLException e1) {\n\t\t\t\t\t\te1.printStackTrace();\n\t\t\t\t\t\te1.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t\tfailedCount.addAndGet(batch.size());\n\t\t\t\t}\n\t\t\t\tbatch = getNextBatch();\n\t\t\t}\n\t\t} finally {\n\t\t\tif (con != null) {\n\t\t\t\tthis.conPool.returnCon(con);\n\t\t\t}\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/TravelRecordInsertJob.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicLong;\n\npublic class TravelRecordInsertJob implements Runnable {\n\tprivate final long endId;\n\tprivate long finsihed;\n\tprivate final long batchSize;\n\tprivate final AtomicLong finshiedCount;\n\tprivate final AtomicLong failedCount;\n\n\tprivate final SimpleConPool conPool;\n\n\tpublic TravelRecordInsertJob(SimpleConPool conPool, long totalRecords,\n\t\t\tlong batchSize, long startId, AtomicLong finshiedCount,\n\t\t\tAtomicLong failedCount) {\n\t\tsuper();\n\t\tthis.conPool = conPool;\n\t\tthis.endId = startId + totalRecords - 1;\n\t\tthis.batchSize = batchSize;\n\t\tthis.finsihed = startId;\n\t\tthis.finshiedCount = finshiedCount;\n\t\tthis.failedCount = failedCount;\n\t}\n\n\tprivate long insert(Connection con, List<Map<String, String>> list)\n\t\t\tthrows SQLException {\n\t\tPreparedStatement ps;\n\n\t\tString sql = \"insert into travelrecord (id,user_id,traveldate,fee,days) values(?,?,?,?,?)\";\n\t\tps = con.prepareStatement(sql);\n\t\tfor (Map<String, String> map : list) {\n\t\t\tps.setLong(1, Long.parseLong(map.get(\"id\")));\n\t\t\tps.setString(2, (String) map.get(\"user_id\"));\n\t\t\tps.setString(3, (String) map.get(\"traveldate\"));\n\t\t\tps.setString(4, (String) map.get(\"fee\"));\n\t\t\tps.setString(5, (String) map.get(\"days\"));\n\t\t\tps.addBatch();\n\t\t}\n\t\tps.executeBatch();\n\t\tcon.commit();\n\t\tps.clearBatch();\n\t\tps.close();\n\t\treturn list.size();\n\t}\n\n\tprivate List<Map<String, String>> getNextBatch() {\n\t\tif (finsihed >= endId) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tlong end = (finsihed + batchSize) < this.endId ? (finsihed + batchSize)\n\t\t\t\t: endId;\n\t\t// the last batch\n\t\tif (end + batchSize > this.endId) {\n\t\t\tend = this.endId;\n\t\t}\n\t\tList<Map<String, String>> list = new ArrayList<Map<String, String>>(\n\t\t\t\tInteger.valueOf((end - finsihed) + \"\"));\n\t\tfor (long i = finsihed; i <= end; i++) {\n\t\t\tMap<String, String> m = new HashMap<String, String>();\n\t\t\tm.put(\"id\", i + \"\");\n\t\t\tm.put(\"user_id\", \"user \" + i);\n\t\t\tm.put(\"traveldate\", getRandomDay(i));\n\t\t\tm.put(\"fee\", i % 10000 + \"\");\n\t\t\tm.put(\"days\", i % 10000 + \"\");\n\t\t\tlist.add(m);\n\t\t}\n\t\t// System.out.println(\"finsihed :\" + finsihed + \"-\" + end);\n\t\tfinsihed += list.size();\n\t\treturn list;\n\t}\n\n\tprivate String getRandomDay(long i) {\n\t\tint year = Long.valueOf(i % 10 + 2000).intValue();\n\t\tint month = Long.valueOf(i % 11 + 1).intValue();\n\t\tint day = Long.valueOf(i % 27 + 1).intValue();\n\t\treturn year + \"-\" + month + \"-\" + day;\n\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tConnection con = null;\n\t\ttry {\n\n\t\t\tList<Map<String, String>> batch = getNextBatch();\n\t\t\twhile (!batch.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (con == null || con.isClosed()) {\n\t\t\t\t\t\tcon = conPool.getConnection();\n\t\t\t\t\t\tcon.setAutoCommit(false);\n\t\t\t\t\t}\n\n\t\t\t\t\tinsert(con, batch);\n\t\t\t\t\tfinshiedCount.addAndGet(batch.size());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcon.rollback();\n\t\t\t\t\t} catch (SQLException e1) {\n\t\t\t\t\t\te1.printStackTrace();\n\t\t\t\t\t\te1.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t\tfailedCount.addAndGet(batch.size());\n\t\t\t\t}\n\t\t\t\tbatch = getNextBatch();\n\t\t\t}\n\t\t} finally {\n\t\t\tif (con != null) {\n\t\t\t\tthis.conPool.returnCon(con);\n\t\t\t}\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/TravelRecordMergeJob.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Random;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class TravelRecordMergeJob implements Runnable {\n\tprivate final Connection con;\n\tprivate final int executeTimes;\n\tCalendar date = Calendar.getInstance();\n\tDateFormat datafomat = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tRandom random = new Random();\n\tprivate final AtomicInteger finshiedCount;\n\tprivate final AtomicInteger failedCount;\n\tprivate volatile long usedTime;\n\tprivate volatile int success;\n\n\tpublic TravelRecordMergeJob(Connection con, int executeTimes,\n\t\t\tAtomicInteger finshiedCount, AtomicInteger failedCount) {\n\t\tsuper();\n\t\tthis.con = con;\n\t\tthis.executeTimes = executeTimes;\n\t\tthis.finshiedCount = finshiedCount;\n\t\tthis.failedCount = failedCount;\n\t}\n\n\tprivate void select() {\n\t\tResultSet rs = null;\n\t\ttry {\n\t\t\tString sql = \"select sum(fee) total_fee, days,count(id),max(fee),min(fee) from  travelrecord   where days = \"\n\t\t\t\t\t+ (Math.abs(random.nextInt()) % 10000)\n\t\t\t\t\t+ \" or days =\"\n\t\t\t\t\t+ (Math.abs(random.nextInt()) % 10000)+ \"  group by days  order by days desc\";\n\t\t\trs = con.createStatement().executeQuery(sql);\n\t\t\tfinshiedCount.addAndGet(1);\n\t\t\tsuccess++;\n\t\t} catch (Exception e) {\n\t\t\tfailedCount.addAndGet(1);\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tif (rs != null) {\n\t\t\t\ttry {\n\t\t\t\t\trs.close();\n\t\t\t\t} catch (SQLException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tlong start = System.currentTimeMillis();\n\t\tfor (int i = 0; i < executeTimes; i++) {\n\t\t\tthis.select();\n\t\t\tusedTime = System.currentTimeMillis() - start;\n\t\t}\n\t\ttry {\n\t\t\tcon.close();\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\tpublic long getUsedTime() {\n\t\treturn this.usedTime;\n\t}\n\n\tpublic int getTPS()\n\t{\n\t\tif(usedTime>0)\n\t\t{\n\t\treturn (int) (this.success*1000/this.usedTime);\n\t\t}else\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/TravelRecordSelectJob.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Random;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class TravelRecordSelectJob implements Runnable ,SelectJob{\n\tprivate final Connection con;\n\tprivate final long minId;\n\tprivate final long maxId;\n\tprivate final int executeTimes;\n\tRandom random = new Random();\n\tprivate final AtomicInteger finshiedCount;\n\tprivate final AtomicInteger failedCount;\n\tprivate volatile long usedTime;\n\tprivate volatile long success;\n\tprivate volatile long maxTTL = 0;\n\tprivate volatile long minTTL = Integer.MAX_VALUE;\n\tprivate volatile long validTTLSum = 0;\n\tprivate volatile long validTTLCount = 0;\n\n\tpublic TravelRecordSelectJob(Connection con, long minId, long maxId,\n\t\t\tint executeTimes, AtomicInteger finshiedCount,\n\t\t\tAtomicInteger failedCount) {\n\t\tsuper();\n\t\tthis.con = con;\n\t\tthis.minId = minId;\n\t\tthis.maxId = maxId;\n\t\tthis.executeTimes = executeTimes;\n\t\tthis.finshiedCount = finshiedCount;\n\t\tthis.failedCount = failedCount;\n\t}\n\n\tprivate long select() {\n\t\tResultSet rs = null;\n\t\tlong used = -1;\n\n\t\ttry {\n\n\t\t\tString sql = \"select * from  travelrecord  where id=\"\n\t\t\t\t\t+ ((Math.abs(random.nextLong()) % (maxId - minId)) + minId);\n\t\t\tlong startTime = System.currentTimeMillis();\n\t\t\trs = con.createStatement().executeQuery(sql);\n\t\t\tif (rs.next()) {\n\t\t\t}\n\t\t\tused = System.currentTimeMillis() - startTime;\n\t\t\tfinshiedCount.addAndGet(1);\n\t\t\tsuccess++;\n\t\t} catch (Exception e) {\n\t\t\tfailedCount.addAndGet(1);\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tif (rs != null) {\n\t\t\t\ttry {\n\t\t\t\t\trs.close();\n\t\t\t\t} catch (SQLException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\t\treturn used;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tlong curmaxTTL = this.maxTTL;\n\t\tlong curminTTL = this.minTTL;\n\t\tlong curvalidTTLSum = this.validTTLSum;\n\t\tlong curvalidTTLCount = this.validTTLCount;\n\n\t\tlong start = System.currentTimeMillis();\n\t\tfor (int i = 0; i < executeTimes; i++) {\n\n\t\t\tlong ttlTime = this.select();\n\t\t\tif (ttlTime != -1) {\n\t\t\t\tif (ttlTime > curmaxTTL) {\n\t\t\t\t\tcurmaxTTL = ttlTime;\n\t\t\t\t} else if (ttlTime < curminTTL) {\n\t\t\t\t\tcurminTTL = ttlTime;\n\t\t\t\t}\n\t\t\t\tcurvalidTTLSum += ttlTime;\n\t\t\t\tcurvalidTTLCount += 1;\n\t\t\t}\n\t\t\tusedTime = System.currentTimeMillis() - start;\n\t\t}\n\t\tmaxTTL = curmaxTTL;\n\t\tminTTL = curminTTL;\n\t\tvalidTTLSum = curvalidTTLSum;\n\t\tvalidTTLCount = curvalidTTLCount;\n\n\t\ttry {\n\t\t\tcon.close();\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tpublic long getUsedTime() {\n\t\treturn this.usedTime;\n\t}\n\n\tpublic double getTPS() {\n\t\tif (usedTime > 0) {\n\t\t\treturn  (this.success * 1000+0.0) / this.usedTime;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t\n\t\n\tpublic long getMaxTTL() {\n\t\treturn maxTTL;\n\t}\n\n\tpublic long getMinTTL() {\n\t\treturn minTTL;\n\t}\n\n\tpublic long getValidTTLSum() {\n\t\treturn validTTLSum;\n\t}\n\n\tpublic long getValidTTLCount() {\n\t\treturn validTTLCount;\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tRandom r = new Random();\n\t\tfor (int i = 0; i < 10; i++) {\n\t\t\tint f = r.nextInt(90000 - 80000) + 80000;\n\t\t\tSystem.out.println(f);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/TravelRecordUpdateJob.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicLong;\n\npublic class TravelRecordUpdateJob implements Runnable {\n\tprivate final long endId;\n\tprivate long finsihed;\n\tprivate final int batchSize;\n\tprivate final AtomicLong finshiedCount;\n\tprivate final AtomicLong failedCount;\n\tCalendar date = Calendar.getInstance();\n\tfinal SimpleConPool conPool;\n\tDateFormat datafomat = new SimpleDateFormat(\"yyyy-MM-dd\");\n\n\tpublic TravelRecordUpdateJob(SimpleConPool conPool, long totalRecords,\n\t\t\tint batchSize, long startId, AtomicLong finshiedCount,\n\t\t\tAtomicLong failedCount) {\n\t\tsuper();\n\t\tthis.conPool = conPool;\n\t\tthis.endId = startId + totalRecords - 1;\n\t\tthis.batchSize = batchSize;\n\t\tthis.finsihed = startId;\n\t\tthis.finshiedCount = finshiedCount;\n\t\tthis.failedCount = failedCount;\n\t}\n\n\tprivate int update(Connection con, List<Map<String, String>> list)\n\t\t\tthrows SQLException {\n\t\tPreparedStatement ps;\n\n\t\tString sql = \"update travelrecord set user_id =? ,traveldate=?,fee=?,days=? where id=?\";\n\t\tps = con.prepareStatement(sql);\n\t\tfor (Map<String, String> map : list) {\n\n\t\t\tps.setString(1, (String) map.get(\"user_id\"));\n\t\t\tps.setString(2, (String) map.get(\"traveldate\"));\n\t\t\tps.setString(3, (String) map.get(\"fee\"));\n\t\t\tps.setString(4, (String) map.get(\"days\"));\n\t\t\tps.setLong(5, Long.parseLong(map.get(\"id\")));\n\t\t\tps.addBatch();\n\t\t\t\n\t\t}\n\t\tps.executeBatch();\n\t\treturn list.size();\n\t}\n\n\tprivate List<Map<String, String>> getNextBatch() {\n\t\tif (finsihed >= endId) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tlong end = (finsihed + batchSize) < this.endId ? (finsihed + batchSize)\n\t\t\t\t: endId;\n\t\t// the last batch\n\t\tif (end + batchSize > this.endId) {\n\t\t\tend = this.endId;\n\t\t}\n\t\tList<Map<String, String>> list = new ArrayList<Map<String, String>>(\n\t\t\t\t);\n\t\tfor (long i = finsihed; i <= end; i++) {\n\t\t\tMap<String, String> m = new HashMap<String, String>();\n\t\t\tm.put(\"id\", i + \"\");\n\t\t\tm.put(\"user_id\", \"user \" + i);\n\t\t\tm.put(\"traveldate\", getRandomDay(i));\n\t\t\tm.put(\"fee\", i % 10000 + \"\");\n\t\t\tm.put(\"days\", i % 7 + \"\");\n\t\t\tlist.add(m);\n\t\t}\n\t\tfinsihed += list.size();\n\t\treturn list;\n\t}\n\n\tprivate String getRandomDay(long i) {\n\t\tint month = Long.valueOf(i % 11 + 1).intValue();\n\t\tint day = Long.valueOf(i % 27 + 1).intValue();\n\n\t\tdate.set(Calendar.MONTH, month);\n\t\tdate.set(Calendar.DAY_OF_MONTH, day);\n\t\treturn datafomat.format(date.getTime());\n\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tConnection con = null;\n\t\ttry {\n\n\t\t\tList<Map<String, String>> batch = getNextBatch();\n\t\t\twhile (!batch.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (con == null || con.isClosed()) {\n\t\t\t\t\t\tcon = conPool.getConnection();\n\t\t\t\t\t\tcon.setAutoCommit(true);\n\t\t\t\t\t}\n\n\t\t\t\t\tupdate(con, batch);\n\t\t\t\t\tfinshiedCount.addAndGet(batch.size());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tfailedCount.addAndGet(batch.size());\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t\tbatch = getNextBatch();\n\t\t\t}\n\t\t} finally {\n\t\t\tif (con != null) {\n\t\t\t\tthis.conPool.returnCon(con);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/UserTableInsertJob.java",
    "content": "package io.mycat.performance;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicLong;\n\npublic class UserTableInsertJob implements Runnable {\n\tprivate long finsihed;\n\tprivate final long batchSize;\n\tprivate final AtomicLong finshiedCount;\n\tprivate final AtomicLong failedCount;\n\n\tprivate final SimpleConPool conPool;\n\tprivate final long totalRecords;\n\tprivate LinkedList<StringItem> sqlTemplateItems;\n\tprivate final boolean autocommit;\n\n\tpublic UserTableInsertJob(SimpleConPool conPool, long totalRecords,\n\t\t\tlong batchSize, AtomicLong finshiedCount, AtomicLong failedCount,\n\t\t\tLinkedList<StringItem> sqlTemplateItems, boolean autoCommit) {\n\t\tsuper();\n\t\tthis.conPool = conPool;\n\t\tthis.totalRecords = totalRecords;\n\t\tthis.batchSize = batchSize;\n\t\tthis.finshiedCount = finshiedCount;\n\t\tthis.failedCount = failedCount;\n\t\tthis.sqlTemplateItems = sqlTemplateItems;\n\t\tthis.autocommit = autoCommit;\n\t}\n\n\tprivate long insert(Connection con, List<String> list) throws SQLException {\n\t\tStatement stms = con.createStatement();\n\t\tfor (String sql : list) {\n\t\t\tstms.addBatch(sql);\n\t\t}\n\t\tstms.executeBatch();\n\t\tif (this.autocommit == false) {\n\t\t\tcon.commit();\n\t\t}\n\t\t// stms.clearBatch();\n\t\tstms.close();\n\t\treturn list.size();\n\t}\n\n\tprivate List<String> getNextBatch() {\n\t\tif (finsihed >= totalRecords) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tint curBatchSize = (int) ((finsihed + batchSize) < totalRecords ? batchSize\n\t\t\t\t: (totalRecords - finsihed));\n\t\tArrayList<String> list = new ArrayList<String>(curBatchSize);\n\t\tfor (long i = 0; i < curBatchSize; i++) {\n\t\t\tlist.add(RandomDataValueUtil.evalRandValueString(sqlTemplateItems));\n\t\t}\n\t\t// System.out.println(\"finsihed :\" + finsihed + \"-\" + end);\n\t\tfinsihed += list.size();\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tConnection con = null;\n\t\ttry {\n\n\t\t\tList<String> batch = getNextBatch();\n\t\t\twhile (!batch.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (con == null || con.isClosed()) {\n\t\t\t\t\t\tcon = conPool.getConnection();\n\t\t\t\t\t\tif (con.getAutoCommit() != autocommit) {\n\t\t\t\t\t\t\tcon.setAutoCommit(autocommit);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tinsert(con, batch);\n\t\t\t\t\tfinshiedCount.addAndGet(batch.size());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tSystem.out.println(\"caught err in  thread :\"\n\t\t\t\t\t\t\t+ Thread.currentThread().getId() + \" \" + e);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcon.rollback();\n\t\t\t\t\t} catch (SQLException e1) {\n\t\t\t\t\t\tSystem.out.println(\"caught err in thread :\"\n\t\t\t\t\t\t\t\t+ Thread.currentThread().getId()\n\t\t\t\t\t\t\t\t+ \" rollback err \" + e1);\n\t\t\t\t\t}\n\t\t\t\t\tfailedCount.addAndGet(batch.size());\n\t\t\t\t}\n\t\t\t\tbatch = getNextBatch();\n\t\t\t}\n\t\t} finally {\n\t\t\tif (con != null) {\n\t\t\t\tthis.conPool.returnCon(con);\n\t\t\t}\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/performance/UserTableSelectJob.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.performance;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.LinkedList;\nimport java.util.Random;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class UserTableSelectJob implements Runnable, SelectJob {\n\tprivate final Connection con;\n\n\tprivate final int executeTimes;\n\tRandom random = new Random();\n\tprivate final AtomicInteger finshiedCount;\n\tprivate final AtomicInteger failedCount;\n\tprivate volatile long usedTime;\n\tprivate volatile long success;\n\tprivate volatile long maxTTL = 0;\n\tprivate volatile long minTTL = Integer.MAX_VALUE;\n\tprivate volatile long validTTLSum = 0;\n\tprivate volatile long validTTLCount = 0;\n\tprivate LinkedList<StringItem> sqlTemplateItems;\n\n\tpublic UserTableSelectJob(Connection con,\n\t\t\tLinkedList<StringItem> sqlTemplateItems, int executeTimes,\n\t\t\tAtomicInteger finshiedCount, AtomicInteger failedCount) {\n\t\tsuper();\n\t\tthis.con = con;\n\t\tthis.sqlTemplateItems = sqlTemplateItems;\n\t\tthis.executeTimes = executeTimes;\n\t\tthis.finshiedCount = finshiedCount;\n\t\tthis.failedCount = failedCount;\n\t}\n\n\tprivate long select() {\n\t\tResultSet rs = null;\n\t\tlong used = -1;\n\n\t\ttry {\n\t\t\tString sql = RandomDataValueUtil\n\t\t\t\t\t.evalRandValueString(sqlTemplateItems);\n\t\t\tlong startTime = System.currentTimeMillis();\n\t\t\trs = con.createStatement().executeQuery(sql);\n\t\t\tif (rs.next()) {\n\t\t\t}\n\t\t\tused = System.currentTimeMillis() - startTime;\n\t\t\tfinshiedCount.addAndGet(1);\n\t\t\tsuccess++;\n\t\t} catch (Exception e) {\n\t\t\tfailedCount.addAndGet(1);\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tif (rs != null) {\n\t\t\t\ttry {\n\t\t\t\t\trs.close();\n\t\t\t\t} catch (SQLException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\t\treturn used;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tlong curmaxTTL = this.maxTTL;\n\t\tlong curminTTL = this.minTTL;\n\t\tlong curvalidTTLSum = this.validTTLSum;\n\t\tlong curvalidTTLCount = this.validTTLCount;\n\n\t\tlong start = System.currentTimeMillis();\n\t\tfor (int i = 0; i < executeTimes; i++) {\n\n\t\t\tlong ttlTime = this.select();\n\t\t\tif (ttlTime != -1) {\n\t\t\t\tif (ttlTime > curmaxTTL) {\n\t\t\t\t\tcurmaxTTL = ttlTime;\n\t\t\t\t} else if (ttlTime < curminTTL) {\n\t\t\t\t\tcurminTTL = ttlTime;\n\t\t\t\t}\n\t\t\t\tcurvalidTTLSum += ttlTime;\n\t\t\t\tcurvalidTTLCount += 1;\n\t\t\t}\n\t\t\tusedTime = System.currentTimeMillis() - start;\n\t\t\tif (i % 100 == 0) {\n\t\t\t\tmaxTTL = curmaxTTL;\n\t\t\t\tminTTL = curminTTL;\n\t\t\t\tvalidTTLSum = curvalidTTLSum;\n\t\t\t\tvalidTTLCount = curvalidTTLCount;\n\t\t\t}\n\t\t}\n\t\tmaxTTL = curmaxTTL;\n\t\tminTTL = curminTTL;\n\t\tvalidTTLSum = curvalidTTLSum;\n\t\tvalidTTLCount = curvalidTTLCount;\n\t\ttry {\n\t\t\tcon.close();\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tpublic long getUsedTime() {\n\t\treturn this.usedTime;\n\t}\n\n\tpublic double getTPS() {\n\t\tif (usedTime > 0) {\n\t\t\treturn  (this.success * 1000+0.0) / this.usedTime;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tpublic long getMaxTTL() {\n\t\treturn maxTTL;\n\t}\n\n\tpublic long getMinTTL() {\n\t\treturn minTTL;\n\t}\n\n\tpublic long getValidTTLSum() {\n\t\treturn validTTLSum;\n\t}\n\n\tpublic long getValidTTLCount() {\n\t\treturn validTTLCount;\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tRandom r = new Random();\n\t\tfor (int i = 0; i < 10; i++) {\n\t\t\tint f = r.nextInt(90000 - 80000) + 80000;\n\t\t\tSystem.out.println(f);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/postgres/PostgresTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.postgres;\n\nimport org.junit.Test;\n\n/**\n * @author mycat\n */\npublic class PostgresTest {\n\n    @Test\n    public void testNothing() {\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/queue/FixedQueue.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.queue;\n\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * 固定容量的阻塞队列\n * \n * @author mycat\n */\npublic final class FixedQueue<E> {\n\n    private final E[] items;\n    private int putIndex;\n    private int takeIndex;\n    private int count;\n    private final ReentrantLock lock;\n\n    @SuppressWarnings(\"unchecked\")\n    public FixedQueue(int capacity) {\n        if (capacity <= 0) {\n            throw new IllegalArgumentException();\n        }\n        this.items = (E[]) new Object[capacity];\n        this.lock = new ReentrantLock();\n    }\n\n    public int size() {\n        final ReentrantLock lock = this.lock;\n        lock.lock();\n        try {\n            return count;\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public boolean offer(E e) {\n        if (e == null) {\n            throw new NullPointerException();\n        }\n        final ReentrantLock lock = this.lock;\n        lock.lock();\n        try {\n            if (count >= items.length) {\n                return false;\n            } else {\n                insert(e);\n                return true;\n            }\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public E poll() {\n        final ReentrantLock lock = this.lock;\n        lock.lock();\n        try {\n            if (count == 0) {\n                return null;\n            }\n            return extract();\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public void clear() {\n        final E[] items = this.items;\n        final ReentrantLock lock = this.lock;\n        lock.lock();\n        try {\n            int i = takeIndex;\n            int j = count;\n            while (j-- > 0) {\n                items[i] = null;\n                i = inc(i);\n            }\n            count = 0;\n            putIndex = 0;\n            takeIndex = 0;\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    private void insert(E x) {\n        items[putIndex] = x;\n        putIndex = inc(putIndex);\n        ++count;\n    }\n\n    private E extract() {\n        E[] items = this.items;\n        int i = takeIndex;\n        E x = items[i];\n        items[i] = null;\n        takeIndex = inc(i);\n        --count;\n        return x;\n    }\n\n    private int inc(int i) {\n        return (++i == items.length) ? 0 : i;\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/queue/Queue.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.queue;\n\n/**\n * @author mycat\n */\npublic final class Queue<T> {\n\n    private final static int MIN_SHRINK_SIZE = 1024;\n\n    private T[] items;\n    private int count = 0;\n    private int start = 0, end = 0;\n    private int suggestedSize, size = 0;\n\n    public Queue(int suggestedSize) {\n        this.size = this.suggestedSize = suggestedSize;\n        items = newArray(this.size);\n    }\n\n    public Queue() {\n        this(4);\n    }\n\n    public synchronized void clear() {\n        count = start = end = 0;\n        size = suggestedSize;\n        items = newArray(size);\n    }\n\n    public synchronized boolean hasElements() {\n        return (count != 0);\n    }\n\n    public synchronized int size() {\n        return count;\n    }\n\n    public synchronized void prepend(T item) {\n        if (count == size) {\n            makeMoreRoom();\n        }\n        if (start == 0) {\n            start = size - 1;\n        } else {\n            start--;\n        }\n        this.items[start] = item;\n        count++;\n        if (count == 1) {\n            notify();\n        }\n    }\n\n    public synchronized void append(T item) {\n        append0(item, count == 0);\n    }\n\n    public synchronized void appendSilent(T item) {\n        append0(item, false);\n    }\n\n    public synchronized void appendLoud(T item) {\n        append0(item, true);\n    }\n\n    public synchronized T getNonBlocking() {\n        if (count == 0) {\n            return null;\n        }\n        // pull the object off, and clear our reference to it\n        T retval = items[start];\n        items[start] = null;\n        start = (start + 1) % size;\n        count--;\n        return retval;\n    }\n\n    public synchronized void waitForItem() {\n        while (count == 0) {\n            try {\n                wait();\n            } catch (InterruptedException e) {\n            }\n        }\n    }\n\n    public synchronized T get(long maxwait) {\n        if (count == 0) {\n            try {\n                wait(maxwait);\n            } catch (InterruptedException e) {\n            }\n            if (count == 0) {\n                return null;\n            }\n        }\n        return get();\n    }\n\n    public synchronized T get() {\n        while (count == 0) {\n            try {\n                wait();\n            } catch (InterruptedException e) {\n            }\n        }\n\n        // pull the object off, and clear our reference to it\n        T retval = items[start];\n        items[start] = null;\n\n        start = (start + 1) % size;\n        count--;\n\n        // if we are only filling 1/8th of the space, shrink by half\n        if ((size > MIN_SHRINK_SIZE) && (size > suggestedSize) && (count < (size >> 3))) {\n            shrink();\n        }\n\n        return retval;\n    }\n\n    private void append0(T item, boolean notify) {\n        if (count == size) {\n            makeMoreRoom();\n        }\n        this.items[end] = item;\n        end = (end + 1) % size;\n        count++;\n        if (notify) {\n            notify();\n        }\n    }\n\n    private void makeMoreRoom() {\n        T[] items = newArray(size * 2);\n        System.arraycopy(this.items, start, items, 0, size - start);\n        System.arraycopy(this.items, 0, items, size - start, end);\n        start = 0;\n        end = size;\n        size *= 2;\n        this.items = items;\n    }\n\n    // shrink by half\n    private void shrink() {\n        T[] items = newArray(size / 2);\n        if (start > end) {\n            // the data wraps around\n            System.arraycopy(this.items, start, items, 0, size - start);\n            System.arraycopy(this.items, 0, items, size - start, end + 1);\n        } else {\n            // the data does not wrap around\n            System.arraycopy(this.items, start, items, 0, end - start + 1);\n        }\n        size = size / 2;\n        start = 0;\n        end = count;\n        this.items = items;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private T[] newArray(int size) {\n        return (T[]) new Object[size];\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder buf = new StringBuilder();\n        buf.append(\"[count=\").append(count);\n        buf.append(\", size=\").append(size);\n        buf.append(\", start=\").append(start);\n        buf.append(\", end=\").append(end);\n        buf.append(\", elements={\");\n        for (int i = 0; i < count; i++) {\n            int pos = (i + start) % size;\n            if (i > 0) {\n                buf.append(\", \");\n            }\n            buf.append(items[pos]);\n        }\n        buf.append(\"}]\");\n        return buf.toString();\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/queue/QueuePerfMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.queue;\n\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\n\nimport jsr166y.LinkedTransferQueue;\n\n/**\n * Queue 性能测试\n * \n * @author mycat\n */\npublic class QueuePerfMain {\n\n    private static byte[] testData = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };\n\n    private static BlockingQueue<byte[]> arrayQueue = new ArrayBlockingQueue<byte[]>(5000000);\n    private static FixedQueue<byte[]> fixedQueue = new FixedQueue<byte[]>(5000000);\n    private static Queue<byte[]> testQueue = new Queue<byte[]>();\n    private static BlockingQueue<byte[]> linkedQueue = new LinkedBlockingQueue<byte[]>();\n    private static LinkedTransferQueue<byte[]> transferQueue = new LinkedTransferQueue<byte[]>();\n\n    public static void tArrayQueue() {\n        new Thread() {\n\n            @Override\n            public void run() {\n                while (true) {\n                    arrayQueue.offer(testData);\n                }\n            }\n        }.start();\n\n        new Thread() {\n\n            @Override\n            public void run() {\n                int count = 0;\n                long num = 0;\n                while (true) {\n                    try {\n                        Thread.sleep(100L);\n                    } catch (InterruptedException e) {\n                    }\n                    count++;\n                    num += arrayQueue.size();\n                    arrayQueue.clear();\n                    if (count == 50) {\n                        System.out.println(num / 50);\n                        count = 0;\n                        num = 0;\n                    }\n                }\n            }\n        }.start();\n    }\n\n    public static void tFixedQueue() {\n        new Thread() {\n\n            @Override\n            public void run() {\n                while (true) {\n                    fixedQueue.offer(testData);\n                }\n            }\n        }.start();\n\n        new Thread() {\n\n            @Override\n            public void run() {\n                int count = 0;\n                long num = 0;\n                while (true) {\n                    try {\n                        Thread.sleep(100L);\n                    } catch (InterruptedException e) {\n                    }\n                    count++;\n                    num += fixedQueue.size();\n                    fixedQueue.clear();\n                    if (count == 50) {\n                        System.out.println(num / 50);\n                        count = 0;\n                        num = 0;\n                    }\n                }\n            }\n        }.start();\n    }\n\n    public static void tQueue() {\n        new Thread() {\n\n            @Override\n            public void run() {\n                while (true) {\n                    testQueue.append(testData);\n                }\n            }\n        }.start();\n\n        new Thread() {\n\n            @Override\n            public void run() {\n                int count = 0;\n                long num = 0;\n                while (true) {\n                    try {\n                        Thread.sleep(100L);\n                    } catch (InterruptedException e) {\n                    }\n                    count++;\n                    num += testQueue.size();\n                    testQueue.clear();\n                    if (count == 50) {\n                        System.out.println(num / 50);\n                        count = 0;\n                        num = 0;\n                    }\n                }\n            }\n        }.start();\n    }\n\n    public static void tLinkedQueue() {\n        new Thread() {\n\n            @Override\n            public void run() {\n                while (true) {\n                    linkedQueue.offer(testData);\n                }\n            }\n        }.start();\n\n        new Thread() {\n\n            @Override\n            public void run() {\n                int count = 0;\n                long num = 0;\n                while (true) {\n                    try {\n                        Thread.sleep(100L);\n                    } catch (InterruptedException e) {\n                    }\n                    count++;\n                    num += linkedQueue.size();\n                    linkedQueue.clear();\n                    if (count == 50) {\n                        System.out.println(num / 50);\n                        count = 0;\n                        num = 0;\n                    }\n                }\n            }\n        }.start();\n    }\n\n    public static void tTransferQueue() {\n        new Thread() {\n\n            @Override\n            public void run() {\n                while (true) {\n                    transferQueue.offer(testData);\n                }\n            }\n        }.start();\n\n        new Thread() {\n\n            @Override\n            public void run() {\n                int count = 0;\n                long num = 0;\n                while (true) {\n                    try {\n                        Thread.sleep(100L);\n                    } catch (InterruptedException e) {\n                    }\n                    count++;\n                    num += transferQueue.size();\n                    transferQueue.clear();\n                    if (count == 50) {\n                        System.out.println(num / 50);\n                        count = 0;\n                        num = 0;\n                    }\n                }\n            }\n        }.start();\n    }\n\n    public static void main(String[] args) {\n        // testArrayQueue();\n        // testFixedQueue();\n        // testQueue();\n        // testLinkedQueue();\n        // testTransferQueue();\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/queue/QueueSimpleMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.queue;\n\nimport jsr166y.LinkedTransferQueue;\n\n/**\n * @author mycat\n */\npublic class QueueSimpleMain {\n\n    static long putCount = 0;\n    static long takeCount = 0;\n\n    public static void main(String[] args) {\n        // final SynchronousQueue<String> queue = new\n        // SynchronousQueue<String>();\n        // final ArrayBlockingQueue<String> queue = new\n        // ArrayBlockingQueue<String>(10000000);\n        final LinkedTransferQueue<String> queue = new LinkedTransferQueue<String>();\n        // final LinkedBlockingQueue<String> queue = new\n        // LinkedBlockingQueue<String>();\n\n        new Thread() {\n            @Override\n            public void run() {\n                for (;;) {\n                    long put = putCount;\n                    long take = takeCount;\n                    try {\n                        Thread.sleep(5000L);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    System.out.println(\"put:\" + (putCount - put) / 5 + \" take:\" + (takeCount - take) / 5);\n                }\n            }\n        }.start();\n\n        new Thread() {\n            @Override\n            public void run() {\n                for (;;) {\n                    if (queue.offer(\"A\")) {\n                        putCount++;\n                    }\n                }\n            }\n        }.start();\n\n        new Thread() {\n            @Override\n            public void run() {\n                for (;;) {\n                    // try {\n                    if (queue.poll() != null) {\n                        takeCount++;\n                    }\n                    // } catch (InterruptedException e) {\n                    // e.printStackTrace();\n                    // }\n                    // try {\n                    // Thread.sleep(10L);\n                    // } catch (InterruptedException e) {\n                    // \n                    // e.printStackTrace();\n                    // }\n                }\n            }\n        }.start();\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/.gitignore",
    "content": "/DruidMysqlSqlSubqueriesParserTest.java\n"
  },
  {
    "path": "src/test/java/io/mycat/route/DDLRouteTest.java",
    "content": "package io.mycat.route;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.Test;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.CacheService;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteService;\nimport io.mycat.route.RouteStrategy;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.route.util.RouterUtil;\nimport io.mycat.server.parser.ServerParse;\nimport junit.framework.Assert;\n\npublic class DDLRouteTest {\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n\tprotected RouteStrategy routeStrategy ;\n\n\tpublic DDLRouteTest() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n\t\tMycatServer.getInstance().getConfig().getSystem().setUseGlobleTableCheck(0); //DDL Route Test  不开启全局表一致性检查\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n\t}\n\n\t\n\t @Test\n\t public void testSpecialCharDDL() throws Exception {\n\t\t SchemaConfig schema = schemaMap.get(\"TESTDB\");\n\t\t\tCacheService cacheService = new CacheService();\n\t        RouteService routerService = new RouteService(cacheService);\n\t        \n\t        // alter table test\n\t        String  sql = \" ALTER TABLE COMPANY\\r\\nADD COLUMN TEST  VARCHAR(255) NULL AFTER CREATE_DATE,\\r\\n CHARACTER SET = UTF8\";\n\t        sql = RouterUtil.getFixedSql(sql);\n\t        List<String> dataNodes = new ArrayList<>();\n\t        String  tablename =  RouterUtil.getTableName(sql, RouterUtil.getAlterTablePos(sql, 0));\n\t        Map<String, TableConfig>  tables = schema.getTables();\n\t        TableConfig tc;\n\t        if (tables != null && (tc = tables.get(tablename)) != null) {\n\t            dataNodes = tc.getDataNodes();\n\t        }\n\t        int nodeSize  = dataNodes.size();\n\n\t        int rs = ServerParse.parse(sql);\n\t        int sqlType = rs & 0xff;\n\t        RouteResultset rrs = routerService.route(new SystemConfig(), schema, sqlType, sql, \"UTF-8\", null);\n\t        Assert.assertTrue(\"COMPANY\".equals(tablename));\n\t        Assert.assertTrue(rrs.getNodes().length == nodeSize);\n\t }\n\t\n\n\t/**\n     * ddl deal test\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testDDL() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n\t\tCacheService cacheService = new CacheService();\n        RouteService routerService = new RouteService(cacheService);\n        \n        // create table/view/function/..\n        String sql = \" create table company(idd int)\";\n        sql = RouterUtil.getFixedSql(sql);\n        String upsql = sql.toUpperCase();\n        \n        //TODO : modify by zhuam\n        // 小写表名，需要额外转为 大写 做比较\n        String tablename =  RouterUtil.getTableName(sql, RouterUtil.getCreateTablePos(upsql, 0));\n        tablename = tablename.toUpperCase();\n        \n        List<String> dataNodes = new ArrayList<>();\n        Map<String, TableConfig> tables = schema.getTables();\n        TableConfig tc;\n        if (tables != null && (tc = tables.get(tablename)) != null) {\n            dataNodes = tc.getDataNodes();\n        }\n        int nodeSize = dataNodes.size();\n\n        int rs = ServerParse.parse(sql);\n\t\tint sqlType = rs & 0xff;\n        RouteResultset rrs = routerService.route(new SystemConfig(), schema, sqlType, sql, \"UTF-8\", null);\n        Assert.assertTrue(\"COMPANY\".equals(tablename));\n        Assert.assertTrue(rrs.getNodes().length == nodeSize);\n\n        // drop table test\n        sql = \" drop table COMPANY\";\n        sql = RouterUtil.getFixedSql(sql);\n        upsql = sql.toUpperCase();\n        \n        tablename =  RouterUtil.getTableName(sql, RouterUtil.getDropTablePos(upsql, 0));\n        tables = schema.getTables();\n        if (tables != null && (tc = tables.get(tablename)) != null) {\n            dataNodes = tc.getDataNodes();\n        }\n        nodeSize = dataNodes.size();\n\n        rs = ServerParse.parse(sql);\n\t\tsqlType = rs & 0xff;\n        rrs = routerService.route(new SystemConfig(), schema, sqlType, sql, \"UTF-8\", null);\n        Assert.assertTrue(\"COMPANY\".equals(tablename));\n        Assert.assertTrue(rrs.getNodes().length == nodeSize);\n\n        //alter table\n        sql = \"   alter table COMPANY add COLUMN name int ;\";\n        sql = RouterUtil.getFixedSql(sql);\n        upsql = sql.toUpperCase();\n        tablename =  RouterUtil.getTableName(sql, RouterUtil.getAlterTablePos(upsql, 0));\n        tables = schema.getTables();\n        if (tables != null && (tc = tables.get(tablename)) != null) {\n            dataNodes = tc.getDataNodes();\n        }\n        nodeSize = dataNodes.size();\n        rs = ServerParse.parse(sql);\n\t\tsqlType = rs & 0xff;\n        rrs = routerService.route(new SystemConfig(), schema, sqlType, sql, \"UTF-8\", null);\n        Assert.assertTrue(\"COMPANY\".equals(tablename));\n        Assert.assertTrue(rrs.getNodes().length == nodeSize);\n\n        //truncate table;\n        sql = \" truncate table COMPANY\";\n        sql = RouterUtil.getFixedSql(sql);\n        upsql = sql.toUpperCase();\n        tablename =  RouterUtil.getTableName(sql, RouterUtil.getTruncateTablePos(upsql, 0));\n        tables = schema.getTables();\n        if (tables != null && (tc = tables.get(tablename)) != null) {\n            dataNodes = tc.getDataNodes();\n        }\n        nodeSize = dataNodes.size();\n        rs = ServerParse.parse(sql);\n\t\tsqlType = rs & 0xff;\n        rrs = routerService.route(new SystemConfig(), schema, sqlType, sql, \"UTF-8\", null);\n        Assert.assertTrue(\"COMPANY\".equals(tablename));\n        Assert.assertTrue(rrs.getNodes().length == nodeSize);\n\n\n\n\n    }\n\n\n\n    @Test\n    public void testDDLDefaultNode() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"solo1\");\n        CacheService cacheService = new CacheService();\n        RouteService routerService = new RouteService(cacheService);\n\n        // create table/view/function/..\n        String sql = \" create table company(idd int)\";\n        sql = RouterUtil.getFixedSql(sql);\n        String upsql = sql.toUpperCase();\n        \n        //TODO：modify by zhuam 小写表名，转为大写比较\n        String tablename =  RouterUtil.getTableName(sql, RouterUtil.getCreateTablePos(upsql, 0));\n        tablename = tablename.toUpperCase();        \n        \n        List<String> dataNodes = new ArrayList<>();\n        Map<String, TableConfig> tables = schema.getTables();\n        TableConfig tc;\n        if (tables != null && (tc = tables.get(tablename)) != null) {\n            dataNodes = tc.getDataNodes();\n        }\n        int nodeSize = dataNodes.size();\n        if (nodeSize==0&& schema.getDataNode()!=null){\n            nodeSize = 1;\n        }\n\n        int rs = ServerParse.parse(sql);\n        int sqlType = rs & 0xff;\n        RouteResultset rrs = routerService.route(new SystemConfig(), schema, sqlType, sql, \"UTF-8\", null);\n        Assert.assertTrue(\"COMPANY\".equals(tablename));\n        Assert.assertTrue(rrs.getNodes().length == nodeSize);\n\n        // drop table test\n        sql = \" drop table COMPANY\";\n        sql = RouterUtil.getFixedSql(sql);\n        upsql = sql.toUpperCase();\n        tablename =  RouterUtil.getTableName(sql, RouterUtil.getDropTablePos(upsql, 0));\n        tables = schema.getTables();\n        if (tables != null && (tc = tables.get(tablename)) != null) {\n            dataNodes = tc.getDataNodes();\n        }\n        nodeSize = dataNodes.size();\n        if (nodeSize==0&& schema.getDataNode()!=null){\n            nodeSize = 1;\n        }\n        rs = ServerParse.parse(sql);\n        sqlType = rs & 0xff;\n        rrs = routerService.route(new SystemConfig(), schema, sqlType, sql, \"UTF-8\", null);\n        Assert.assertTrue(\"COMPANY\".equals(tablename));\n        Assert.assertTrue(rrs.getNodes().length == nodeSize);\n\n        // drop table test\n        sql = \" drop table if exists COMPANY\";\n        sql = RouterUtil.getFixedSql(sql);\n        upsql = sql.toUpperCase();\n        tablename =  RouterUtil.getTableName(sql, RouterUtil.getDropTablePos(upsql, 0));\n        tables = schema.getTables();\n        if (tables != null && (tc = tables.get(tablename)) != null) {\n            dataNodes = tc.getDataNodes();\n        }\n        nodeSize = dataNodes.size();\n        if (nodeSize==0&& schema.getDataNode()!=null){\n            nodeSize = 1;\n        }\n        rs = ServerParse.parse(sql);\n        sqlType = rs & 0xff;\n        rrs = routerService.route(new SystemConfig(), schema, sqlType, sql, \"UTF-8\", null);\n        Assert.assertTrue(\"COMPANY\".equals(tablename));\n        Assert.assertTrue(rrs.getNodes().length == nodeSize);\n\n        //alter table\n        sql = \"   alter table COMPANY add COLUMN name int ;\";\n        sql = RouterUtil.getFixedSql(sql);\n        upsql = sql.toUpperCase();\n        tablename =  RouterUtil.getTableName(sql, RouterUtil.getAlterTablePos(upsql, 0));\n        tables = schema.getTables();\n        if (tables != null && (tc = tables.get(tablename)) != null) {\n            dataNodes = tc.getDataNodes();\n        }\n        nodeSize = dataNodes.size();\n\n        if (nodeSize==0&& schema.getDataNode()!=null){\n            nodeSize = 1;\n        }\n        rs = ServerParse.parse(sql);\n        sqlType = rs & 0xff;\n        rrs = routerService.route(new SystemConfig(), schema, sqlType, sql, \"UTF-8\", null);\n        Assert.assertTrue(\"COMPANY\".equals(tablename));\n        Assert.assertTrue(rrs.getNodes().length == nodeSize);\n\n        //truncate table;\n        sql = \" truncate table COMPANY\";\n        sql = RouterUtil.getFixedSql(sql);\n        upsql = sql.toUpperCase();\n        tablename =  RouterUtil.getTableName(sql, RouterUtil.getTruncateTablePos(upsql, 0));\n        tables = schema.getTables();\n        if (tables != null && (tc = tables.get(tablename)) != null) {\n            dataNodes = tc.getDataNodes();\n        }\n        nodeSize = dataNodes.size();\n\n        if (nodeSize==0&& schema.getDataNode()!=null){\n            nodeSize = 1;\n        }\n\n        rs = ServerParse.parse(sql);\n        sqlType = rs & 0xff;\n        rrs = routerService.route(new SystemConfig(), schema, sqlType, sql, \"UTF-8\", null);\n        Assert.assertTrue(\"COMPANY\".equals(tablename));\n        Assert.assertTrue(rrs.getNodes().length == nodeSize);\n\n\n    }\n\n\n\n//    @Test\n//    public void testTableMetaRead() throws Exception {\n//        final SchemaConfig schema = schemaMap.get(\"cndb\");\n//\n//        String sql = \"desc offer\";\n//        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.DESCRIBE, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        // random return one node\n//        // Assert.assertEquals(\"offer_dn[0]\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"desc offer\", rrs.getNodes()[0].getStatement());\n//\n//        sql = \" desc cndb.offer\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.DESCRIBE, sql, null, null, cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        // random return one node\n//        // Assert.assertEquals(\"offer_dn[0]\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"desc offer\", rrs.getNodes()[0].getStatement());\n//\n//        sql = \" desc cndb.offer col1\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.DESCRIBE, sql, null, null, cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        // random return one node\n//        // Assert.assertEquals(\"offer_dn[0]\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"desc offer col1\", rrs.getNodes()[0].getStatement());\n//\n//        sql = \"SHOW FULL COLUMNS FROM  offer  IN db_name WHERE true\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SHOW, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        // random return one node\n//        // Assert.assertEquals(\"offer_dn[0]\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"SHOW FULL COLUMNS FROM offer WHERE true\",\n//                rrs.getNodes()[0].getStatement());\n//\n//        sql = \"SHOW FULL COLUMNS FROM  db.offer  IN db_name WHERE true\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SHOW, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        // random return one node\n//        // Assert.assertEquals(\"offer_dn[0]\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"SHOW FULL COLUMNS FROM offer WHERE true\",\n//                rrs.getNodes()[0].getStatement());\n//\n//\n//        sql = \"SHOW FULL TABLES FROM `TESTDB` WHERE Table_type != 'VIEW'\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SHOW, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(\"SHOW FULL TABLES WHERE Table_type != 'VIEW'\", rrs.getNodes()[0].getStatement());\n//\n//        sql = \"SHOW INDEX  IN offer FROM  db_name\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SHOW, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        // random return one node\n//        // Assert.assertEquals(\"offer_dn[0]\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"SHOW INDEX  FROM offer\",\n//                rrs.getNodes()[0].getStatement());\n//    }\n    \n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/DQLRouteTest.java",
    "content": "package io.mycat.route;\n\nimport java.lang.reflect.Method;\nimport java.sql.SQLSyntaxErrorException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.Test;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\nimport com.alibaba.druid.stat.TableStat.Condition;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.route.parser.druid.DruidShardingParseInfo;\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\nimport io.mycat.route.parser.druid.MycatStatementParser;\nimport io.mycat.route.parser.druid.RouteCalculateUnit;\nimport junit.framework.Assert;\n\npublic class DQLRouteTest {\n\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n\tprotected RouteStrategy routeStrategy;\n\tprivate Map<String, String> tableAliasMap = new HashMap<String, String>();\n\n\tprotected DruidShardingParseInfo ctx;\n\n\tpublic DQLRouteTest() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n\t}\n\n\t@Test\n\tpublic void test() throws Exception {\n\t\tString stmt = \"select * from `offer` where id = 100\";\n\t\tSchemaConfig schema = schemaMap.get(\"mysqldb\");\n\t\tRouteResultset rrs = new RouteResultset(stmt, 7);\n\t\tSQLStatementParser parser = null;\n\t\tif (schema.isNeedSupportMultiDBType()) {\n\t\t\tparser = new MycatStatementParser(stmt);\n\t\t} else {\n\t\t\tparser = new MySqlStatementParser(stmt);\n\t\t}\n\t\tSQLStatement statement;\n\t\tMycatSchemaStatVisitor visitor = null;\n\n\t\ttry {\n\t\t\tstatement = parser.parseStatement();\n\t\t\tvisitor = new MycatSchemaStatVisitor();\n\t\t} catch (Exception t) {\n\t\t\tthrow new SQLSyntaxErrorException(t);\n\t\t}\n\t\tctx = new DruidShardingParseInfo();\n\t\tctx.setSql(stmt);\n\n\t\tList<RouteCalculateUnit> taskList = visitorParse(rrs, statement, visitor);\n\t\tAssert.assertEquals(true, !taskList.get(0).getTablesAndConditions().isEmpty());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate List<RouteCalculateUnit> visitorParse(RouteResultset rrs, SQLStatement stmt, MycatSchemaStatVisitor visitor) throws Exception {\n\n\t\tstmt.accept(visitor);\n\n\t\tList<List<Condition>> mergedConditionList = new ArrayList<List<Condition>>();\n\t\tif (visitor.hasOrCondition()) {// 包含or语句\n\t\t\t// TODO\n\t\t\t// 根据or拆分\n\t\t\tmergedConditionList = visitor.splitConditions();\n\t\t} else {// 不包含OR语句\n\t\t\tmergedConditionList.add(visitor.getConditions());\n\t\t}\n\n\t\tif (visitor.getAliasMap() != null) {\n\t\t\tfor (Map.Entry<String, String> entry : visitor.getAliasMap().entrySet()) {\n\t\t\t\tString key = entry.getKey();\n\t\t\t\tString value = entry.getValue();\n\t\t\t\tif (key != null && key.indexOf(\"`\") >= 0) {\n\t\t\t\t\tkey = key.replaceAll(\"`\", \"\");\n\t\t\t\t}\n\t\t\t\tif (value != null && value.indexOf(\"`\") >= 0) {\n\t\t\t\t\tvalue = value.replaceAll(\"`\", \"\");\n\t\t\t\t}\n\t\t\t\t// 表名前面带database的，去掉\n\t\t\t\tif (key != null) {\n\t\t\t\t\tint pos = key.indexOf(\".\");\n\t\t\t\t\tif (pos > 0) {\n\t\t\t\t\t\tkey = key.substring(pos + 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (key.equals(value)) {\n\t\t\t\t\tctx.addTable(key.toUpperCase());\n\t\t\t\t}\n\t\t\t\t// else {\n\t\t\t\t// tableAliasMap.put(key, value);\n\t\t\t\t// }\n\t\t\t\ttableAliasMap.put(key.toUpperCase(), value);\n\t\t\t}\n\t\t\tvisitor.getAliasMap().putAll(tableAliasMap);\n\t\t\tctx.setTableAliasMap(tableAliasMap);\n\t\t}\n\n\t\t//利用反射机制单元测试DefaultDruidParser类的私有方法buildRouteCalculateUnits\n\t\tClass<?> clazz = Class.forName(\"io.mycat.route.parser.druid.impl.DefaultDruidParser\");\n\t\tMethod buildRouteCalculateUnits = clazz.getDeclaredMethod(\"buildRouteCalculateUnits\",\n\t\t\t\tnew Class[] { MycatSchemaStatVisitor.class, List.class });\n\t\t//System.out.println(\"buildRouteCalculateUnits:\\t\" + buildRouteCalculateUnits);\n\t\tObject newInstance = clazz.newInstance();\n\t\tbuildRouteCalculateUnits.setAccessible(true);\n\t\tObject returnValue = buildRouteCalculateUnits.invoke(newInstance,\n\t\t\t\tnew Object[] { visitor, mergedConditionList });\n\t\tList<RouteCalculateUnit> retList = new ArrayList<RouteCalculateUnit>();\n\t\tif (returnValue instanceof ArrayList<?>) {\n\t\t\tretList.add(((ArrayList<RouteCalculateUnit>)returnValue).get(0));\n\t\t\t//retList = (ArrayList<RouteCalculateUnit>)returnValue;\n\t\t\t//System.out.println(taskList.get(0).getTablesAndConditions().values());\t\t\t\n\t\t}\n\t\treturn retList;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/DeleteSqlParseTest.java",
    "content": "package io.mycat.route;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport org.junit.Test;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport junit.framework.Assert;\n\n/**\n * 测试删除\n * \n * @author huangyiming\n *\n */\npublic class DeleteSqlParseTest {\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n    protected RouteStrategy routeStrategy;\n\n\tpublic DeleteSqlParseTest() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n\t}\n\n\t@Test\n\tpublic void testDeleteToRoute() throws SQLNonTransientException {\n\t\tString sql = \"delete t  from offer as t  \";\n\t\tSchemaConfig schema = schemaMap.get(\"config\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(128, rrs.getNodes().length);\n        \n\t}\n\n\n\n    \n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/DruidDb2SqlParserTest.java",
    "content": "package io.mycat.route;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport org.junit.Test;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport junit.framework.Assert;\n\npublic class DruidDb2SqlParserTest\n{\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n    protected RouteStrategy routeStrategy;\n\n\tpublic DruidDb2SqlParserTest() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n\t}\n\n\t@Test\n\tpublic void testLimitToDb2Page() throws SQLNonTransientException {\n\t\tString sql = \"select * from offer order by id desc limit 5,10\";\n\t\tSchemaConfig schema = schemaMap.get(\"db2db\");\n\t\tRouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n\t                null, cachePool);        \n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"db2_1\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(\"db2_2\", rrs.getNodes()[1].getName());\n\n        sql= rrs.getNodes()[0].getStatement() ;\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(0, rrs.getLimitStart());\n        Assert.assertEquals(15, rrs.getLimitSize());\n\t\t\n        sql=\"select * from offer1 order by id desc limit 5,10\" ;\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(5, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"db2_1\", rrs.getNodes()[0].getName());\n\n\t}\n\n\n\n\t@Test\n\tpublic void testDb2PageSQL() throws SQLNonTransientException {\n\t\tString sql = \"SELECT *\\n\" + \"FROM (SELECT sid, ROW_NUMBER() OVER (ORDER BY sid DESC) AS ROWNUM\\n\"\n\t\t\t\t+ \"\\tFROM offer \\n\" + \"\\tWHERE sts <> 'N'\\n\" + \"\\t\\t\\t) XX\\n\" + \"WHERE ROWNUM > 5\\n\"\n\t\t\t\t+ \"\\tAND ROWNUM <= 15\\n\";\n\t\tSchemaConfig schema = schemaMap.get(\"db2db\");\n\t\tRouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n\t\tAssert.assertEquals(2, rrs.getNodes().length);\n\t\tAssert.assertEquals(5, rrs.getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getLimitSize());\n\t\tAssert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n\t\tAssert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n\t\tAssert.assertEquals(\"db2_1\", rrs.getNodes()[0].getName());\n\t\tAssert.assertEquals(\"db2_2\", rrs.getNodes()[1].getName());\n\n\t\tsql = \"SELECT *\\n\" + \"FROM (SELECT sid, ROW_NUMBER() OVER (ORDER BY sid DESC) AS ROWNUM\\n\" + \"\\tFROM offer1 \\n\"\n\t\t\t\t+ \"\\tWHERE sts <> 'N'\\n\" + \"\\t\\t\\t) XX\\n\" + \"WHERE ROWNUM > 5\\n\" + \"\\tAND ROWNUM <= 15\\n\";\n\t\trrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n\t\tAssert.assertEquals(1, rrs.getNodes().length);\n\t\tAssert.assertEquals(5, rrs.getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getLimitSize());\n\t\tAssert.assertEquals(5, rrs.getNodes()[0].getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n\t\tAssert.assertEquals(sql, rrs.getNodes()[0].getStatement());\n\t\tAssert.assertEquals(\"db2_1\", rrs.getNodes()[0].getName());\n\n\t\tsql = \"SELECT sid\\n\" + \"FROM offer  \\n\" + \"ORDER BY sid desc\\n\" + \"FETCH FIRST 10  ROWS ONLY\";\n\t\trrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n\t\tAssert.assertEquals(2, rrs.getNodes().length);\n\t\tAssert.assertEquals(0, rrs.getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getLimitSize());\n\t\tAssert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n\t\tAssert.assertEquals(sql, rrs.getNodes()[0].getStatement());\n\t\tAssert.assertEquals(\"db2_1\", rrs.getNodes()[0].getName());\n\t\tAssert.assertEquals(\"db2_2\", rrs.getNodes()[1].getName());\n\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/DruidMysqlCreateTableTest.java",
    "content": "package io.mycat.route;\n\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.SQLName;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;\nimport com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLTableElement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.server.interceptor.impl.GlobalTableUtil;\nimport io.mycat.util.StringUtil;\nimport junit.framework.Assert;\nimport org.junit.Test;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.List;\nimport java.util.Map;\n\npublic class DruidMysqlCreateTableTest\n{\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n    protected RouteStrategy routeStrategy;\n    private static final String originSql1 = \"CREATE TABLE autoslot\"\n            + \"(\"\n            + \"\tID BIGINT AUTO_INCREMENT,\"\n            + \"\tCHANNEL_ID INT(11),\"\n            + \"\tCHANNEL_INFO varchar(128),\"\n            + \"\tCONSTRAINT RETL_MARK_ID PRIMARY KEY (ID)\"\n            + \") ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\";\n\n\n    public DruidMysqlCreateTableTest() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n\t}\n\n\t@Test\n\tpublic void testCreate() throws SQLNonTransientException {\n\n\t\tSchemaConfig schema = schemaMap.get(\"mysqldb\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, originSql1, null,\n                null, cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n      String sql=  rrs.getNodes()[0].getStatement();\n\n        Assert.assertTrue(parseSql(sql));\n\n\n\t\t\n\n\n\t}\n\n   // @Test\n    public void testInsert() throws SQLNonTransientException {\n\n        SchemaConfig schema = schemaMap.get(\"mysqldb\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, \"insert into autoslot (id,sid) values(1,2) \", null,\n                null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n\n        Assert.assertTrue(isInsertHasSlot(rrs.getStatement()));\n\n\n\n\n\n    }\n\n    private boolean isInsertHasSlot(String sql)\n    {\n        MySqlStatementParser parser = new MySqlStatementParser(sql);\n        MySqlInsertStatement insertStatement= (MySqlInsertStatement)parser.parseStatement();\n     List<SQLExpr> cc= insertStatement.getColumns();\n        for (SQLExpr sqlExpr : cc) {\n            SQLIdentifierExpr c= (SQLIdentifierExpr) sqlExpr;\n            if(\"_slot\".equalsIgnoreCase(c.getName())   &&cc.size()==insertStatement.getValues().getValues().size())    return true;\n        }\n        return false;\n    }\n\n    public boolean parseSql(String sql) {\n        MySqlStatementParser parser = new MySqlStatementParser(sql);\n        SQLStatement statement = parser.parseStatement();\n        return hasColumn(statement);\n    }\n\n    private static boolean hasColumn(SQLStatement statement){\n        for (SQLTableElement tableElement : ((SQLCreateTableStatement)statement).getTableElementList()) {\n            SQLName sqlName = null;\n            if (tableElement instanceof SQLColumnDefinition) {\n                sqlName = ((SQLColumnDefinition)tableElement).getName();\n            }\n            if (sqlName != null) {\n                String simpleName = sqlName.getSimpleName();\n                simpleName = StringUtil.removeBackquote(simpleName);\n                if (tableElement instanceof SQLColumnDefinition && \"_slot\".equalsIgnoreCase(simpleName)) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/DruidMysqlHavingTest.java",
    "content": "package io.mycat.route;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport org.junit.Test;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport junit.framework.Assert;\n\npublic class DruidMysqlHavingTest\n{\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n    protected RouteStrategy routeStrategy;\n\n\tpublic DruidMysqlHavingTest() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n\t\tRouteStrategyFactory.init();\n\t\trouteStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n\t}\n\n\t@Test\n\tpublic void testHaving() throws SQLNonTransientException {\n\t\tString sql = \"select avg(offer_id) avgofferid, member_id from offer_detail group by member_id having avgofferid > 100\";\n\t\tSchemaConfig schema = schemaMap.get(\"cndb\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(3, rrs.getSqlMerge().getHavingColsName().length);\n\n\t\tsql = \"select avg(offer_id) avgofferid, member_id from offer_detail group by member_id having avg(offer_id) > 100\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(3, rrs.getSqlMerge().getHavingColsName().length);\n\n        sql = \"select count(offer_id) countofferid, member_id from offer_detail group by member_id having countofferid > 100\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(3, rrs.getSqlMerge().getHavingColsName().length);\n\n        sql = \"select count(offer_id) countofferid, member_id from offer_detail group by member_id having count(offer_id) > 100\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(3, rrs.getSqlMerge().getHavingColsName().length);\n\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/DruidMysqlRouteStrategyTest.java",
    "content": "package io.mycat.route;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.NoSuchElementException;\nimport java.util.Set;\n\nimport org.junit.Test;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.server.parser.ServerParse;\nimport junit.framework.Assert;\nimport junit.framework.TestCase;\n\npublic class DruidMysqlRouteStrategyTest extends TestCase {\n    protected Map<String, SchemaConfig> schemaMap;\n    protected LayerCachePool cachePool = new SimpleCachePool();\n    protected RouteStrategy routeStrategy;\n\n    public DruidMysqlRouteStrategyTest() {\n        String schemaFile = \"/route/schema.xml\";\n        String ruleFile = \"/route/rule.xml\";\n        SchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n        schemaMap = schemaLoader.getSchemas();\n        MycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n    }\n\n    protected void setUp() throws Exception {\n        // super.setUp();\n        // schemaMap = CobarServer.getInstance().getConfig().getSchemas();\n\n    }\n\n//\tpublic void testAlias() throws Exception {\n//\t\tString sql = \"SELECT  UM.UserId , UM.MenuId ,SM.ParentId ,SM.FullName , SM.Description , SM.Img , SM.NavigateUrl ,SM.FormName ,SM.Target ,SM.IsUnfold FROM    Lever_SysMenu SM INNER JOIN ( SELECT UR.UserId AS UserId ,  RM.MenuId AS MenuId FROM   Lever_RoleMenu RM  INNER JOIN Lever_UserRole UR ON RM.RoleId = UR.RoleId  UNION SELECT UserId ,   MenuId FROM   Lever_UserMenu   UNION SELECT U.UserId ,      RM.MenuId   FROM   Lever_User U     LEFT JOIN Lever_RoleMenu RM ON U.RoleId = RM.RoleId    WHERE  U.UserId = '8d28533f-1762-4e79-b71f-64eb1a50cb8b' ) UM ON SM.MenuId = UM.MenuId   WHERE   UM.UserId = '8d28533f-1762-4e79-b71f-64eb1a50cb8b'  AND SM.Enabled = 1  ORDER BY SM.SortCode\";\n//\t\tSchemaConfig schema = schemaMap.get(\"wdw\");\n//\t\tRouteResultset rrs = routeStrategy.route(new SystemConfig(),schema, -1, sql, null,\n//\t\t\t\tnull, cachePool);\n//\t}\n\n\n    public void testRouteInsertShort() throws Exception {\n        String sql = \"inSErt into offer_detail (`offer_id`, gmt) values ('123',now())\";\n        SchemaConfig schema = schemaMap.get(\"cndb\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(-1l, rrs.getLimitSize());\n        Assert.assertEquals(\"detail_dn15\", rrs.getNodes()[0].getName());\n        String expect = \"INSERT INTO offer_detail (`offer_id`, gmt)\\n\" +\n                \"VALUES ('123', now())\";\n        Assert.assertEquals(expect, rrs.getNodes()[0].getStatement());\n        sql = \"inSErt into offer_detail ( gmt) values (now())\";\n        schema = schemaMap.get(\"cndb\");\n        try {\n            rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        } catch (Exception e) {\n            String msg = \"bad insert sql (sharding column:\";\n            Assert.assertTrue(e.getMessage().contains(msg));\n        }\n        sql = \"inSErt into offer_detail (offer_id, gmt) values (123,now())\";\n        schema = schemaMap.get(\"cndb\");\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(-1l, rrs.getLimitSize());\n        Assert.assertEquals(\"detail_dn15\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(\n                \"INSERT INTO offer_detail (offer_id, gmt)\\nVALUES ('123', now())\",\n                rrs.getNodes()[0].getStatement());\n\n        sql = \"insert into offer(group_id,offer_id,member_id)values(234,123,'abc')\";\n        schema = schemaMap.get(\"cndb\");\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(-1l, rrs.getLimitSize());\n        Assert.assertEquals(\"offer_dn12\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(\n                \"INSERT INTO offer (group_id, offer_id, member_id)\\n\" +\n                        \"VALUES (234, 123, 'abc')\",\n                rrs.getNodes()[0].getStatement());\n\n\n\n\n        sql = \"\\n\" +\n                \"  INSERT INTO \\n\" +\n                \"`offer` \\n\" +\n                \"(`asf`,member_id) \\n\" +\n                \"VALUES \\n\" +\n                \"(' the articles sfroms user selection ','abc')\";\n        schema = schemaMap.get(\"cndb\");\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n\n\n    }\n\n    public void testGlobalTableroute() throws Exception {\n        String sql = null;\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        RouteResultset rrs = null;\n        // select of global table route to only one datanode defined\n        sql = \"select * from company where company.name like 'aaa'\";\n        schema = schemaMap.get(\"TESTDB\");\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        // query of global table only route to one datanode\n        sql = \"insert into company (id,name,level) values(111,'company1',3)\";\n        schema = schemaMap.get(\"TESTDB\");\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        Assert.assertEquals(3, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n\n        // update of global table route to every datanode defined\n        sql = \"update company set name=name+aaa\";\n        schema = schemaMap.get(\"TESTDB\");\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        Assert.assertEquals(3, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        \n     // delete of global table route to every datanode defined\n        sql = \"delete from company where id = 1\";\n        schema = schemaMap.get(\"TESTDB\");\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        Assert.assertEquals(3, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n\n        // company is global table ,will route to differnt tables\n        schema = schemaMap.get(\"TESTDB\");\n        sql = \"select * from  company A where a.sharding_id=10001 union select * from  company B where B.sharding_id =10010\";\n        Set<String> nodeSet = new HashSet<String>();\n        for (int i = 0; i < 10; i++) {\n            rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n            Assert.assertEquals(false, rrs.isCacheAble());\n            Assert.assertEquals(1, rrs.getNodes().length);\n            nodeSet.add(rrs.getNodes()[0].getName());\n\n        }\n        Assert.assertEquals(true, nodeSet.size() > 1);\n\n    }\n\n    public void testMoreGlobalTableroute() throws Exception {\n        String sql = null;\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        RouteResultset rrs = null;\n        // select of global table route to only one datanode defined\n        sql = \"select * from company,area where area.company_id=company.id \";\n        schema = schemaMap.get(\"TESTDB\");\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());   // 全局表涉及到多个节点时,不缓存路由结果\n\n    }\n\n    public void testRouteMultiTables() throws Exception {\n        // company is global table ,route to 3 datanode and ignored in route\n        String sql = \"select * from company,customer ,orders where customer.company_id=company.id and orders.customer_id=customer.id and company.name like 'aaa' limit 10\";\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(\"dn1\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(\"dn2\", rrs.getNodes()[1].getName());\n\n    }\n\n    public void testRouteCache() throws Exception {\n        // select cache ID\n        this.cachePool.putIfAbsent(\"TESTDB_EMPLOYEE\", \"88\", \"dn2\");\n\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        String sql = \"select * from employee where id=88\";\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());//已经缓存了,不必再缓存了\n        Assert.assertEquals(null, rrs.getPrimaryKey());\n        Assert.assertEquals(-1, rrs.getLimitSize());\n        Assert.assertEquals(\"dn2\", rrs.getNodes()[0].getName());\n\n        // select cache ID not found ,return all node and rrst not cached\n        sql = \"select * from employee where id=89\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(\"TESTDB_EMPLOYEE.ID\", rrs.getPrimaryKey());\n        Assert.assertEquals(-1, rrs.getLimitSize());\n\n        // update cache ID found\n        sql = \"update employee  set name='aaa' where id=88\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(null, rrs.getPrimaryKey());\n        Assert.assertEquals(\"dn2\", rrs.getNodes()[0].getName());\n\n        // delete cache ID should be not founded\n        sql = \"delete from  employee  where id=88\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n\t\tAssert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n\t\tAssert.assertEquals(\"dn2\", rrs.getNodes()[0].getName());\n\t\t// Assert.assertEquals(\"dn2\", rrs.getNodes()[1].getName());\n    }\n\n    private static Map<String, RouteResultsetNode> getNodeMap(\n            RouteResultset rrs, int expectSize) {\n        RouteResultsetNode[] routeNodes = rrs.getNodes();\n        Assert.assertEquals(expectSize, routeNodes.length);\n        Map<String, RouteResultsetNode> nodeMap = new HashMap<String, RouteResultsetNode>(\n                expectSize, 1);\n        for (int i = 0; i < expectSize; i++) {\n            RouteResultsetNode routeNode = routeNodes[i];\n            nodeMap.put(routeNode.getName(), routeNode);\n        }\n        Assert.assertEquals(expectSize, nodeMap.size());\n        return nodeMap;\n    }\n\n    private static interface NodeNameDeconstructor {\n        public int getNodeIndex(String name);\n    }\n\n    private static class NodeNameAsserter implements NodeNameDeconstructor {\n        private String[] expectNames;\n\n        public NodeNameAsserter() {\n        }\n\n        public NodeNameAsserter(String... expectNames) {\n            Assert.assertNotNull(expectNames);\n            this.expectNames = expectNames;\n        }\n\n        protected void setNames(String[] expectNames) {\n            Assert.assertNotNull(expectNames);\n            this.expectNames = expectNames;\n        }\n\n        public void assertRouteNodeNames(Collection<String> nodeNames) {\n            Assert.assertNotNull(nodeNames);\n            Assert.assertEquals(expectNames.length, nodeNames.size());\n            for (String name : expectNames) {\n                Assert.assertTrue(nodeNames.contains(name));\n            }\n        }\n\n        @Override\n        public int getNodeIndex(String name) {\n            for (int i = 0; i < expectNames.length; ++i) {\n                if (name.equals(expectNames[i])) {\n                    return i;\n                }\n            }\n            throw new NoSuchElementException(\"route node \" + name\n                    + \" dosn't exist!\");\n        }\n    }\n\n    private static class IndexedNodeNameAsserter extends NodeNameAsserter {\n        /**\n         * @param from included\n         * @param to   excluded\n         */\n        public IndexedNodeNameAsserter(String prefix, int from, int to) {\n            super();\n            String[] names = new String[to - from];\n            for (int i = 0; i < names.length; ++i) {\n                names[i] = prefix + (i + from) ;\n            }\n            setNames(names);\n        }\n    }\n\n    private static class RouteNodeAsserter {\n        private NodeNameDeconstructor deconstructor;\n        private SQLAsserter sqlAsserter;\n\n        public RouteNodeAsserter(NodeNameDeconstructor deconstructor,\n                                 SQLAsserter sqlAsserter) {\n            this.deconstructor = deconstructor;\n            this.sqlAsserter = sqlAsserter;\n        }\n\n        public void assertNode(RouteResultsetNode node) throws Exception {\n            int nodeIndex = deconstructor.getNodeIndex(node.getName());\n            sqlAsserter.assertSQL(node.getStatement(), nodeIndex);\n        }\n    }\n\n    private static interface SQLAsserter {\n        public void assertSQL(String sql, int nodeIndex) throws Exception;\n    }\n\n    private static class SimpleSQLAsserter implements SQLAsserter {\n        private Map<Integer, Set<String>> map = new HashMap<Integer, Set<String>>();\n\n        public SimpleSQLAsserter addExpectSQL(int nodeIndex, String sql) {\n            Set<String> set = map.get(nodeIndex);\n            if (set == null) {\n                set = new HashSet<String>();\n                map.put(nodeIndex, set);\n            }\n            set.add(sql);\n            return this;\n        }\n\n        @Override\n        public void assertSQL(String sql, int nodeIndex) throws Exception {\n            Assert.assertNotNull(map.get(nodeIndex));\n            Assert.assertTrue(map.get(nodeIndex).contains(sql));\n        }\n    }\n\n    public void testroute() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"cndb\");\n\n        String sql = \"select * from independent where member='abc'\";\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null,\n                cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        Map<String, RouteResultsetNode> nodeMap = getNodeMap(rrs, 128);\n        IndexedNodeNameAsserter nameAsserter = new IndexedNodeNameAsserter(\n                \"independent_dn\", 0, 128);\n        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n        SimpleSQLAsserter sqlAsserter = new SimpleSQLAsserter();\n        for (int i = 0; i < 128; ++i) {\n            sqlAsserter.addExpectSQL(i,\n                    \"select * from independent where member='abc'\");\n        }\n        RouteNodeAsserter asserter = new RouteNodeAsserter(nameAsserter,\n                sqlAsserter);\n        for (RouteResultsetNode node : nodeMap.values()) {\n            asserter.assertNode(node);\n        }\n\n        // include database schema ,should remove\n        sql = \"select * from cndb.independent A  where a.member='abc'\";\n        schema = schemaMap.get(\"cndb\");\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        nodeMap = getNodeMap(rrs, 128);\n        nameAsserter = new IndexedNodeNameAsserter(\"independent_dn\", 0, 128);\n        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n        sqlAsserter = new SimpleSQLAsserter();\n        for (int i = 0; i < 128; ++i) {\n            sqlAsserter.addExpectSQL(i,\n                    \"select * from independent A  where a.member='abc'\");\n        }\n        asserter = new RouteNodeAsserter(nameAsserter, sqlAsserter);\n        for (RouteResultsetNode node : nodeMap.values()) {\n            asserter.assertNode(node);\n        }\n\n    }\n\n    public void testERroute() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        String sql = \"insert into orders (id,name,customer_id) values(1,'testonly',1)\";\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null,\n                cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(\"dn1\", rrs.getNodes()[0].getName());\n\n        sql = \"insert into orders (id,name,customer_id) values(1,'testonly',2000001)\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(\"dn2\", rrs.getNodes()[0].getName());\n\n        // can't update join key\n        sql = \"update orders set id=1 ,name='aaa' , customer_id=2000001\";\n        String err = null;\n        try {\n            rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        } catch (SQLNonTransientException e) {\n            err = e.getMessage();\n        }\n        Assert.assertEquals(\n                true,\n                err.startsWith(\"Parent relevant column can't be updated ORDERS->CUSTOMER_ID\"));\n\n        // route by parent rule ,update sql\n        sql = \"update orders set id=1 ,name='aaa' where customer_id=2000001\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        Assert.assertEquals(\"dn2\", rrs.getNodes()[0].getName());\n\n        // route by parent rule but can't find datanode\n        sql = \"update orders set id=1 ,name='aaa' where customer_id=-1\";\n        try {\n            rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        } catch (Exception e) {\n            err = e.getMessage();\n        }\n        Assert.assertEquals(true,\n                err.startsWith(\"can't find datanode for sharding column:\"));\n\n        // route by parent rule ,select sql\n        sql = \"select * from orders  where customer_id=2000001\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        Assert.assertEquals(\"dn2\", rrs.getNodes()[0].getName());\n\n        // route by parent rule ,delete sql\n        sql = \"delete from orders  where customer_id=2000001\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(\"dn2\", rrs.getNodes()[0].getName());\n\n        //test alias in column\n        sql = \"select name as order_name from  orders order by order_name limit 10,5\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        MySqlStatementParser parser = new MySqlStatementParser(\"SELECT name AS order_name FROM orders ORDER BY order_name LIMIT 0,15\");\n        SQLStatement statement = parser.parseStatement();\n\n//\t\tAssert.assertEquals(sql, rrs.getNodes()[0].getStatement());\n    }\n\n    public void testDuplicatePartitionKey() throws Exception {\n        String sql = null;\n        SchemaConfig schema = schemaMap.get(\"cndb\");\n        RouteResultset rrs = null;\n\n        sql = \"select * from cndb.offer where (offer_id, group_id ) In (123,234)\";\n        schema = schemaMap.get(\"cndb\");\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        Assert.assertEquals(-1l, rrs.getLimitSize());\n        Assert.assertEquals(128, rrs.getNodes().length);\n        for (int i = 0; i < 128; i++) {\n//            Assert.assertEquals(\"offer_dn\" + i ,\n//                    rrs.getNodes()[i].getName());//node的排序有变化，所以此处不强求\n            Assert.assertEquals(\n                    \"select * from offer where (offer_id, group_id ) In (123,234)\",\n                    rrs.getNodes()[i].getStatement());\n        }\n\n        sql = \"SELECT * FROM offer WHERE FALSE OR offer_id = 123 AND member_id = 123 OR member_id = 123 AND member_id = 234 OR member_id = 123 AND member_id = 345 OR member_id = 123 AND member_id = 456 OR offer_id = 234 AND group_id = 123 OR offer_id = 234 AND group_id = 234 OR offer_id = 234 AND group_id = 345 OR offer_id = 234 AND group_id = 456 OR offer_id = 345 AND group_id = 123 OR offer_id = 345 AND group_id = 234 OR offer_id = 345 AND group_id = 345 OR offer_id = 345 AND group_id = 456 OR offer_id = 456 AND group_id = 123 OR offer_id = 456 AND group_id = 234 OR offer_id = 456 AND group_id = 345 OR offer_id = 456 AND group_id = 456\";\n        schema = schemaMap.get(\"cndb\");\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        getNodeMap(rrs, 128);\n\n        sql = \"select * from  offer where false\"\n                + \" or offer_id=123 and group_id=123\"\n                + \" or group_id=123 and offer_id=234\"\n                + \" or offer_id=123 and group_id=345\"\n                + \" or offer_id=123 and group_id=456  \";\n        schema = schemaMap.get(\"cndb\");\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        Assert.assertEquals(-1l, rrs.getLimitSize());\n\n    }\n\n    public void testAddLimitToSQL() throws Exception {\n        final SchemaConfig schema = schemaMap.get(\"TESTDB\");\n\n        String sql = null;\n        RouteResultset rrs = null;\n\n        sql = \"select * from orders\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        Map<String, RouteResultsetNode> nodeMap = getNodeMap(rrs, 2);\n        NodeNameAsserter nameAsserter = new NodeNameAsserter(\"dn2\",\n                \"dn1\");\n        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n        Assert.assertEquals(schema.getDefaultMaxLimit(), rrs.getLimitSize());\n//\t\tAssert.assertEquals(\"SELECT * FROM orders LIMIT 100\", rrs.getNodes()[0].getStatement());\n        MySqlStatementParser parser = new MySqlStatementParser(\"SELECT * FROM orders LIMIT 100\");\n        SQLStatement statement = parser.parseStatement();\n        Assert.assertEquals(statement.toString(), rrs.getNodes()[0].getStatement());\n\n\n        sql = \"select * from goods\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(schema.getDefaultMaxLimit(), rrs.getLimitSize());\n//\t\tAssert.assertEquals(\"select * from goods\", rrs.getNodes()[0].getStatement());\n        parser = new MySqlStatementParser(\"SELECT * FROM goods LIMIT 100\");\n        statement = parser.parseStatement();\n        Assert.assertEquals(statement.toString(), rrs.getNodes()[0].getStatement());\n\n\n        sql = \"select * from goods limit 2 ,3\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(1, rrs.getNodes().length);\n//\t\tAssert.assertEquals(-1, rrs.getLimitSize());\n        Assert.assertEquals(\"select * from goods limit 2 ,3\", rrs.getNodes()[0].getStatement());\n\n\n        sql = \"select * from notpartionTable limit 2 ,3\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(3, rrs.getLimitSize());\n        Assert.assertEquals(\"select * from notpartionTable limit 2 ,3\", rrs.getNodes()[0].getStatement());\n\n    }\n\n\n    public void testModifySQLLimit() throws Exception {\n        final SchemaConfig schema = schemaMap.get(\"TESTDB\");\n\n        String sql = null;\n        RouteResultset rrs = null;\n        //SQL span multi datanode \n        sql = \"select * from orders limit 2,3\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        Map<String, RouteResultsetNode> nodeMap = getNodeMap(rrs, 2);\n        NodeNameAsserter nameAsserter = new NodeNameAsserter(\"dn2\",\n                \"dn1\");\n        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n        Assert.assertEquals(3, rrs.getLimitSize());\n\n\n        MySqlStatementParser parser = new MySqlStatementParser(\"SELECT * FROM orders LIMIT 0,5\");\n        SQLStatement statement = parser.parseStatement();\n\n        Assert.assertEquals(statement.toString(), rrs.getNodes()[0].getStatement());\n\n        //SQL  not span multi datanode\n        sql = \"select * from customer where id=10000 limit 2,3\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        nodeMap = getNodeMap(rrs, 1);\n        nameAsserter = new NodeNameAsserter(\"dn1\");\n        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n        Assert.assertEquals(3, rrs.getLimitSize());\n        Assert.assertEquals(\"select * from customer where id=10000 limit 2,3\", rrs.getNodes()[0].getStatement());\n\n\n    }\n\n    public void testGroupLimit() throws Exception {\n        final SchemaConfig schema = schemaMap.get(\"cndb\");\n\n        String sql = null;\n        RouteResultset rrs = null;\n\n        sql = \"select count(*) from (select * from(select * from offer_detail where offer_id='123' or offer_id='234' limit 88)offer  where offer.member_id='abc' limit 60) w \"\n                + \" where w.member_id ='pavarotti17' limit 99\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        // Assert.assertEquals(88L, rrs.getLimitSize());\n        // Assert.assertEquals(RouteResultset.SUM_FLAG, rrs.getFlag());\n        Map<String, RouteResultsetNode> nodeMap = getNodeMap(rrs, 2);\n        NodeNameAsserter nameAsserter = new NodeNameAsserter(\"detail_dn29\",\n                \"detail_dn15\");\n        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n\n        sql = \"select count(*) from (select * from(select max(id) from offer_detail where offer_id='123' or offer_id='234' limit 88)offer  where offer.member_id='abc' limit 60) w \"\n                + \" where w.member_id ='pavarotti17' limit 99\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        nodeMap = getNodeMap(rrs, 2);\n        nameAsserter = new NodeNameAsserter(\"detail_dn29\", \"detail_dn15\");\n        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n\n        sql = \"select * from (select * from(select max(id) from offer_detail where offer_id='123' or offer_id='234' limit 88)offer  where offer.member_id='abc' limit 60) w \"\n                + \" where w.member_id ='pavarotti17' limit 99\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        nodeMap = getNodeMap(rrs, 2);\n        nameAsserter = new NodeNameAsserter(\"detail_dn29\", \"detail_dn15\");\n        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n\n        sql = \"select * from (select count(*) from(select * from offer_detail where offer_id='123' or offer_id='234' limit 88)offer  where offer.member_id='abc' limit 60) w \"\n                + \" where w.member_id ='pavarotti17' limit 99\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(true, rrs.isCacheAble());\n        // Assert.assertEquals(88L, rrs.getLimitSize());\n        // Assert.assertEquals(RouteResultset.SUM_FLAG, rrs.getFlag());\n        nodeMap = getNodeMap(rrs, 2);\n        nameAsserter = new NodeNameAsserter(\"detail_dn29\", \"detail_dn15\");\n        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n\n    }\n\n//    public void testTableMetaRead() throws Exception {\n//        final SchemaConfig schema = schemaMap.get(\"cndb\");\n//\n//        String sql = \" desc offer\";\n//        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.DESCRIBE, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        // random return one node\n//        // Assert.assertEquals(\"offer_dn[0]\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"desc offer\", rrs.getNodes()[0].getStatement());\n//\n//        sql = \"desc cndb.offer\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.DESCRIBE, sql, null, null, cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        // random return one node\n//        // Assert.assertEquals(\"offer_dn[0]\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"desc offer\", rrs.getNodes()[0].getStatement());\n//\n//        sql = \"desc cndb.offer col1\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.DESCRIBE, sql, null, null, cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        // random return one node\n//        // Assert.assertEquals(\"offer_dn[0]\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"desc offer col1\", rrs.getNodes()[0].getStatement());\n//\n//        sql = \"SHOW FULL COLUMNS FROM  offer  IN db_name WHERE true\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SHOW, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        // random return one node\n//        // Assert.assertEquals(\"offer_dn[0]\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"SHOW FULL COLUMNS FROM offer WHERE true\",\n//                rrs.getNodes()[0].getStatement());\n//\n//        sql = \"SHOW FULL COLUMNS FROM  db.offer  IN db_name WHERE true\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SHOW, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        // random return one node\n//        // Assert.assertEquals(\"offer_dn[0]\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"SHOW FULL COLUMNS FROM offer WHERE true\",\n//                rrs.getNodes()[0].getStatement());\n//\n//\n//        sql = \"SHOW FULL TABLES FROM `TESTDB` WHERE Table_type != 'VIEW'\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SHOW, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(\"SHOW FULL TABLES WHERE Table_type != 'VIEW'\", rrs.getNodes()[0].getStatement());\n//\n//        sql = \"SHOW INDEX  IN offer FROM  db_name\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SHOW, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        // random return one node\n//        // Assert.assertEquals(\"offer_dn[0]\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"SHOW INDEX  FROM offer\",\n//                rrs.getNodes()[0].getStatement());\n//\n//        sql = \"SHOW TABLES from db_name like 'solo'\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SHOW, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Map<String, RouteResultsetNode> nodeMap = getNodeMap(rrs, 3);\n//        NodeNameAsserter nameAsserter = new NodeNameAsserter(\"detail_dn0\",\n//                \"offer_dn0\", \"independent_dn0\");\n//        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n//        SimpleSQLAsserter sqlAsserter = new SimpleSQLAsserter();\n//        sqlAsserter.addExpectSQL(0, \"SHOW TABLES like 'solo'\")\n//                .addExpectSQL(1, \"SHOW TABLES like 'solo'\")\n//                .addExpectSQL(2, \"SHOW TABLES like 'solo'\")\n//                .addExpectSQL(3, \"SHOW TABLES like 'solo'\");\n//        RouteNodeAsserter asserter = new RouteNodeAsserter(nameAsserter,\n//                sqlAsserter);\n//        for (RouteResultsetNode node : nodeMap.values()) {\n//            asserter.assertNode(node);\n//        }\n//\n//        sql = \"SHOW TABLES in db_name \";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SHOW, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        nodeMap = getNodeMap(rrs, 3);\n//        nameAsserter = new NodeNameAsserter(\"detail_dn0\", \"offer_dn0\",\n//                \"independent_dn0\");\n//        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n//        sqlAsserter = new SimpleSQLAsserter();\n//        sqlAsserter.addExpectSQL(0, \"SHOW TABLES\")\n//                .addExpectSQL(1, \"SHOW TABLES\").addExpectSQL(2, \"SHOW TABLES\")\n//                .addExpectSQL(3, \"SHOW TABLES\");\n//        asserter = new RouteNodeAsserter(nameAsserter, sqlAsserter);\n//        for (RouteResultsetNode node : nodeMap.values()) {\n//            asserter.assertNode(node);\n//        }\n//\n//        sql = \"SHOW TABLeS \";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SHOW, sql, null, null,\n//                cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        nodeMap = getNodeMap(rrs, 3);\n//        nameAsserter = new NodeNameAsserter(\"offer_dn0\", \"detail_dn0\",\n//                \"independent_dn0\");\n//        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n//        sqlAsserter = new SimpleSQLAsserter();\n//        sqlAsserter.addExpectSQL(0, \"SHOW TABLeS \")\n//                .addExpectSQL(1, \"SHOW TABLeS \").addExpectSQL(2, \"SHOW TABLeS \");\n//        asserter = new RouteNodeAsserter(nameAsserter, sqlAsserter);\n//        for (RouteResultsetNode node : nodeMap.values()) {\n//            asserter.assertNode(node);\n//        }\n//    }\n\n    public void testConfigSchema() throws Exception {\n        try {\n            SchemaConfig schema = schemaMap.get(\"config\");\n            String sql = \"select * from offer where offer_id=1\";\n            routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n            Assert.assertFalse(true);\n        } catch (Exception e) {\n            Assert.assertEquals(\"route rule for table OFFER is required: select * from offer where offer_id=1\", e.getMessage());\n        }\n        try {\n            SchemaConfig schema = schemaMap.get(\"config\");\n            String sql = \"select * from offer where col11111=1\";\n            routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n            Assert.assertFalse(true);\n        } catch (Exception e) {\n        }\n        try {\n            SchemaConfig schema = schemaMap.get(\"config\");\n            String sql = \"select * from offer \";\n            routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n            Assert.assertFalse(true);\n        } catch (Exception e) {\n        }\n    }\n\n    public void testIgnoreSchema() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"ignoreSchemaTest\");\n        String sql = \"select * from offer where offer_id=1\";\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null,\n                cachePool);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(\"cndb_dn\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(sql, rrs.getNodes()[0].getStatement());\n        sql = \"select * from ignoreSchemaTest.offer1 where ignoreSchemaTest.offer1.offer_id=1\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(\"select * from offer1 where offer1.offer_id=1\",\n                rrs.getNodes()[0].getStatement());\n        sql = \"select * from ignoreSchemaTest2.offer where ignoreSchemaTest2.offer.offer_id=1\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(sql, rrs.getNodes()[0].getStatement(), sql);\n        sql = \"select * from ignoreSchemaTest2.offer a,offer b  where ignoreSchemaTest2.offer.offer_id=1\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(\n                \"select * from ignoreSchemaTest2.offer a,offer b  where ignoreSchemaTest2.offer.offer_id=1\",\n                rrs.getNodes()[0].getStatement());\n\n    }\n\n//    public void testNonPartitionSQL() throws Exception {\n//\n//        SchemaConfig schema = schemaMap.get(\"cndb\");\n//        String sql = null;\n//        RouteResultset rrs = null;\n//\n//        schema = schemaMap.get(\"dubbo2\");\n//        sql = \"SHOW TABLES from db_name like 'solo'\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, 9, sql, null, null, cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        Assert.assertEquals(\"dn1\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"SHOW TABLES like 'solo'\",\n//                rrs.getNodes()[0].getStatement());\n//\n//        schema = schemaMap.get(\"dubbo\");\n//        sql = \"SHOW TABLES from db_name like 'solo'\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, 9, sql, null, null, cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        Assert.assertEquals(\"dubbo_dn\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"SHOW TABLES like 'solo'\",\n//                rrs.getNodes()[0].getStatement());\n//\n//\n//\n//        sql = \"desc cndb.offer\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Assert.assertEquals(-1L, rrs.getLimitSize());\n//        Assert.assertEquals(1, rrs.getNodes().length);\n//        Assert.assertEquals(\"dubbo_dn\", rrs.getNodes()[0].getName());\n//        Assert.assertEquals(\"desc cndb.offer\", rrs.getNodes()[0].getStatement());\n//\n//        schema = schemaMap.get(\"cndb\");\n//        sql = \"SHOW fulL TaBLES from db_name like 'solo'\";\n//        rrs = routeStrategy.route(new SystemConfig(), schema, 9, sql, null, null, cachePool);\n//        Assert.assertEquals(false, rrs.isCacheAble());\n//        Map<String, RouteResultsetNode> nodeMap = getNodeMap(rrs, 3);\n//        NodeNameAsserter nameAsserter = new NodeNameAsserter(\"detail_dn0\",\n//                \"offer_dn0\", \"independent_dn0\");\n//        nameAsserter.assertRouteNodeNames(nodeMap.keySet());\n//        SimpleSQLAsserter sqlAsserter = new SimpleSQLAsserter();\n//        sqlAsserter.addExpectSQL(0, \"SHOW FULL TABLES like 'solo'\")\n//                .addExpectSQL(1, \"SHOW FULL TABLES like 'solo'\")\n//                .addExpectSQL(2, \"SHOW FULL TABLES like 'solo'\")\n//                .addExpectSQL(3, \"SHOW FULL TABLES like 'solo'\");\n//        RouteNodeAsserter asserter = new RouteNodeAsserter(nameAsserter,\n//                sqlAsserter);\n//        for (RouteResultsetNode node : nodeMap.values()) {\n//            asserter.assertNode(node);\n//        }\n//    }\n\n    public void testGlobalTableSingleNodeLimit() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        String sql = \"select * from globalsn\";\n        RouteResultset rrs = null;\n        rrs = routeStrategy.route(new SystemConfig(), schema,\n                ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertEquals(100L, rrs.getLimitSize());\n    }\n\n    /**\n     * select 1\n     * select 1 union all select 2\n     *\n     * @throws Exception\n     */\n    public void testSelectNoTable() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        String sql = \"select 1\";\n        RouteResultset rrs = null;\n        rrs = routeStrategy.route(new SystemConfig(), schema,\n                ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n\n\n        sql = \"select 1 union select 2\";\n        rrs = routeStrategy.route(new SystemConfig(), schema,\n                ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n    }\n\n    /**\n     * 支持insert into ... values (),()...\n     * 不支持insert into ... select...\n     *\n     * @throws Exception\n     */\n    public void testBatchInsert() throws Exception {\n\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        RouteResultset rrs = null;\n        //不支持childtable 批量插入\n        String sql = \"insert into orders (id,name,customer_id) values(1,'testonly',1),(2,'testonly',2000001)\";\n        try {\n            rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null,\n                    cachePool);\n        } catch (Exception e) {\n            Assert.assertEquals(\"ChildTable multi insert not provided\", e.getMessage());\n        }\n\n        sql = \"insert into employee (id,name,customer_id) select id,name,customer_id from customer\";\n        try {\n            rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null,\n                    cachePool);\n        } catch (Exception e) {\n            Assert.assertEquals(\"TODO:insert into .... select .... not supported!\", e.getMessage());\n        }\n\n        //分片表批量插入正常 employee\n        sql = \"insert into employee (id,name,sharding_id) values(1,'testonly',10000),(2,'testonly','10010')\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null,\n                cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(\"dn1\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(\"dn2\", rrs.getNodes()[1].getName());\n        String node1Sql = formatSql(\"insert into employee (id,name,sharding_id) values(1,'testonly','10000')\");\n        String node2Sql = formatSql(\"insert into employee (id,name,sharding_id) values(2,'testonly','10010')\");\n        RouteResultsetNode[] nodes = rrs.getNodes();\n        Assert.assertEquals(node1Sql, nodes[0].getStatement());\n        Assert.assertEquals(node2Sql, nodes[1].getStatement());\n    }\n\n    /**\n     * insert ... on duplicate key ... update...\n     *\n     * @throws Exception\n     */\n    public void testInsertOnDuplicateKey() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        String sql = \"insert into employee (id,name,sharding_id) values(1,'testonly',10000) on duplicate key update name='nihao'\";\n        RouteResultset rrs = null;\n        rrs = routeStrategy.route(new SystemConfig(), schema,\n                ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(\"dn1\", rrs.getNodes()[0].getName());\n\n        //insert ... on duplicate key ... update col1 = VALUES(col1),col2 = VALUES(col2)\n        sql = \"insert into employee (id,name,sharding_id) values(1,'testonly',10000) \" +\n                \"on duplicate key update name=VALUES(name),id = VALUES(id)\";\n        rrs = routeStrategy.route(new SystemConfig(), schema,\n                ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(\"dn1\", rrs.getNodes()[0].getName());\n\n        //insert ... on duplicate key ,sharding key can't be updated\n        sql = \"insert into employee (id,name,sharding_id) values(1,'testonly',10000) \" +\n                \"on duplicate key update name=VALUES(name),id = VALUES(id),sharding_id = VALUES(sharding_id)\";\n\n        try {\n            rrs = routeStrategy.route(new SystemConfig(), schema,\n                    ServerParse.SELECT, sql, null, null, cachePool);\n        } catch (Exception e) {\n            Assert.assertEquals(\"Sharding column can't be updated: EMPLOYEE -> SHARDING_ID\", e.getMessage());\n        }\n\n\n    }\n\n    /**\n     * 测试函数COUNT\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testAggregateExpr() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        String sql = \"select id, name, count(name) from employee group by name;\";\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n\t\tAssert.assertTrue(rrs.getMergeCols().containsKey(\"count2\"));\n\n        sql = \"select id, name, count(name) as c from employee group by name;\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertTrue(rrs.getMergeCols().containsKey(\"c\"));\n\n        sql = \"select id, name, count(name) c from employee group by name;\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertTrue(rrs.getMergeCols().containsKey(\"c\"));\n    }\n    \n    /**\n     * 测试between语句的路由\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testBetweenExpr() throws Exception {\n//    \t0-200M=0\n//    \t200M1-400M=1\n//    \t400M1-600M=2\n//    \t600M1-800M=3\n//    \t800M1-1000M=4\n    \t\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        String sql = \"select * from customer where id between 1 and 5;\";\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertTrue(rrs.getNodes().length == 1);\n        Assert.assertTrue(rrs.getNodes()[0].getName().equals(\"dn1\"));\n\n        sql = \"select * from customer where id between 1 and 2000001;\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertTrue(rrs.getNodes().length == 2);\n        \n        sql = \"select * from customer where id between 2000001 and 3000001;\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertTrue(rrs.getNodes().length == 1);\n        Assert.assertTrue(rrs.getNodes()[0].getName().equals(\"dn2\"));\n        \n        sql = \"delete from customer where id between 2000001 and 3000001;\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertTrue(rrs.getNodes().length == 1);\n        Assert.assertTrue(rrs.getNodes()[0].getName().equals(\"dn2\"));\n        \n        sql = \"update customer set name='newName' where id between 2000001 and 3000001;\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertTrue(rrs.getNodes().length == 1);\n        Assert.assertTrue(rrs.getNodes()[0].getName().equals(\"dn2\"));\n        \n    }\n    \n    /**\n     * 测试or语句的路由\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testOr() throws Exception {\n//    \t0-200M=0\n//    \t200M1-400M=1\n//    \t400M1-600M=2\n//    \t600M1-800M=3\n//    \t800M1-1000M=4\n    \t\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        String sql = \"select * from customer where sharding_id=10000 or 1=1;\";\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertTrue(rrs.getNodes().length == 2);\n        Assert.assertTrue(rrs.getNodes()[0].getName().equals(\"dn1\"));\n        Assert.assertTrue(rrs.getNodes()[1].getName().equals(\"dn2\"));\n\n        sql = \"select * from customer where sharding_id = 10000 or sharding_id = 10010\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertTrue(rrs.getNodes()[0].getName().equals(\"dn1\"));\n        Assert.assertTrue(rrs.getNodes()[1].getName().equals(\"dn2\"));\n        \n        sql = \"select * from customer where sharding_id = 10000 or user_id = 'wangwu'\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertTrue(rrs.getNodes()[0].getName().equals(\"dn1\"));\n        Assert.assertTrue(rrs.getNodes()[1].getName().equals(\"dn2\"));\n\n        // #2694 验证(name=1 or name=2) and id=11 的路由等价于id=11 and (name=1 or name=2) 路由\n        sql = \"select * from customer where sharding_id = 10000 and (user_id = 'zhangsan' or user_id='lisi')\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertTrue(rrs.getNodes()[0].getName().equals(\"dn1\"));\n\n        sql = \"select * from customer where (user_id = 'zhangsan' or user_id='lisi') and sharding_id = 10000\";\n        RouteResultset rrs2 = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null,\n                cachePool);\n        Assert.assertTrue(rrs2.getNodes()[0].getName().equals(rrs.getNodes()[0].getName()));\n\n    }\n    \n    /**\n     * 测试父子表，查询子表的语句路由到多个节点\n     * @throws Exception\n     */\n    @Test\n    public void testERRouteMutiNode() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        String sql = \"select * from orders where customer_id in(1,2000001);\";\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n        Assert.assertTrue(rrs.getNodes().length == 2);\n        Assert.assertTrue(rrs.getNodes()[0].getName().equals(\"dn1\"));\n        Assert.assertTrue(rrs.getNodes()[1].getName().equals(\"dn2\"));\n    }\n    \n    /**\n     * 测试多层or语句\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testMultiLevelOr() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        String sql = \"select id from travelrecord \"\n        \t\t\t+ \" where id = 1 and ( fee=3 or days=5 or (traveldate = '2015-05-04 00:00:07.375' \"\n        \t\t\t+ \" and (user_id=2 or fee=days or fee = 0))) and name = 'zhangsan'\" ;\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n\n        Assert.assertTrue(rrs.getNodes().length == 1);\n        \n\t    sql = \"select id from travelrecord \"\n\t    \t\t\t+ \" where id = 1 and ( fee=3 or days=5 or (traveldate = '2015-05-04 00:00:07.375' \"\n\t    \t\t\t+ \" and (user_id=2 or fee=days or fee = 0))) and name = 'zhangsan' or id = 2000001\" ;\n\t    rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n\t\n\t    Assert.assertTrue(rrs.getNodes().length == 2);\n\t    \n\t    sql = \"select id from travelrecord \"\n    \t\t\t+ \" where id = 1 and ( fee=3 or days=5 or (traveldate = '2015-05-04 00:00:07.375' \"\n    \t\t\t+ \" and (user_id=2 or fee=days or fee = 0))) and name = 'zhangsan' or id = 2000001 or id = 4000001\" ;\n\t    rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n\t\n\t    Assert.assertTrue(rrs.getNodes().length == 3);\n    }\n    \n    /**\n     * 测试 global table 的or语句\n     * \n     *\n     * @throws Exception\n     */\n    @Test\n    public void testGlobalTableOr() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        String sql = \"select id from company where 1 = 1 and name ='company1' or name = 'company2'\" ;\n        for(int i = 0; i < 20; i++) {\n        \tRouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.SELECT, sql, null, null, cachePool);\n            Assert.assertTrue(rrs.getNodes().length == 1);\n        }\n    }\n    \n    /**\n     * 测试别名路由\n     *\n     * @throws Exception\n     */\n    public void testAlias() throws Exception {\n\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        RouteResultset rrs = null;\n        //不支持childtable 批量插入\n        //update 全局表\n        String sql = \"update company a set name = '' where a.id = 1;\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null,\n                    cachePool);\n\n        Assert.assertEquals(3, rrs.getNodes().length);\n\n        //update带别名时的路由\n        sql = \"update travelrecord a set name = '' where a.id = 1;\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null,\n                    cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        \n        //别名大小写路由\n\t\tsql = \"select * from travelrecord A where a.id = 1;\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null,\n                    cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n    }\n\n\n\t/**\n\t * 测试\"schema.table\"\n\t *\n\t * @throws Exception\n\t */\n\tpublic void testSchemaTable() throws Exception {\n\n\t\tSchemaConfig schema = schemaMap.get(\"schema_table_test\");\n\t\tRouteResultset rrs = null;\n\t\t// 1 schema==当前逻辑schema\n\t\tString sql = \"select * from schema_table_test.offer   where id = 1;\";\n\t\trrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n\n\t\tAssert.assertEquals(1, rrs.getNodes().length);\n\t\tAssert.assertTrue(rrs.getNodes()[0].getName().equals(\"dn1\"));\n\n\t\t// 2 schema !=当前schema,路由到默认节点。带alias\n\t\tsql = \"select * from noExistSchema.offer a where a.id = 1;\";\n\t\trrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n\t\tAssert.assertEquals(1, rrs.getNodes().length);\n\t\tAssert.assertTrue(rrs.getNodes()[0].getName().equals(\"dn2\"));\n\n\t\t// 不带alias\n\t\tsql = \"select * from noExistSchema.offer  where id = 1;\";\n\t\trrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n\t\tAssert.assertEquals(1, rrs.getNodes().length);\n\t\tAssert.assertTrue(rrs.getNodes()[0].getName().equals(\"dn2\"));\n\n\t\t// 3 默认节点不存在，抛错\n\t\tschema = schemaMap.get(\"TESTDB\");\n\t\tsql = \"select * from schema_table_test.offer   where id = 1;\";\n\t\ttry {\n\t\t\trrs = routeStrategy.route(new SystemConfig(), schema, 1, sql, null, null, cachePool);\n\t\t} catch (Exception e) {\n\t\t\tAssert.assertTrue(e.getMessage().contains(\n\t\t\t\t\t\"can't find table define in schema SCHEMA_TABLE_TEST.OFFER alias：schema_table_test.offer, schema:TESTDB\"));\n\t\t}\n\n\t}   \n\n    private String formatSql(String sql) {\n        MySqlStatementParser parser = new MySqlStatementParser(sql);\n        SQLStatement stmt = parser.parseStatement();\n        return stmt.toString();\n    }\n\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/DruidMysqlSqlParserTest.java",
    "content": "package io.mycat.route;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport org.junit.Test;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.server.parser.ServerParse;\nimport junit.framework.Assert;\n\npublic class DruidMysqlSqlParserTest\n{\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n    protected RouteStrategy routeStrategy;\n\n\tpublic DruidMysqlSqlParserTest() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n\t}\n\n\t@Test\n\tpublic void testLimitPage() throws SQLNonTransientException {\n\t\tString sql = \"select * from offer order by id desc limit 5,10\";\n\t\tSchemaConfig schema = schemaMap.get(\"mysqldb\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"dn1\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(\"dn2\", rrs.getNodes()[1].getName());\n\n        sql= rrs.getNodes()[0].getStatement() ;\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(0, rrs.getLimitStart());\n        Assert.assertEquals(15, rrs.getLimitSize());\n\t\t\n        sql=\"select * from offer1 order by id desc limit 5,10\" ;\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(5, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"dn1\", rrs.getNodes()[0].getName());\n\n\n        sql=\"select * from offer1 order by id desc limit 10\" ;\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(0, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"dn1\", rrs.getNodes()[0].getName());\n\t}\n\n\t@Test\n\tpublic void testLockTableSql() throws SQLNonTransientException{\n\t\tString sql = \"lock tables goods write\";\n\t\tSchemaConfig schema = schemaMap.get(\"TESTDB\");\n\t\tRouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, ServerParse.LOCK, sql, null, null, cachePool);\n\t\tAssert.assertEquals(3, rrs.getNodes().length);\n\t}\n\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/DruidMysqlSqlSubqueriesParserTest.java",
    "content": "package io.mycat.route;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport org.junit.Test;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport junit.framework.Assert;\n\n@SuppressWarnings(\"deprecation\")\npublic class DruidMysqlSqlSubqueriesParserTest\n{\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n    protected RouteStrategy routeStrategy;\n\n\tpublic DruidMysqlSqlSubqueriesParserTest() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n\t}\n\n\t\n\t@Test\n\tpublic void testSubQueries() throws SQLNonTransientException {\n\t\t//子查询测试需要构建ServerConnection. 暂时不在单元测试中体现.以测试报告的形式体现\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/DruidOracleSqlParserTest.java",
    "content": "package io.mycat.route;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport org.junit.Test;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport junit.framework.Assert;\n\n\npublic class DruidOracleSqlParserTest\n{\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n    protected RouteStrategy routeStrategy;\n\n\tpublic DruidOracleSqlParserTest() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n\t}\n\n    @Test\n    public void testInsertUpdate() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"oracledb\");\n\n   String     sql = \"insert into offer1(group_id,offer_id,member_id)values(234,123,'abc')\";\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(-1, rrs.getLimitSize());\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(-1, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"d_oracle1\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(\n                \"insert into offer1(group_id,offer_id,member_id)values(234,123,'abc')\",\n                rrs.getNodes()[0].getStatement());\n\n        sql = \"update offer set name='x'\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(-1, rrs.getLimitSize());\n        Assert.assertEquals(false, rrs.isCacheAble());\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(-1, rrs.getNodes()[0].getLimitSize());\n\n    }\n\n\n\n    @Test\n\tpublic void testLimitToOraclePage() throws SQLNonTransientException {\n\t\tString sql = \"select * from offer order by id desc limit 5,10\";\n\t\tSchemaConfig schema = schemaMap.get(\"oracledb\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"d_oracle1\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(\"d_oracle2\", rrs.getNodes()[1].getName());\n\n\t\t// 语法解析报错，先屏蔽\n//        sql= rrs.getNodes()[0].getStatement() ;\n//        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n//                null, cachePool);\n//        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n//        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n//        Assert.assertEquals(0, rrs.getLimitStart());\n//        Assert.assertEquals(15, rrs.getLimitSize());\n\t\t\n        sql=\"select * from offer1 order by id desc limit 5,10\" ;\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(5, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"d_oracle1\", rrs.getNodes()[0].getName());\n\t}\n\n\n\n    @Test\n    public void testOraclePageSQL() throws SQLNonTransientException {\n        String sql = \"SELECT *\\n\" +\n                \"FROM (SELECT XX.*, ROWNUM AS RN \\n\" +\n                \" FROM (\\n\" +\n                \"SELECT *   FROM offer\\n\" +\n                \"                ) XX\\n\" +\n                \"        WHERE ROWNUM <= 15\\n\" +\n                \"        ) XXX\\n\" +\n                \"WHERE RN > 5 \\n\";\n        SchemaConfig schema = schemaMap.get(\"oracledb\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"d_oracle1\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(\"d_oracle2\", rrs.getNodes()[1].getName());\n\n        sql = \"SELECT *\\n\" +\n                \"FROM (SELECT XX.*, ROWNUM AS RN \\n\" +\n                \" FROM (\\n\" +\n                \"SELECT *   FROM offer1\" +\n                \"                ) XX\\n\" +\n                \"        WHERE ROWNUM <= 15\\n\" +\n                \"        ) XXX\\n\" +\n                \"WHERE RN > 5 \\n\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(5, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(sql,rrs.getNodes()[0].getStatement()) ;\n        Assert.assertEquals(\"d_oracle1\", rrs.getNodes()[0].getName());\n\n        sql=\"SELECT *\\n\" +\n                \"FROM (SELECT t.*, ROW_NUMBER() OVER (ORDER BY sid DESC) AS ROWNUM1\\n\" +\n                \"\\tFROM offer t\\n\" +\n                \"\\tWHERE sts <> 'N'\\n\" +\n                \"\\t\\t\\n\" +\n                \"\\t) XX\\n\" +\n                \"WHERE ROWNUM1 > 5\\n\" +\n                \"\\tAND ROWNUM1 <= 15\\n\";\n\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"d_oracle1\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(\"d_oracle2\", rrs.getNodes()[1].getName());\n\n\n        sql=\"SELECT *\\n\" +\n                \"FROM (SELECT t.*, ROW_NUMBER() OVER (ORDER BY sid DESC) AS ROWNUM1\\n\" +\n                \"\\tFROM offer1  t\\n\" +\n                \"\\tWHERE sts <> 'N'\\n\" +\n                \"\\t\\t\\n\" +\n                \"\\t) XX\\n\" +\n                \"WHERE ROWNUM1 > 5\\n\" +\n                \"\\tAND ROWNUM1 <= 15\\n\";\n\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(5, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(sql,rrs.getNodes()[0].getStatement()) ;\n        Assert.assertEquals(\"d_oracle1\", rrs.getNodes()[0].getName());\n\n\n        sql=\"select sid from (select sid from offer ) where rownum<=10\"  ;\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(0, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(sql,rrs.getNodes()[0].getStatement()) ;\n        Assert.assertEquals(\"d_oracle1\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(\"d_oracle2\", rrs.getNodes()[1].getName());\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/DruidPostgresqlSqlParserTest.java",
    "content": "package io.mycat.route;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport org.junit.Test;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport junit.framework.Assert;\n\npublic class DruidPostgresqlSqlParserTest\n{\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n    protected RouteStrategy routeStrategy;\n\n\tpublic DruidPostgresqlSqlParserTest() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n\t}\n\n\t@Test\n\tpublic void testLimitToPgPage() throws SQLNonTransientException {\n\t\tString sql = \"select * from offer order by id desc limit 5,10\";\n\t\tSchemaConfig schema = schemaMap.get(\"pgdb\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n\n        sql= rrs.getNodes()[0].getStatement() ;\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(0, rrs.getLimitStart());\n        Assert.assertEquals(15, rrs.getLimitSize());\n\t\t\n        sql=\"select * from offer1 order by id desc limit 5,10\" ;\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(5, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n\t}\n\n\n\n    @Test\n    public void testPGPageSQL() throws SQLNonTransientException {\n        String sql = \"select sid from offer order by sid limit 10 offset 5\";\n        SchemaConfig schema = schemaMap.get(\"pgdb\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n\n        sql = \"select sid from offer1 order by sid limit 10 offset 5\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(5, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"SELECT sid\\n\" +\n                \"FROM offer1\\n\" +\n                \"ORDER BY sid\\n\" +\n                \"LIMIT 10 OFFSET 5\",rrs.getNodes()[0].getStatement()) ;\n\n\n\n\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/DruidSqlServerSqlParserTest.java",
    "content": "package io.mycat.route;\n\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport org.junit.Test;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport junit.framework.Assert;\n\npublic class DruidSqlServerSqlParserTest\n{\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n    protected RouteStrategy routeStrategy;\n\n\tpublic DruidSqlServerSqlParserTest() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n\t}\n\n\t@Test\n\tpublic void testLimitToSqlServerPage() throws SQLNonTransientException {\n\t\tString sql = \"select * from offer order by id desc limit 5,10\";\n\t\tSchemaConfig schema = schemaMap.get(\"sqlserverdb\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(2, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"sqlserver_1\", rrs.getNodes()[0].getName());\n        Assert.assertEquals(\"sqlserver_2\", rrs.getNodes()[1].getName());\n\n///语句解析报错，先注释掉\n//        sql= rrs.getNodes()[0].getStatement() ;\n//        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n//                null, cachePool);\n//        Assert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n//        Assert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n//        Assert.assertEquals(0, rrs.getLimitStart());\n//        Assert.assertEquals(15, rrs.getLimitSize());\n\t\t\n        sql=\"select * from offer1 order by id desc limit 5,10\" ;\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(1, rrs.getNodes().length);\n        Assert.assertEquals(5, rrs.getLimitStart());\n        Assert.assertEquals(10, rrs.getLimitSize());\n        Assert.assertEquals(5, rrs.getNodes()[0].getLimitStart());\n        Assert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n        Assert.assertEquals(\"sqlserver_1\", rrs.getNodes()[0].getName());\n\n\t}\n\n\n\n\t@Test\n\tpublic void testSqlServerPageSQL() throws SQLNonTransientException {\n\t\tString sql = \"SELECT *\\n\" + \"FROM (SELECT sid, ROW_NUMBER() OVER (ORDER BY sid DESC) AS ROWNUM\\n\"\n\t\t\t\t+ \"\\tFROM offer \\n\" + \"\\tWHERE sts <> 'N'\\n\" + \"\\t\\t\\t) XX\\n\" + \"WHERE ROWNUM > 5\\n\"\n\t\t\t\t+ \"\\tAND ROWNUM <= 15\\n\";\n\t\tSchemaConfig schema = schemaMap.get(\"sqlserverdb\");\n\t\tRouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n\t\tAssert.assertEquals(2, rrs.getNodes().length);\n\t\tAssert.assertEquals(5, rrs.getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getLimitSize());\n\t\tAssert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n\t\tAssert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n\t\tAssert.assertEquals(\"sqlserver_1\", rrs.getNodes()[0].getName());\n\t\tAssert.assertEquals(\"sqlserver_2\", rrs.getNodes()[1].getName());\n\n\t\tsql = \"SELECT *\\n\" + \"FROM (SELECT sid, ROW_NUMBER() OVER (ORDER BY sid DESC) AS ROWNUM\\n\" + \"\\tFROM offer1 \\n\"\n\t\t\t\t+ \"\\tWHERE sts <> 'N'\\n\" + \"\\t\\t\\t) XX\\n\" + \"WHERE ROWNUM > 5\\n\" + \"\\tAND ROWNUM <= 15\\n\";\n\t\trrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n\t\tAssert.assertEquals(1, rrs.getNodes().length);\n\t\tAssert.assertEquals(5, rrs.getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getLimitSize());\n\t\tAssert.assertEquals(5, rrs.getNodes()[0].getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n\t\tAssert.assertEquals(sql, rrs.getNodes()[0].getStatement());\n\t\tAssert.assertEquals(\"sqlserver_1\", rrs.getNodes()[0].getName());\n\n\t\tsql = \"select * from ( select row_number()over(order by tempColumn)tempRowNumber,* from ( select top \\n\"\n\t\t\t\t+ \"15 tempColumn=0, sid \\n\"\n\t\t\t\t+ \"from offer  where sts<>'N' and asf like '%'+'akka'+'%' order by sid  )t )tt where tempRowNumber>5\";\n\t\trrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n\t\tAssert.assertEquals(2, rrs.getNodes().length);\n\t\tAssert.assertEquals(5, rrs.getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getLimitSize());\n\t\tAssert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n\t\tAssert.assertEquals(15, rrs.getNodes()[0].getLimitSize());\n\t\tAssert.assertEquals(\"sqlserver_1\", rrs.getNodes()[0].getName());\n\t\tAssert.assertEquals(\"sqlserver_2\", rrs.getNodes()[1].getName());\n\n\t\tsql = \"select * from ( select row_number()over(order by tempColumn)tempRowNumber,* from ( select top \\n\"\n\t\t\t\t+ \"15 tempColumn=0, sid \\n\"\n\t\t\t\t+ \"from offer1  where sts<>'N' and asf like '%'+'akka'+'%' order by sid  )t )tt where tempRowNumber>5\";\n\t\trrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n\t\tAssert.assertEquals(1, rrs.getNodes().length);\n\t\tAssert.assertEquals(5, rrs.getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getLimitSize());\n\t\tAssert.assertEquals(5, rrs.getNodes()[0].getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n\t\tAssert.assertEquals(sql, rrs.getNodes()[0].getStatement());\n\t\tAssert.assertEquals(\"sqlserver_1\", rrs.getNodes()[0].getName());\n\n\t\tsql = \"SELECT TOP 10 sid  \\n\" + \" FROM offer  where sts<>'N' and asf like '%'+'akka'+'%' \\n\"\n\t\t\t\t+ \" ORDER BY sid desc\";\n\t\trrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n\t\tAssert.assertEquals(2, rrs.getNodes().length);\n\t\tAssert.assertEquals(0, rrs.getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getLimitSize());\n\t\tAssert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n\t\tAssert.assertEquals(sql, rrs.getNodes()[0].getStatement());\n\t\tAssert.assertEquals(\"sqlserver_1\", rrs.getNodes()[0].getName());\n\t\tAssert.assertEquals(\"sqlserver_2\", rrs.getNodes()[1].getName());\n\n\t}\n\n\t@Test\n\tpublic void testTopPageSQL() throws SQLNonTransientException {\n\t\tSchemaConfig schema = schemaMap.get(\"sqlserverdb\");\n\t\tRouteResultset rrs = null;\n\n\t\tString sql = \"SELECT TOP 10  *  \\n\" + \" FROM offer1  where sts<>'N' and asf like '%'+'akka'+'%' \\n\"\n\t\t\t\t+ \" ORDER BY sid desc\";\n\t\trrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n\n\t\tAssert.assertEquals(1, rrs.getNodes().length);\n\t\tAssert.assertEquals(0, rrs.getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getLimitSize());\n\t\tAssert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n\t\tAssert.assertEquals(sql, rrs.getNodes()[0].getStatement());\n\t\tAssert.assertEquals(\"sqlserver_1\", rrs.getNodes()[0].getName());\n\n\t\tsql = \"SELECT TOP 10  offer1.name,offer1.id  \\n\" + \" FROM offer1  where sts<>'N' and asf like '%'+'akka'+'%' \\n\"\n\t\t\t\t+ \" ORDER BY sid desc\";\n\t\trrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null, null, cachePool);\n\n\t\tAssert.assertEquals(1, rrs.getNodes().length);\n\t\tAssert.assertEquals(0, rrs.getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getLimitSize());\n\t\tAssert.assertEquals(0, rrs.getNodes()[0].getLimitStart());\n\t\tAssert.assertEquals(10, rrs.getNodes()[0].getLimitSize());\n\t\tAssert.assertEquals(sql, rrs.getNodes()[0].getStatement());\n\t\tAssert.assertEquals(\"sqlserver_1\", rrs.getNodes()[0].getName());\n\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/HintDBTypeTest.java",
    "content": "package io.mycat.route;\r\n\r\nimport io.mycat.MycatServer;\r\nimport io.mycat.SimpleCachePool;\r\nimport io.mycat.cache.CacheService;\r\nimport io.mycat.cache.LayerCachePool;\r\nimport io.mycat.config.loader.SchemaLoader;\r\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\r\nimport io.mycat.config.model.SchemaConfig;\r\nimport io.mycat.config.model.SystemConfig;\r\nimport io.mycat.route.factory.RouteStrategyFactory;\r\nimport io.mycat.server.parser.ServerParse;\r\n\r\nimport java.util.Map;\r\n\r\nimport junit.framework.Assert;\r\n\r\nimport org.junit.Test;\r\n\r\npublic class HintDBTypeTest {\r\n\tprotected Map<String, SchemaConfig> schemaMap;\r\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\r\n\tprotected RouteStrategy routeStrategy;\r\n\r\n\tpublic HintDBTypeTest() {\r\n\t\tString schemaFile = \"/route/schema.xml\";\r\n\t\tString ruleFile = \"/route/rule.xml\";\r\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\r\n\t\tschemaMap = schemaLoader.getSchemas();\r\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\r\n        RouteStrategyFactory.init();\r\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\r\n\t}\r\n\t/**\r\n     * 测试注解\r\n     *\r\n     * @throws Exception\r\n     */\r\n    @Test\r\n    public void testHint() throws Exception {\r\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\r\n       //使用注解（新注解，/*!mycat*/），runOnSlave=false 强制走主节点\r\n        String sql = \"/*!mycat:db_type=master*/select * from employee where sharding_id=1\";\r\n        CacheService cacheService = new CacheService();\r\n        RouteService routerService = new RouteService(cacheService);\r\n        RouteResultset rrs = routerService.route(new SystemConfig(), schema, ServerParse.SELECT, sql, \"UTF-8\", null);\r\n        Assert.assertTrue(!rrs.getRunOnSlave());\r\n\r\n        //使用注解（新注解，/*#mycat*/），runOnSlave=false 强制走主节点\r\n        sql = \"/*#mycat:db_type=master*/select * from employee where sharding_id=1\";\r\n        rrs = routerService.route(new SystemConfig(), schema, ServerParse.SELECT, sql, \"UTF-8\", null);\r\n        Assert.assertTrue(!rrs.getRunOnSlave());\r\n        \r\n        //使用注解（新注解，/*mycat*/），runOnSlave=false 强制走主节点\r\n        sql = \"/*mycat:db_type=master*/select * from employee where sharding_id=1\";\r\n        rrs = routerService.route(new SystemConfig(), schema, ServerParse.SELECT, sql, \"UTF-8\", null);\r\n        Assert.assertTrue(!rrs.getRunOnSlave());\r\n        \r\n        //不使用注解，runOnSlave=null, 根据读写分离策略走主从库\r\n        sql = \"select * from employee where sharding_id=1\";\r\n        rrs = routerService.route(new SystemConfig(), schema, ServerParse.SELECT, sql, \"UTF-8\", null);\r\n        Assert.assertTrue(rrs.getRunOnSlave()==null);\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/test/java/io/mycat/route/HintTest.java",
    "content": "package io.mycat.route;\n\nimport java.util.Map;\n\nimport junit.framework.Assert;\n\nimport org.junit.Test;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.CacheService;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteService;\nimport io.mycat.route.RouteStrategy;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.server.parser.ServerParse;\n\npublic class HintTest {\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n\tprotected RouteStrategy routeStrategy;\n\n\tpublic HintTest() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"fdbparser\");\n\t}\n\t/**\n     * 测试注解\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testHint() throws Exception {\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n       //使用注解（新注解，/*后面没有空格），路由到1个节点\n        String sql = \"/*!mycat: sql = select * from employee where sharding_id = 10010 */select * from employee\";\n        CacheService cacheService = new CacheService();\n        RouteService routerService = new RouteService(cacheService);\n        RouteResultset rrs = routerService.route(new SystemConfig(), schema, ServerParse.SELECT, sql, \"UTF-8\", null);\n        Assert.assertTrue(rrs.getNodes().length == 1);\n\n        //使用注解（新注解，/*后面有空格），路由到1个节点\n        sql = \"/*#mycat: sql = select * from employee where sharding_id = 10000 */select * from employee\";\n        rrs = routerService.route(new SystemConfig(), schema, ServerParse.SELECT, sql, \"UTF-8\", null);\n        Assert.assertTrue(rrs.getNodes().length == 1);\n        \n        // 支持hint里面带有”，“\n        sql = \"/*!mycat: sql = select * from employee where sharding_id in ('10000','10010') */select * from employee\";\n        rrs = routerService.route(new SystemConfig(), schema, ServerParse.SELECT, sql, \"UTF-8\", null);\n        Assert.assertTrue(rrs.getNodes().length == 2);\n\n        //不用注解，路由到2个节点\n        sql = \"select * from employee\";\n        rrs = routerService.route(new SystemConfig(), schema, ServerParse.SELECT, sql, \"UTF-8\", null);\n        Assert.assertTrue(rrs.getNodes().length == 2);\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/TestSelectBetweenSqlParser.java",
    "content": "package io.mycat.route;\n\nimport java.io.IOException;\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\nimport junit.framework.Assert;\n\nimport org.junit.Test;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\nimport io.mycat.server.ServerConnection;\n\n/**\n * 修改内容\n * \n * @author lxy\n *\n */\npublic class TestSelectBetweenSqlParser {\n\tprotected Map<String, SchemaConfig> schemaMap;\n\tprotected LayerCachePool cachePool = new SimpleCachePool();\n\n\tpublic TestSelectBetweenSqlParser() {\n\t\tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschemaMap = schemaLoader.getSchemas();\n\t\tMycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n\t\tRouteStrategyFactory.init();\n\t}\n\n\t@Test\n\tpublic void testBetweenSqlRoute() throws SQLNonTransientException, IOException {\n\t\tString sql = \"select * from offer_detail where offer_id between 1 and 33\";\n\t\tSchemaConfig schema = schemaMap.get(\"cndb\");\n\t\tRouteResultset rrs = RouteStrategyFactory.getRouteStrategy().route(new SystemConfig(),schema, -1, sql, null,\n\t\t\t\tnull, cachePool);\n\t\tAssert.assertEquals(5, rrs.getNodes().length);\n\t\t\n\t\tsql = \"select * from offer_detail where col_1 = 33 and offer_id between 1 and 33 and col_2 = 18\";\n\t\tschema = schemaMap.get(\"cndb\");\n\t\trrs = RouteStrategyFactory.getRouteStrategy().route(new SystemConfig(),schema, -1, sql, null,\n\t\t\t\tnull, cachePool);\n\t\tAssert.assertEquals(5, rrs.getNodes().length);\n\t\t\n//\t\tsql = \"select b.* from offer_date b join  offer_detail a on a.id=b.id \" +\n//\t\t\t\t\"where b.col_date between '2014-02-02' and '2014-04-12' and col_1 = 3 and offer_id between 1 and 33\";\n\t\t\n\t\t\n\t\tsql = \"select b.* from offer_detail a  join  offer_date b on a.id=b.id \" +\n\t\t\t\t\"where b.col_date between '2014-02-02' and '2014-04-12' and col_1 = 3 and offer_id between 1 and 33\";\n//\t\tsql = \"select a.* from offer_detail a join offer_date b on a.id=b.id \" +\n//\t\t\t\t\"where b.col_date = '2014-04-02' and col_1 = 33 and offer_id =1\";\n\t\tschema = schemaMap.get(\"cndb\");\n\t\t// 两个路由规则不一样的表现在 走catlet. 不再取交集, catlet 测试时需要前端连接.这里注释掉.\n//\t\trrs = RouteStrategyFactory.getRouteStrategy().route(new SystemConfig(),schema, -1, sql, null,\n//\t\t\t\tnull, cachePool);\n//\t\tAssert.assertEquals(2, rrs.getNodes().length);    //这里2个表都有条件路由，取的是交集, \n\t\t\n\t\t//确认大于小于操作符\n\t\tsql = \"select b.* from  offer_date b \" +\n\t\t\t\t\"where b.col_date > '2014-02-02'\";\n//\t\tsql = \"select a.* from offer_detail a join offer_date b on a.id=b.id \" +\n//\t\t\t\t\"where b.col_date = '2014-04-02' and col_1 = 33 and offer_id =1\";\n\t\tschema = schemaMap.get(\"cndb\");\n\t\trrs = RouteStrategyFactory.getRouteStrategy().route(new SystemConfig(),schema, -1, sql, null,\n\t\t\t\tnull, cachePool);\n\t\tAssert.assertEquals(128, rrs.getNodes().length);\n\t\t\n\t\tsql = \"select * from offer_date where col_1 = 33 and col_date between '2014-01-02' and '2014-01-12'\";\n\t\tschema = schemaMap.get(\"cndb\");\n\t\trrs = RouteStrategyFactory.getRouteStrategy().route(new SystemConfig(),schema, -1, sql, null,\n\t\t\t\tnull, cachePool);\n\t\tAssert.assertEquals(2, rrs.getNodes().length);\n\n\t\tsql = \"select * from offer_date a where col_1 = 33 and a.col_date between '2014-01-02' and '2014-01-12'\";\n\t\tschema = schemaMap.get(\"cndb\");\n\t\trrs = RouteStrategyFactory.getRouteStrategy().route(new SystemConfig(),schema, -1, sql, null,\n\t\t\t\tnull, cachePool);\n\t\tAssert.assertEquals(2, rrs.getNodes().length);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/function/AutoPartitionByLongTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\nimport junit.framework.Assert;\n\nimport org.junit.Test;\n\npublic class AutoPartitionByLongTest {\n\n\t@Test\n\tpublic void test()  {\n\t\tAutoPartitionByLong autoPartition=new AutoPartitionByLong();\n\t\tautoPartition.setMapFile(\"autopartition-long.txt\");\n\t\tautoPartition.init();\n\t\tString idVal=\"0\";\n\t\tAssert.assertEquals(true, 0==autoPartition.calculate(idVal));\n\t\t\n\t\tidVal=\"2000000\";\n\t\tAssert.assertEquals(true, 0==autoPartition.calculate(idVal)); \n\t\t\n\t\tidVal=\"2000001\";\n\t\tAssert.assertEquals(true, 1==autoPartition.calculate(idVal)); \n\t\t\n\t\tidVal=\"4000000\";\n\t\tAssert.assertEquals(true, 1==autoPartition.calculate(idVal)); \n\t\t\n\t\tidVal=\"4000001\";\n\t\tAssert.assertEquals(true, 2==autoPartition.calculate(idVal)); \n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/function/PartitionByCRC32PreSlotTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.config.model.rule.RuleConfig;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport static io.mycat.route.function.PartitionByCRC32PreSlot.genDataNodesString;\n\n\npublic class PartitionByCRC32PreSlotTest {\n\n    static TableConfig genTableConfig(int count) {\n        RuleConfig rule = new RuleConfig(\"id\", \"crc32slot\");\n        String sb = genDataNodesString(count);\n        TableConfig tableConf = new TableConfig(\"test\", \"id\", true, false, -1, sb,\n                null, rule, true, null, false, null, null, null, false);\n        return tableConf;\n    }\n\n    @Test\n    public void test() {\n        PartitionByCRC32PreSlot partition = new PartitionByCRC32PreSlot();\n        partition.setRuleName(\"test\");\n        partition.setTableConfig(genTableConfig(1000));\n        partition.reInit();\n\n        Assert.assertEquals(true, 521 == partition.calculate(\"1000316\"));\n        Assert.assertEquals(true, 637 == partition.calculate(\"2\"));\n\n\n        partition.setTableConfig(genTableConfig(2));\n        partition.reInit();\n\n        Assert.assertEquals(true, 0 == partition.calculate(\"1\"));\n        Assert.assertEquals(true, 1 == partition.calculate(\"2\"));\n        Assert.assertEquals(true, 0 == partition.calculate(\"3\"));\n        Assert.assertEquals(true, 1 == partition.calculate(\"4\"));\n        Assert.assertEquals(true, 0 == partition.calculate(\"5\"));\n        Assert.assertEquals(true, 0 == partition.calculate(\"6\"));\n        Assert.assertEquals(true, 0 == partition.calculate(\"7\"));\n        Assert.assertEquals(true, 0 == partition.calculate(\"8\"));\n        Assert.assertEquals(true, 0 == partition.calculate(\"9\"));\n\n        Assert.assertEquals(true, 0 == partition.calculate(\"9999\"));\n        Assert.assertEquals(true, 1 == partition.calculate(\"123456789\"));\n        Assert.assertEquals(true, 1 == partition.calculate(\"35565\"));\n\n\n        partition.setTableConfig(genTableConfig(3));\n        partition.reInit();\n\n        Assert.assertEquals(true, 1 == partition.calculate(\"1\"));\n        Assert.assertEquals(true, 1 == partition.calculate(\"2\"));\n        Assert.assertEquals(true, 0 == partition.calculate(\"3\"));\n        Assert.assertEquals(true, 2 == partition.calculate(\"4\"));\n        Assert.assertEquals(true, 0 == partition.calculate(\"5\"));\n        Assert.assertEquals(true, 1 == partition.calculate(\"6\"));\n        Assert.assertEquals(true, 1 == partition.calculate(\"7\"));\n        Assert.assertEquals(true, 0 == partition.calculate(\"8\"));\n        Assert.assertEquals(true, 0 == partition.calculate(\"9\"));\n\n        Assert.assertEquals(true, 0 == partition.calculate(\"9999\"));\n        Assert.assertEquals(true, 2 == partition.calculate(\"123456789\"));\n        Assert.assertEquals(true, 2 == partition.calculate(\"35565\"));\n    }\n\n    public static void main(String[] args) {\n\n        for (int i = 0; i < 20; i++) {\n            int y = 9;\n            int count = 3;\n            long slot = i % y;\n            int slotSize = y / count;\n\n            Long index = slot / slotSize;\n            if (slotSize * count != y && index > count - 1) {\n                index = index - 1;\n            }\n            System.out.println(slot + \"   \" + index);\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/function/PartitionByDateTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese\n * opensource volunteers. you can redistribute it and/or modify it under the\n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n *\n * Any questions about this component can be directed to it's project Web address\n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class PartitionByDateTest {\n\n\t@Test\n\tpublic void test()  {\n\t\tPartitionByDate partition=new PartitionByDate();\n\n\t\tpartition.setDateFormat(\"yyyy-MM-dd\");\n\t\tpartition.setsBeginDate(\"2014-01-01\");\n\t\tpartition.setsPartionDay(\"10\");\n\n\t\tpartition.init();\n\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-01\"));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-10\"));\n\t\tAssert.assertEquals(true, 1 == partition.calculate(\"2014-01-11\"));\n\t\tAssert.assertEquals(true, 12 == partition.calculate(\"2014-05-01\"));\n\n\t\tpartition.setDateFormat(\"yyyy-MM-dd\");\n\t\tpartition.setsBeginDate(\"2014-01-01\");\n\t\tpartition.setsEndDate(\"2014-01-31\");\n\t\tpartition.setsPartionDay(\"10\");\n\t\tpartition.init();\n//\n//\t\t/**\n//\t\t * 0 : 01.01-01.10,02.10-02.19\n//\t\t * 1 : 01.11-01.20,02.20-03.01\n//\t\t * 2 : 01.21-01.30,03.02-03.12\n//\t\t * 3  ： 01.31-02-09,03.13-03.23\n//\t\t */\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-01\"));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-10\"));\n\t\tAssert.assertEquals(true, 1 == partition.calculate(\"2014-01-11\"));\n\t\tAssert.assertEquals(true, 3 == partition.calculate(\"2014-02-01\"));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-02-19\"));\n\t\tAssert.assertEquals(true, 1 == partition.calculate(\"2014-02-20\"));\n\t\tAssert.assertEquals(true, 1 == partition.calculate(\"2014-03-01\"));\n\t\tAssert.assertEquals(true, 2 == partition.calculate(\"2014-03-02\"));\n\t\tAssert.assertEquals(true, 2 == partition.calculate(\"2014-03-11\"));\n\t\tAssert.assertEquals(true, 3 == partition.calculate(\"2014-03-20\"));\n\n\t\t//测试默认1\n\t\tpartition.setDateFormat(\"yyyy-MM-dd\");\n\t\tpartition.setsBeginDate(\"2014-01-01\");\n\t\tpartition.setsEndDate(\"2014-01-31\");\n\t\tpartition.setsPartionDay(\"1\");\n\t\tpartition.init();\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-01\"));\n\t\tAssert.assertEquals(true, 9 == partition.calculate(\"2014-01-10\"));\n\t\tAssert.assertEquals(true, 10 == partition.calculate(\"2014-01-11\"));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-02-01\"));\n\t\tSystem.out.println(partition.calculate(\"2014-02-19\"));\n\n\n\t\t//自然日测试\n\t\t//1、只开启自然日分表开关\n\t\tPartitionByDate partition2=new PartitionByDate();\n\t\tpartition2.setDateFormat(\"yyyy-MM-dd\");\n\t\tpartition2.setsNaturalDay(\"1\");\n\t\tpartition2.init();\n\t\t//Assert.assertEquals(true, 6 == partition2.calculate(\"2014-01-20\"));\n\t\tAssert.assertEquals(true, 19 == partition2.calculate(\"2014-01-20\"));\n\t\tAssert.assertEquals(true, 0 == partition2.calculate(\"2014-03-01\"));\n\t\tAssert.assertEquals(true, 30 == partition2.calculate(\"2018-03-31\"));\n\n\n\t\t//2、顺便开启开始时间\n\t\tpartition2.setDateFormat(\"yyyy-MM-dd\");\n\t\tpartition2.setsNaturalDay(\"1\");\n\t\tpartition2.setsPartionDay(\"1\");\n\t\tpartition2.setsBeginDate(\"2014-01-02\");\n\t\tpartition2.init();\n\n\t\tAssert.assertEquals(true, 19 == partition2.calculate(\"2014-01-20\"));\n\t\tAssert.assertEquals(true, 0 == partition2.calculate(\"2014-03-01\"));\n\t\tAssert.assertEquals(true, 30 == partition2.calculate(\"2018-03-31\"));\n\n\t\t//2、顺便开启开始时间,结束时间不足28天（开启自然日失败，默认间隔模式）PartionDay=1\n\t\tPartitionByDate partition3=new PartitionByDate();\n\t\tpartition3.setDateFormat(\"yyyy-MM-dd\");\n\t\tpartition3.setsNaturalDay(\"1\");\n\t\tpartition3.setsPartionDay(\"1\");\n\t\tpartition3.setsBeginDate(\"2014-01-02\");\n\t\tpartition3.setsEndDate(\"2014-01-20\");\n\t\tpartition3.init();\n\t\tAssert.assertEquals(true, 0 == partition3.calculate(\"2014-01-02\"));\n\t\tAssert.assertEquals(true, 1 == partition3.calculate(\"2014-01-03\"));\n\t\tAssert.assertEquals(true, 2 == partition3.calculate(\"2014-01-04\"));\n\t\tAssert.assertEquals(true, 6 == partition3.calculate(\"2014-01-08\"));\n\t\tAssert.assertEquals(true, 8 == partition3.calculate(\"2014-01-10\"));\n\t\tAssert.assertEquals(true, 12 == partition3.calculate(\"2014-01-14\"));\n\t\tAssert.assertEquals(true, 18 == partition3.calculate(\"2014-01-20\"));\n\t\tSystem.out.println(partition3.calculate(\"2014-03-01\"));\n\t\t//Assert.assertEquals(true, 0 == partition3.calculate(\"2014-03-01\"));\n\n\t\t//3、顺便开启开始时间,结束时间不足28天（开启自然日失败，默认间隔模式）PartionDay=10 恢复间隔模式\n\t\tpartition.setsNaturalDay(\"1\");\n\t\tpartition.setsBeginDate(\"2014-01-01\");\n\t\tpartition.setsEndDate(\"2014-01-24\");\n\t\tpartition.setsPartionDay(\"10\");\n\t\tpartition.init();\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-01\"));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-10\"));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-05\"));\n\t\tAssert.assertEquals(true, 1 == partition.calculate(\"2014-01-20\"));\n\t\tSystem.out.println(\"------------success!----\");\n\n\n\t\t//4、顺便开启开始时间,结束时间超过29天 PartionDay=1\n\t\tpartition.setsNaturalDay(\"1\");\n\t\tpartition.setsBeginDate(\"2014-01-01\");\n\t\tpartition.setsEndDate(\"2014-01-29\");\n\t\tpartition.setsPartionDay(\"10\");\n\t\tpartition.init();\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-01\"));\n\t\tAssert.assertEquals(true, 9 == partition.calculate(\"2014-01-10\"));\n\t\tAssert.assertEquals(true, 4 == partition.calculate(\"2014-01-05\"));\n\t\tAssert.assertEquals(true, 19 == partition.calculate(\"2014-01-20\"));\n\t\tAssert.assertEquals(true, 30 == partition.calculate(\"2018-01-31\"));\n\n\n\t\t//4、顺便开启开始时间,结束时间超过29天 PartionDay=1\n\t\tpartition.setsNaturalDay(\"1\");\n\t\tpartition.setsBeginDate(\"2014-01-01\");\n\t\tpartition.setsEndDate(\"2018-01-29\");\n\t\tpartition.setsPartionDay(\"1\");\n\t\tpartition.init();\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-01\"));\n\t\tAssert.assertEquals(true, 9 == partition.calculate(\"2014-01-10\"));\n\t\tAssert.assertEquals(true, 4 == partition.calculate(\"2014-01-05\"));\n\t\tAssert.assertEquals(true, 19 == partition.calculate(\"2014-01-20\"));\n\t\tAssert.assertEquals(true, 30 == partition.calculate(\"2018-01-31\"));\n\t\tSystem.out.println(\"------------success!----\");\n\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/function/PartitionByHashModTest.java",
    "content": "package io.mycat.route.function;\n\nimport junit.framework.Assert;\nimport org.junit.Test;\n\nimport java.util.Random;\nimport java.util.concurrent.CountDownLatch;\n\n/**\n * 哈希值取模单元测试\n *\n * @author Hash Zhang\n */\npublic class PartitionByHashModTest {\n    String allChar = \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n    @Test\n    public void test() throws InterruptedException {\n        CountDownLatch countDownLatch = new CountDownLatch(1);\n        Task task1 = new Task(countDownLatch,63);\n        Task task2 = new Task(countDownLatch,64);\n\n        task1.start();\n        task2.start();\n        countDownLatch.countDown();\n        task1.join();\n        task2.join();\n    }\n\n    private class Task extends Thread{\n        CountDownLatch countDownLatch;\n        int count;\n\n        public Task(CountDownLatch countDownLatch,int count) {\n            this.countDownLatch = countDownLatch;\n            this.count = count;\n        }\n\n        @Override\n        public void run() {\n            try {\n                countDownLatch.await();\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n            PartitionByHashMod partitionByHashMod = new PartitionByHashMod();\n            partitionByHashMod.setCount(count);\n            Random random = new Random();\n            StringBuffer sb = new StringBuffer();\n            long start = System.currentTimeMillis();\n            for (int i = 0; i < 1000000; i++) {\n                for (int j = 0; j < 32; j++) {\n                    sb.append(allChar.charAt(random.nextInt(allChar.length())));\n                }\n                int result = partitionByHashMod.calculate(sb.toString());\n                sb = new StringBuffer();\n                Assert.assertTrue(0<=result && result<count);\n            }\n            long end = System.currentTimeMillis();\n            System.out.println(\"Shard Count is \"+count+\", time elapsed: \"+ (end-start));\n        }\n    }\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/function/PartitionByHotDateTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class PartitionByHotDateTest {\n\n\t@Test\n\tpublic void test()  {\nPartitionByHotDate partition = new PartitionByHotDate();\n\t\t\n\t\tpartition.setDateFormat(\"yyyy-MM-dd\");\n\t\tpartition.setsLastDay(\"10\");\n\t\tpartition.setsPartionDay(\"1\");\n\n\t\tpartition.init();\n\t\t\n\t\tDateFormat dateFormat = new SimpleDateFormat(\"yyyy-MM-dd\");\n\n\t\tCalendar cDate = Calendar.getInstance();\n\t\tcDate.set(Calendar.MONTH, cDate.get(Calendar.MONTH));\n\t\tcDate.set(Calendar.DATE, cDate.get(Calendar.DATE));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(dateFormat.format(cDate.getTime())));\n\t\t\n\t\tcDate = Calendar.getInstance();\n\t\tcDate.add(Calendar.DATE,-5);\n\t\tSystem.err.println(dateFormat.format(cDate.getTime()));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(dateFormat.format(cDate.getTime())));\n\t\t\n\t\tcDate = Calendar.getInstance();\n\t\tcDate.add(Calendar.DATE,-11);\n\t\tSystem.err.println(dateFormat.format(cDate.getTime()));\n\t\tAssert.assertEquals(true, 2 == partition.calculate(dateFormat.format(cDate.getTime())));\n\t\t\n\t\tcDate = Calendar.getInstance();\n\t\tcDate.add(Calendar.DATE, -21);\n\t\tSystem.err.println(dateFormat.format(cDate.getTime()));\n\t\tAssert.assertEquals(true, 12 == partition.calculate(dateFormat.format(cDate.getTime())));\n\n\t\tcDate = Calendar.getInstance();\n\t\tcDate.add(Calendar.DATE,-5);\n\t\tSystem.err.println(dateFormat.format(cDate.getTime()));\n\t\tAssert.assertEquals(true, 0 == partition.calculateRange(dateFormat.format(cDate.getTime()),dateFormat.format(Calendar.getInstance().getTime()))[0]);\n\t\t\n\t\tcDate = Calendar.getInstance();\n\t\tcDate.add(Calendar.DATE,-11);\n\t\tSystem.err.println(dateFormat.format(cDate.getTime()));\n\t\tAssert.assertEquals(true, 0 == partition.calculateRange(dateFormat.format(cDate.getTime()),dateFormat.format(Calendar.getInstance().getTime()))[0]);\n\t\tAssert.assertEquals(true, 1 == partition.calculateRange(dateFormat.format(cDate.getTime()),dateFormat.format(Calendar.getInstance().getTime()))[1]);\n\t\tAssert.assertEquals(true, 2 == partition.calculateRange(dateFormat.format(cDate.getTime()),dateFormat.format(Calendar.getInstance().getTime()))[2]);\n\n\t\tcDate = Calendar.getInstance();\n\t\tcDate.add(Calendar.DATE, -21);\n\t\tSystem.err.println(dateFormat.format(cDate.getTime()));\n\t\tAssert.assertEquals(true, 0 == partition.calculateRange(dateFormat.format(cDate.getTime()),dateFormat.format(Calendar.getInstance().getTime()))[0]);\n\t\tAssert.assertEquals(true, 1 == partition.calculateRange(dateFormat.format(cDate.getTime()),dateFormat.format(Calendar.getInstance().getTime()))[1]);\n\t\tAssert.assertEquals(true, 2 == partition.calculateRange(dateFormat.format(cDate.getTime()),dateFormat.format(Calendar.getInstance().getTime()))[2]);\n\t\tAssert.assertEquals(true, 12 == partition.calculateRange(dateFormat.format(cDate.getTime()),dateFormat.format(Calendar.getInstance().getTime()))[12]);\n\t\tAssert.assertEquals(true, 13 == partition.calculateRange(dateFormat.format(cDate.getTime()),dateFormat.format(Calendar.getInstance().getTime())).length);\n\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/function/PartitionByJumpConsistentHashTest.java",
    "content": "package io.mycat.route.function;\n\nimport junit.framework.Assert;\nimport org.junit.Test;\n\nimport io.mycat.route.function.PartitionByJumpConsistentHash;\n\n/**\n * 跳增一致性哈希分片的测试类\n *\n * @author XiaoSK\n */\npublic class PartitionByJumpConsistentHashTest {\n\n    @Test\n    public void test() {\n        int[] expect = {1,2,1,0,0,2,1,1,1,0,2,1,1,2,1,0,0,2,1,0,0,0,2,1};\n\n        PartitionByJumpConsistentHash jch = new PartitionByJumpConsistentHash();\n        jch.setTotalBuckets(3);\n        jch.init();\n\n        for(int i = 1; i <= expect.length; i++) {\n            Assert.assertEquals(true, expect[i-1] == jch.calculate(i + \"\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/function/PartitionByMonthTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.Arrays;\n\npublic class PartitionByMonthTest {\n\n\t@Test\n\tpublic void test()  {\n\t\tPartitionByMonth partition = new PartitionByMonth();\n\n\t\tpartition.setDateFormat(\"yyyy-MM-dd\");\n\t\tpartition.setsBeginDate(\"2014-01-01\");\n\n\t\tpartition.init();\n\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-01\"));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-10\"));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-31\"));\n\t\tAssert.assertEquals(true, 1 == partition.calculate(\"2014-02-01\"));\n\t\tAssert.assertEquals(true, 1 == partition.calculate(\"2014-02-28\"));\n\t\tAssert.assertEquals(true, 2 == partition.calculate(\"2014-03-1\"));\n\t\tAssert.assertEquals(true, 11 == partition.calculate(\"2014-12-31\"));\n\t\tAssert.assertEquals(true, 12 == partition.calculate(\"2015-01-31\"));\n\t\tAssert.assertEquals(true, 23 == partition.calculate(\"2015-12-31\"));\n\n\t\tpartition.setDateFormat(\"yyyy-MM-dd\");\n\t\tpartition.setsBeginDate(\"2015-01-01\");\n\t\tpartition.setsEndDate(\"2015-12-01\");\n\n\t\tpartition.init();\n\n\t\t/**\n\t\t *  0 : 2016-01-01~31, 2015-01-01~31, 2014-01-01~31\n\t\t *  1 : 2016-02-01~28, 2015-02-01~28, 2014-02-01~28\n\t\t *  5 : 2016-06-01~30, 2015-06-01~30, 2014-06-01~30\n\t\t * 11 : 2016-12-01~31, 2015-12-01~31, 2014-12-01~31\n\t\t */\n\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2013-01-02\"));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2014-01-01\"));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2015-01-10\"));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2015-01-31\"));\n\t\tAssert.assertEquals(true, 0 == partition.calculate(\"2016-01-20\"));\n\n\t\tAssert.assertEquals(true, 1 == partition.calculate(\"2013-02-02\"));\n\t\tAssert.assertEquals(true, 1 == partition.calculate(\"2014-02-01\"));\n\t\tAssert.assertEquals(true, 1 == partition.calculate(\"2015-02-10\"));\n\t\tAssert.assertEquals(true, 1 == partition.calculate(\"2015-02-28\"));\n\t\tAssert.assertEquals(true, 1 == partition.calculate(\"2016-02-20\"));\n\n\t\tAssert.assertEquals(true, 5 == partition.calculate(\"2013-06-01\"));\n\t\tAssert.assertEquals(true, 5 == partition.calculate(\"2014-06-01\"));\n\t\tAssert.assertEquals(true, 5 == partition.calculate(\"2015-06-10\"));\n\t\tAssert.assertEquals(true, 5 == partition.calculate(\"2015-06-28\"));\n\t\tAssert.assertEquals(true, 5 == partition.calculate(\"2016-06-20\"));\n\n\t\tAssert.assertEquals(true, 11 == partition.calculate(\"2013-12-28\"));\n\t\tAssert.assertEquals(true, 11 == partition.calculate(\"2014-12-01\"));\n\t\tAssert.assertEquals(true, 11 == partition.calculate(\"2014-12-31\"));\n\t\tAssert.assertEquals(true, 11 == partition.calculate(\"2015-12-11\"));\n\t\tAssert.assertEquals(true, 11 == partition.calculate(\"2016-12-31\"));\n\n\t}\n\n\t/**\n\t * 范围对比\n\t */\n\t@Test\n\tpublic void sence1CalculateRangeContrastTest(){\n\t\t// 场景1：无开始/结束时间，节点数量必须是12个，从1月~12月\n\t\tPartitionByMonth partition = new PartitionByMonth();\n\t\tpartition.setDateFormat(\"yyyy-MM-dd\");\n        partition.setsBeginDate(\"2013-01-01\");\n        partition.setsEndDate(\"2013-12-01\");\n\t\tpartition.init();\n\n\t\tPartitionByMonth scene = new PartitionByMonth();\n\t\tscene.setDateFormat(\"yyyy-MM-dd\");\n\t\tscene.init();\n\t\tAssert.assertEquals(\n\t\t\t\tArrays.toString(partition.calculateRange(\"2014-01-01\", \"2014-04-03\")),\n\t\t\t\tArrays.toString(scene.calculateRange(\"2014-01-01\", \"2014-04-03\"))\n\t\t);\n\t\tAssert.assertEquals(\n\t\t\t\tArrays.toString(partition.calculateRange(\"2013-01-01\", \"2014-04-03\")),\n\t\t\t\tArrays.toString(scene.calculateRange(\"2013-01-01\", \"2014-04-03\"))\n\t\t);\n\t\tAssert.assertEquals(\n\t\t\t\t// []\n\t\t\t\tArrays.toString(partition.calculateRange(\"2015-01-01\", \"2014-04-03\")),\n\t\t\t\t// []\n\t\t\t\tArrays.toString(scene.calculateRange(\"2015-01-01\", \"2014-04-03\"))\n\t\t);\n\t}\n\t@Test\n\tpublic void sence1(){\n\t\tPartitionByMonth scene = new PartitionByMonth();\n\t\tscene.setDateFormat(\"yyyy-MM-dd\");\n\t\tscene.init();\n\n\t\tAssert.assertEquals(true, 0 == scene.calculate(\"2014-01-01\"));\n\t\tAssert.assertEquals(true, 0 == scene.calculate(\"2014-01-10\"));\n\t\tAssert.assertEquals(true, 0 == scene.calculate(\"2014-01-31\"));\n\t\tAssert.assertEquals(true, 1 == scene.calculate(\"2014-02-01\"));\n\t\tAssert.assertEquals(true, 1 == scene.calculate(\"2014-02-28\"));\n\t\tAssert.assertEquals(true, 2 == scene.calculate(\"2014-03-1\"));\n\t\tAssert.assertEquals(true, 11 == scene.calculate(\"2014-12-31\"));\n\t\tAssert.assertEquals(true, 0 == scene.calculate(\"2015-01-31\"));\n\t\tAssert.assertEquals(true, 11 == scene.calculate(\"2015-12-31\"));\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/function/PartitionByPatternTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport io.mycat.route.function.PartitionByPattern;\n\npublic class PartitionByPatternTest {\n\n\t@Test\n\tpublic void test() {\n\t\tPartitionByPattern autoPartition = new PartitionByPattern();\n\t\tautoPartition.setPatternValue(256);\n\t\tautoPartition.setDefaultNode(2);\n\t\tautoPartition.setMapFile(\"partition-pattern.txt\");\n\t\tautoPartition.init();\n\t\tString idVal = \"0\";\n\t\tAssert.assertEquals(true, 7 == autoPartition.calculate(idVal));\n\t\tidVal = \"45a\";\n\t\tAssert.assertEquals(true, 2 == autoPartition.calculate(idVal));\n\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/function/PartitionByPrefixPatternTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\nimport junit.framework.Assert;\n\nimport org.junit.Test;\n\npublic class PartitionByPrefixPatternTest {\n\n\t@Test\n\tpublic void test()  {\n\t\t/**\n\t\t * ASCII编码：\n\t\t * 48-57=0-9阿拉伯数字\n\t\t * 64、65-90=@、A-Z \n\t\t * 97-122=a-z\n\t\t * \n\t\t */\n\t\tPartitionByPrefixPattern autoPartition=new PartitionByPrefixPattern();\n\t\tautoPartition.setPatternValue(32);\n\t\tautoPartition.setPrefixLength(5);\n\t\tautoPartition.setMapFile(\"partition_prefix_pattern.txt\");\n\t\tautoPartition.init();\n\t\t\n\t\tString idVal=\"gf89f9a\";\n\t\tAssert.assertEquals(true, 0==autoPartition.calculate(idVal));\n\t\t\n\t\tidVal=\"8df99a\";\n\t\tAssert.assertEquals(true, 4==autoPartition.calculate(idVal)); \n\t\t\n\t\tidVal=\"8dhdf99a\";\n\t\tAssert.assertEquals(true, 3==autoPartition.calculate(idVal)); \n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/function/PartitionByRangeDateHashTest.java",
    "content": "package io.mycat.route.function;\n\nimport com.google.common.hash.Hashing;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteStrategy;\nimport io.mycat.route.factory.RouteStrategyFactory;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.sql.SQLNonTransientException;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class PartitionByRangeDateHashTest\n{\n\n    @Test\n    public void test() throws ParseException {\n        PartitionByRangeDateHash partition = new PartitionByRangeDateHash();\n\n        partition.setDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        partition.setsBeginDate(\"2014-01-01 00:00:00\");\n        partition.setsPartionDay(\"3\");\n        partition.setGroupPartionSize(\"6\");\n\n        partition.init();\n\n        Integer calculate = partition.calculate(\"2014-01-01 00:00:00\");\n        Assert.assertEquals(true, 3 == calculate);\n\n         calculate = partition.calculate(\"2014-01-01 00:00:01\");\n        Assert.assertEquals(true, 1 == calculate);\n\n        calculate = partition.calculate(\"2014-01-04 00:00:00\");\n        Assert.assertEquals(true, 7 == calculate);\n\n        calculate = partition.calculate(\"2014-01-04 00:00:01\");\n        Assert.assertEquals(true, 11== calculate);\n\n\n        Date beginDate = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").parse(\"2014-01-01 00:00:00\");\n        Calendar cal = Calendar.getInstance();\n        cal.setTime(beginDate);\n\n\n        for (int i = 0; i < 60*60*24*3-1; i++)\n        {\n              cal.add(Calendar.SECOND, 1);\n        int v=    partition.calculate(new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(cal.getTime()))     ;\n            Assert.assertTrue(v<6);\n        }\n\n\n    }\n\n\n\n    protected Map<String, SchemaConfig> schemaMap;\n    protected LayerCachePool cachePool = new SimpleCachePool();\n    protected RouteStrategy routeStrategy;\n\n    public PartitionByRangeDateHashTest() {\n        String schemaFile = \"/route/schema.xml\";\n        String ruleFile = \"/route/rule.xml\";\n        SchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n        schemaMap = schemaLoader.getSchemas();\n        MycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n    }\n\n    @Test\n    public void testRange() throws SQLNonTransientException {\n        String sql = \"select * from offer1  where col_date between '2014-01-01 00:00:00'  and '2014-01-03 23:59:59'     order by id desc limit 100\";\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        junit.framework.Assert.assertEquals(6, rrs.getNodes().length);\n\n        sql = \"select * from offer1  where col_date between '2014-01-01 00:00:00'  and '2014-01-04 00:00:59'      order by id desc limit 100\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        junit.framework.Assert.assertEquals(12, rrs.getNodes().length);\n\n        sql = \"select * from offer1  where col_date between '2014-01-04 00:00:00'  and '2014-01-06 23:59:59'      order by id desc limit 100\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        junit.framework.Assert.assertEquals(6, rrs.getNodes().length);\n\n\n    }\n\n     public static int hash(long str,int size)\n     {\n     return     Hashing.consistentHash(str,size)      ;\n     }\n\n    public static void main(String[] args) throws ParseException\n    {\n\n        Map map=new HashMap<>()  ;\n        Date beginDate = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").parse(\"2014-01-04 00:00:01\");\n        for (int i = 0; i < 60*60*24*10; i++)\n        {\n\n\n            Calendar cal = Calendar.getInstance();\n            cal.setTime(beginDate);\n           cal.add(Calendar.SECOND, 1);\n            beginDate = cal.getTime();\n            int hash = hash(beginDate.getTime(), 3);\n            if(map.containsKey(hash))\n            {\n            map.put(hash,    (int)map.get(hash)+1);\n            } else\n            {\n                map.put(hash,1);\n            }\n          //  System.out.println(hash);\n        }\n\n\n        System.out.println(map.values());\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/function/PartitionByRangeModTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\nimport junit.framework.Assert;\nimport org.junit.Test;\n\nimport io.mycat.MycatServer;\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.RouteStrategy;\nimport io.mycat.route.factory.RouteStrategyFactory;\n\nimport java.math.BigInteger;\nimport java.sql.SQLNonTransientException;\nimport java.util.Map;\n\npublic class PartitionByRangeModTest\n{\n\n    @Test\n    public void test()  {\n        PartitionByRangeMod autoPartition = new PartitionByRangeMod();\n        autoPartition.setMapFile(\"partition-range-mod.txt\");\n        autoPartition.init();\n        String idVal = \"0\";\n        Assert.assertEquals(true, 0 == autoPartition.calculate(idVal));\n        idVal = \"1\";\n        Assert.assertEquals(true, 1 == autoPartition.calculate(idVal));\n        idVal = \"2\";\n        Assert.assertEquals(true, 2 == autoPartition.calculate(idVal));\n        idVal = \"3\";\n        Assert.assertEquals(true, 3 == autoPartition.calculate(idVal));\n        idVal = \"4\";\n        Assert.assertEquals(true, 4 == autoPartition.calculate(idVal));\n        idVal = \"5\";\n        Assert.assertEquals(true, 0 == autoPartition.calculate(idVal));\n\n        idVal=\"2000000\";\n\t\tAssert.assertEquals(true, 0==autoPartition.calculate(idVal));\n\n\t\tidVal=\"2000001\";\n\t\tAssert.assertEquals(true, 5==autoPartition.calculate(idVal));\n\n\t\tidVal=\"4000000\";\n\t\tAssert.assertEquals(true, 5==autoPartition.calculate(idVal));\n\n\t\tidVal=\"4000001\";\n\t\tAssert.assertEquals(true, 7==autoPartition.calculate(idVal));\n    }\n\n\n    private static int mod(long v, int size)\n    {\n        BigInteger bigNum = BigInteger.valueOf(v).abs();\n        return (bigNum.mod(BigInteger.valueOf(size))).intValue();\n    }\n\n    protected Map<String, SchemaConfig> schemaMap;\n    protected LayerCachePool cachePool = new SimpleCachePool();\n    protected RouteStrategy routeStrategy;\n\n    public PartitionByRangeModTest() {\n        String schemaFile = \"/route/schema.xml\";\n        String ruleFile = \"/route/rule.xml\";\n        SchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n        schemaMap = schemaLoader.getSchemas();\n        MycatServer.getInstance().getConfig().getSchemas().putAll(schemaMap);\n        RouteStrategyFactory.init();\n        routeStrategy = RouteStrategyFactory.getRouteStrategy(\"druidparser\");\n    }\n\n    @Test\n    public void testRange() throws SQLNonTransientException {\n        String sql = \"select * from offer  where id between 2000000  and 4000001     order by id desc limit 100\";\n        SchemaConfig schema = schemaMap.get(\"TESTDB\");\n        RouteResultset rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(10, rrs.getNodes().length);\n\n        sql = \"select * from offer  where id between 9  and 2000     order by id desc limit 100\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(5, rrs.getNodes().length);\n\n        sql = \"select * from offer  where id between 4000001  and 6005001     order by id desc limit 100\";\n        rrs = routeStrategy.route(new SystemConfig(), schema, -1, sql, null,\n                null, cachePool);\n        Assert.assertEquals(8, rrs.getNodes().length);\n\n\n    }\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/function/PartitionByStringTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport io.mycat.route.function.PartitionByString;\n\npublic class PartitionByStringTest {\n\n\t@Test\n\tpublic void test() {\n\t\tPartitionByString rule = new PartitionByString();\n\t\tString idVal=null;\n\t\trule.setPartitionLength(\"512\");\n\t\trule.setPartitionCount(\"2\");\n\t\trule.init();\n\t\trule.setHashSlice(\"0:2\");\n//\t\tidVal = \"0\";\n//\t\tAssert.assertEquals(true, 0 == rule.calculate(idVal));\n//\t\tidVal = \"45a\";\n//\t\tAssert.assertEquals(true, 1 == rule.calculate(idVal));\n\n\t\t\n\t\t\n\t\t//last 4\n\t\trule = new PartitionByString();\n\t\trule.setPartitionLength(\"512\");\n\t\trule.setPartitionCount(\"2\");\n\t\trule.init();\n\t\t//last 4 characters\n\t\trule.setHashSlice(\"-4:0\");\n\t\tidVal = \"aaaabbb0000\";\n\t\tAssert.assertEquals(true, 0 == rule.calculate(idVal));\n\t\tidVal = \"aaaabbb2359\";\n\t\tAssert.assertEquals(true, 0 == rule.calculate(idVal));\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/function/RuleFunctionSuitTableTest.java",
    "content": "package io.mycat.route.function;\n\nimport java.util.Arrays;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport io.mycat.config.model.TableConfig;\nimport io.mycat.config.model.rule.RuleConfig;\nimport io.mycat.util.SplitUtil;\n\n/**\n * 测试分片算法定义是否符合分片表的定义, 主要测试分区数是否符合分片表分片数\n * \n * @author CrazyPig\n *\n */\npublic class RuleFunctionSuitTableTest {\n\t\n\t@Test\n\tpublic void testAutoPartitionByLong() {\n\t\tAutoPartitionByLong autoPartition=new AutoPartitionByLong();\n\t\tautoPartition.setMapFile(\"autopartition-long.txt\");\n\t\tautoPartition.init(); // partition = 3\n\t\tAssert.assertEquals(3, autoPartition.getPartitionNum());\n\t\tRuleConfig rule = new RuleConfig(\"id\", \"auto-partition-long\");\n\t\trule.setRuleAlgorithm(autoPartition);\n\t\tTableConfig tableConf = new TableConfig(\"test\", \"id\", true, false, -1, \"dn1,dn2\",\n\t\t\t\tnull, rule, true, null, false, null, null, null, false);\n\t\tint suit1 = autoPartition.suitableFor(tableConf);\n\t\tAssert.assertEquals(-1, suit1);\n\t\t\n\t\ttableConf.getDataNodes().clear();\n\t\ttableConf.getDataNodes().addAll(Arrays.asList(\"dn1\", \"dn2\", \"dn3\"));\n\t\t\n\t\tint suit2 = autoPartition.suitableFor(tableConf);\n\t\tAssert.assertEquals(0, suit2);\n\t\t\n\t\ttableConf.getDataNodes().clear();\n\t\ttableConf.getDataNodes().addAll(Arrays.asList(\"dn1\", \"dn2\", \"dn3\", \"dn4\"));\n\t\tint suit3 = autoPartition.suitableFor(tableConf);\n\t\tAssert.assertEquals(1, suit3);\n\t\t\n\t\t/* \n\t\t * autopartition-long-dupl.txt\n\t\t * 0-1000=0\n\t\t * 1001-2000=1\n\t\t * 2001-3000=0\n\t\t * 3001-4000=1\n\t\t*/\n\t\tAutoPartitionByLong autoPartition2 = new AutoPartitionByLong();\n\t\tautoPartition2.setMapFile(\"autopartition-long-dupl.txt\");\n\t\tautoPartition2.init();\n\t\tAssert.assertEquals(2, autoPartition2.getPartitionNum());\n\t\tRuleConfig rule2 = new RuleConfig(\"id\", \"auto-partition-long-dupl\");\n\t\trule2.setRuleAlgorithm(autoPartition2);\n\t\tTableConfig tableConf2 = new TableConfig(\"test2\", \"id\", true, false, -1, \"dn1,dn2\",\n\t\t\t\tnull, rule, true, null, false, null, null, null,false);\n\t\tAssert.assertEquals(0, autoPartition2.suitableFor(tableConf2));\n\t\t\n\t\tAssert.assertEquals(0, autoPartition2.calculate(\"500\").intValue());\n\t\tAssert.assertEquals(1, autoPartition2.calculate(\"1500\").intValue());\n\t\tAssert.assertEquals(1, autoPartition2.calculate(\"2000\").intValue());\n\t\tAssert.assertEquals(0, autoPartition2.calculate(\"3000\").intValue());\n\t\tAssert.assertEquals(1, autoPartition2.calculate(\"3001\").intValue());\n\t}\n\t\n\t@Test\n\tpublic void testPartitionByDate() {\n\t\t\n\t\tPartitionByDate partition = new PartitionByDate();\n\t\tpartition.setDateFormat(\"yyyy-MM-dd\");\n\t\tpartition.setsBeginDate(\"2014-01-01\");\n\t\tpartition.setsEndDate(\"2014-01-31\");\n\t\tpartition.setsPartionDay(\"10\");\n\t\tpartition.init(); // partition = 4\n\t\tAssert.assertEquals(4, partition.getPartitionNum());\n\t\t\n\t\tRuleConfig rule = new RuleConfig(\"col_date\", \"partition-date\");\n\t\trule.setRuleAlgorithm(partition);\n\t\tTableConfig tableConf = new TableConfig(\"test\", \"id\", true, false, -1, \"dn1,dn2,dn3\",\n\t\t\t\tnull, rule, true, null, false, null, null, null, false);\n\t\tint suit1 = partition.suitableFor(tableConf);\n\t\t\n\t\tAssert.assertEquals(-1, suit1);\n\t\t\n\t\ttableConf.getDataNodes().clear();\n\t\ttableConf.getDataNodes().addAll(Arrays.asList(\"dn1\", \"dn2\", \"dn3\", \"dn4\"));\n\t\tint suit2 = partition.suitableFor(tableConf);\n\t\tAssert.assertEquals(0, suit2);\n\t\t\n\t\ttableConf.getDataNodes().clear();\n\t\ttableConf.getDataNodes().addAll(Arrays.asList(\"dn1\", \"dn2\", \"dn3\", \"dn4\", \"dn5\"));\n\t\tint suit3 = partition.suitableFor(tableConf);\n\t\tAssert.assertEquals(1, suit3);\n\t\t\n\t\tPartitionByDate partition1 = new PartitionByDate();\n\t\tpartition.setDateFormat(\"yyyy-MM-dd\");\n\t\tpartition.setsBeginDate(\"2014-01-01\");\n\t\tpartition.setsPartionDay(\"10\");\n\t\tpartition.init(); // partition no limit\n\t\t\n\t\tint suit4 = partition1.suitableFor(tableConf);\n\t\tAssert.assertEquals(0, suit4);\n\t\t\n\t}\n\t\n\t@Test\n\tpublic void testPartitionByHashMod() {\n\t\t\n\t\tPartitionByHashMod partition = new PartitionByHashMod();\n\t\tpartition.setCount(3); // partition = 3;\n\t\tAssert.assertEquals(3, partition.getPartitionNum());\n\t\t\n\t\tRuleConfig rule = new RuleConfig(\"id\", \"partition-hash-mod\");\n\t\trule.setRuleAlgorithm(partition);\n\t\tTableConfig tableConf = new TableConfig(\"test\", \"id\", true, false, -1, \"dn1,dn2,dn3\",\n\t\t\t\tnull, rule, true, null, false, null, null, null, false);\n\t\tint suit1 = partition.suitableFor(tableConf);\n\t\tAssert.assertEquals(0, suit1);\n\t\t\n\t\ttableConf.getDataNodes().clear();\n\t\ttableConf.getDataNodes().addAll(Arrays.asList(\"dn1\", \"dn2\", \"dn3\", \"dn4\"));\n\t\tint suit2 = partition.suitableFor(tableConf);\n\t\tAssert.assertEquals(1, suit2);\n\t\t\n\t\ttableConf.getDataNodes().clear();\n\t\ttableConf.getDataNodes().addAll(Arrays.asList(\"dn1\", \"dn2\"));\n\t\tint suit3 = partition.suitableFor(tableConf);\n\t\tAssert.assertEquals(-1, suit3);\n\t}\n\t\n\t@Test\n\tpublic void testPartitionByRangeMod() {\n\t\tPartitionByRangeMod partition = new PartitionByRangeMod();\n\t\tpartition.setMapFile(\"partition-range-mod.txt\");\n\t\tpartition.init();\n\t\t\n\t\tAssert.assertEquals(20, partition.getPartitionNum()); // partition = 20\n\t\tRuleConfig rule = new RuleConfig(\"id\", \"partition-range-mod\");\n\t\trule.setRuleAlgorithm(partition);\n\t\tTableConfig tableConf = new TableConfig(\"test\", \"id\", true, false, -1, \"dn$1-10\",\n\t\t\t\tnull, rule, true, null, false, null, null, null, false);\n\t\tint suit1 = partition.suitableFor(tableConf);\n\t\tAssert.assertEquals(-1, suit1);\n\t\t\n\t\ttableConf.getDataNodes().clear();\n\t\tString[] dataNodes = SplitUtil.split(\"dn$1-20\", ',', '$', '-');\n\t\ttableConf.getDataNodes().addAll(Arrays.asList(dataNodes));\n\t\tint suit2 = partition.suitableFor(tableConf);\n\t\tAssert.assertEquals(0, suit2);\n\t\t\n\t\ttableConf.getDataNodes().clear();\n\t\tdataNodes = SplitUtil.split(\"dn$1-30\", ',', '$', '-');\n\t\ttableConf.getDataNodes().addAll(Arrays.asList(dataNodes));\n\t\tint suit3 = partition.suitableFor(tableConf);\n\t\tAssert.assertEquals(1, suit3);\n\t\t\n\t}\n\t\n\t@Test\n\tpublic void testPartitionByPattern() {\n\t\tPartitionByPattern partition = new PartitionByPattern();\n\t\tpartition.setMapFile(\"partition-pattern.txt\");\n\t\tpartition.init();\n\t\t\n\t\t/*\n\t\t * partition-pattern.txt\n\t\t * 1-32=0\n         * 33-64=1\n         * 65-96=2\n         * 97-128=3\n         * 129-160=4\n         * 161-192=5\n         * 193-224=6\n         * 225-256=7\n         * 0-0=7\n\t\t */\n\t\t\n\t\tAssert.assertEquals(8, partition.getPartitionNum());\n\t\t\n\t}\n\t\n\t@Test\n\tpublic void testPartitionByPrefixPattern() {\n\t\tPartitionByPrefixPattern partition = new PartitionByPrefixPattern();\n\t\tpartition.setMapFile(\"partition_prefix_pattern.txt\");\n\t\tpartition.init();\n\t\t\n\t\t\n\t\t/*\n\t\t * partition_prefix_pattern.txt\n\t\t * 1-4=0\n         * 5-8=1\n         * 9-12=2\n         * 13-16=3\n         * 17-20=4\n\t     * 21-24=5\n         * 25-28=6\n         * 29-32=7\n         * 0-0=7\n\t\t */\n\t\tAssert.assertEquals(8, partition.getPartitionNum());\n\t\t\n\t\tRuleConfig rule = new RuleConfig(\"id\", \"partition-prefix-pattern\");\n\t\trule.setRuleAlgorithm(partition);\n\t\tTableConfig tableConf = new TableConfig(\"test\", \"id\", true, false, -1, \"dn1,dn2\",\n\t\t\t\tnull, rule, true, null, false, null, null, null, false);\n\t\tint suit1 = partition.suitableFor(tableConf);\n\t\tAssert.assertEquals(-1, suit1);\n\t\t\n\t\ttableConf.getDataNodes().clear();\n\t\tString[] dataNodes = SplitUtil.split(\"dn$1-8\", ',', '$', '-');\n\t\ttableConf.getDataNodes().addAll(Arrays.asList(dataNodes));\n\t\tint suit2 = partition.suitableFor(tableConf);\n\t\tAssert.assertEquals(0, suit2);\n\t\t\n\t\ttableConf.getDataNodes().clear();\n\t\tdataNodes = SplitUtil.split(\"dn$1-10\", ',', '$', '-');\n\t\ttableConf.getDataNodes().addAll(Arrays.asList(dataNodes));\n\t\tint suit3 = partition.suitableFor(tableConf);\n\t\tAssert.assertEquals(1, suit3);\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/function/TestLatestMonthPartion.java",
    "content": "package io.mycat.route.function;\n\nimport static org.junit.Assert.assertTrue;\n\nimport org.junit.Test;\n\npublic class TestLatestMonthPartion {\n\n\t@Test\n\tpublic void testSetDataNodes()  {\n\t\tLatestMonthPartion partion = new LatestMonthPartion();\n\t\tpartion.setSplitOneDay(24);\n\t\tInteger val = partion.calculate(\"2015020100\");\n\t\tassertTrue(val == 0);\n\t\tval = partion.calculate(\"2015020216\");\n\t\tassertTrue(val == 40);\n\t\tval = partion.calculate(\"2015022823\");\n\t\tassertTrue(val == 27 * 24 + 23);\n\n\t\tInteger[] span = partion.calculateRange(\"2015020100\", \"2015022823\");\n\t\tassertTrue(span.length == 27 * 24 + 23 + 1);\n\t\tassertTrue(span[0] == 0 && span[span.length - 1] == 27 * 24 + 23);\n\t\t\n\t\t\n\t\tspan = partion.calculateRange(\"2015020100\", \"2015020123\");\n\t\tassertTrue(span.length == 24);\n\t\tassertTrue(span[0] == 0 && span[span.length - 1] == 23);\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/function/TestNumberParseUtil.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.function;\n\nimport org.junit.Test;\n\nimport io.mycat.route.function.NumberParseUtil;\nimport junit.framework.Assert;\n\npublic class TestNumberParseUtil {\n\n\t@Test\n\tpublic void test() {\n\t\tString val = \"2000\";\n\t\tAssert.assertEquals(2000, NumberParseUtil.parseLong(val));\n\t\tval = \"2M\";\n\t\tAssert.assertEquals(20000, NumberParseUtil.parseLong(val));\n\t\tval = \"2M1\";\n\t\tAssert.assertEquals(20001, NumberParseUtil.parseLong(val));\n\t\tval = \"1000M\";\n\t\tAssert.assertEquals(10000000, NumberParseUtil.parseLong(val));\n\t\tval = \"30K\";\n\t\tAssert.assertEquals(30000, NumberParseUtil.parseLong(val));\n\t\tval = \"30K1\";\n\t\tAssert.assertEquals(30001, NumberParseUtil.parseLong(val));\n\t\tval = \"30K09\";\n\t\tAssert.assertEquals(30009, NumberParseUtil.parseLong(val));\n\t}\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/parser/druid/impl/DefaultDruidParserTest.java",
    "content": "package io.mycat.route.parser.druid.impl;\n\nimport static org.junit.Assert.assertArrayEquals;\nimport static org.mockito.Mockito.mock;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\nimport com.alibaba.druid.sql.parser.SQLStatementParser;\n\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.route.RouteResultset;\nimport io.mycat.route.parser.druid.DruidParser;\nimport io.mycat.route.parser.druid.DruidShardingParseInfo;\nimport io.mycat.route.parser.druid.MycatSchemaStatVisitor;\nimport io.mycat.server.parser.ServerParse;\n\n/**\n * sql解析单元测试\n * @author lian\n * @date 2016年12月2日\n */\npublic class DefaultDruidParserTest {\n\t\n\tprivate SchemaConfig schema;\n\tprivate DruidParser druidParser;\n\t@Before\n\tpublic void setUp(){\n\t\t\n\t\tschema = mock(SchemaConfig.class);\n\t\tdruidParser = new DefaultDruidParser();\n\t}\n\t@Test\n\tpublic void testParser() throws Exception {\n\t\t\n\t\tassertArrayEquals(getParseTables(\"select id as id from company t;\"), \n\t\t\t\tgetArr(\"company\".toUpperCase()));\n\t\tassertArrayEquals(getParseTables(\"select 1 from (select 1 from company) company;\"), \n\t\t\t\tgetArr(\"company\".toUpperCase()));\n\t\tassertArrayEquals(getParseTables(\"select 1 from company,customer where company.id = customer.cid\"), \n\t\t\t\tgetArr(\"company\".toUpperCase(),\"customer\".toUpperCase()));\n\t\tassertArrayEquals(getParseTables(\"select 1 from db1.company,db1.customer where company.id = customer.cid\"), \n\t\t\t\tgetArr(\"db1.company\".toUpperCase(), \"db1.customer\".toUpperCase()));\n\t\tassertArrayEquals(getParseTables(\"select 1 from mysql.company,db1.customer where company.id = customer.cid\"), \n\t\t\t\tgetArr(\"db1.customer\".toUpperCase()));\n\t}\n\t\n\tprivate Object[] getParseTables(String sql) throws Exception{\n\t\t\n\t\tSQLStatementParser parser = new MySqlStatementParser(sql);\n\t\tSQLStatement statement = parser.parseStatement();\n        MycatSchemaStatVisitor visitor = new MycatSchemaStatVisitor();\n        \n        \n        LayerCachePool cachePool = mock(LayerCachePool.class);\n        RouteResultset rrs = new RouteResultset(sql, ServerParse.SELECT);\n\t\t\n\t\tdruidParser.parser(schema, rrs, statement, sql, cachePool, visitor);\n\t\t\n\t\tDruidShardingParseInfo ctx = druidParser.getCtx();\n\t\treturn ctx.getTables().toArray();\n\t}\n\t\n\tprivate Object[] getArr(String...strings){\n\t\treturn strings;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/route/perf/NoShardingSpace.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.perf;\n\nimport java.sql.SQLNonTransientException;\n\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\n\n/**\n * @author mycat\n */\npublic class NoShardingSpace {\n    private SchemaConfig schema;\n    private static int total=1000000;\n    protected LayerCachePool cachePool = new SimpleCachePool();\n    public NoShardingSpace() {\n    \tString schemaFile = \"/route/schema.xml\";\n\t\tString ruleFile = \"/route/rule.xml\";\n\t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n\t\tschema = schemaLoader.getSchemas().get(\"dubbo\");\n    }\n\n    public void testDefaultSpace() throws SQLNonTransientException {\n        SchemaConfig schema = this.schema;\n        String stmt = \"insert into offer (member_id, gmt_create) values ('1','2001-09-13 20:20:33')\";\n        for (int i = 0; i < total; i++) {\n            RouteStrategyFactory.getRouteStrategy().route(new SystemConfig(),schema, -1,stmt, null, null,cachePool);\n        }\n    }\n\n    public static void main(String[] args) throws SQLNonTransientException {\n        NoShardingSpace test = new NoShardingSpace();\n        System.currentTimeMillis();\n\n        long start = System.currentTimeMillis();\n        test.testDefaultSpace();\n        long end = System.currentTimeMillis();\n        System.out.println(\"take \" + (end - start) + \" ms. avg \"+(end-start+0.0)/total);\n    }\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/perf/ShardingDefaultSpace.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.perf;\n\nimport java.sql.SQLNonTransientException;\n\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\n\n/**\n * @author mycat\n */\npublic class ShardingDefaultSpace {\n    private SchemaConfig schema;\n    private static int total=1000000;\n    protected LayerCachePool cachePool = new SimpleCachePool();\n    public ShardingDefaultSpace() throws InterruptedException {\n         String schemaFile = \"/route/schema.xml\";\n \t\tString ruleFile = \"/route/rule.xml\";\n \t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n \t\tschema = schemaLoader.getSchemas().get(\"cndb\");\n    }\n\n    /**\n     * 路由到defaultSpace的性能测试\n     */\n    public void testDefaultSpace() throws SQLNonTransientException {\n        SchemaConfig schema = this.getSchema();\n        String sql = \"insert into offer (member_id, gmt_create) values ('1','2001-09-13 20:20:33')\";\n        for (int i = 0; i < total; i++) {\n            RouteStrategyFactory.getRouteStrategy().route(new SystemConfig(),schema,-1, sql, null, null,cachePool);\n        }\n    }\n\n    protected SchemaConfig getSchema() {\n        return schema;\n    }\n\n    public static void main(String[] args) throws Exception {\n        ShardingDefaultSpace test = new ShardingDefaultSpace();\n        System.currentTimeMillis();\n\n        long start = System.currentTimeMillis();\n        test.testDefaultSpace();\n        long end = System.currentTimeMillis();\n        System.out.println(\"take \" + (end - start) + \" ms. avg \"+(end-start+0.0)/total);\n    }\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/perf/ShardingMultiTableSpace.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.perf;\n\nimport java.sql.SQLNonTransientException;\n\nimport io.mycat.SimpleCachePool;\nimport io.mycat.cache.LayerCachePool;\nimport io.mycat.config.loader.SchemaLoader;\nimport io.mycat.config.loader.xml.XMLSchemaLoader;\nimport io.mycat.config.model.SchemaConfig;\nimport io.mycat.config.model.SystemConfig;\nimport io.mycat.route.factory.RouteStrategyFactory;\n\n/**\n * @author mycat\n */\npublic class ShardingMultiTableSpace {\n    private SchemaConfig schema;\n    private static int total=1000000;\n    protected LayerCachePool cachePool = new SimpleCachePool();\n    public ShardingMultiTableSpace() throws InterruptedException {\n         String schemaFile = \"/route/schema.xml\";\n \t\tString ruleFile = \"/route/rule.xml\";\n \t\tSchemaLoader schemaLoader = new XMLSchemaLoader(schemaFile, ruleFile);\n \t\tschema = schemaLoader.getSchemas().get(\"cndb\");\n    }\n\n    /**\n     * 路由到tableSpace的性能测试\n     * \n     * @throws SQLNonTransientException\n     */\n    public void testTableSpace() throws SQLNonTransientException {\n        SchemaConfig schema = getSchema();\n        String sql = \"select id,member_id,gmt_create from offer where member_id in ('1','22','333','1124','4525')\";\n        for (int i = 0; i < total; i++) {\n            RouteStrategyFactory.getRouteStrategy().route(new SystemConfig(),schema, -1,sql, null, null,cachePool);\n        }\n    }\n\n    protected SchemaConfig getSchema() {\n        return schema;\n    }\n\n    public static void main(String[] args) throws Exception {\n        ShardingMultiTableSpace test = new ShardingMultiTableSpace();\n        System.currentTimeMillis();\n\n        long start = System.currentTimeMillis();\n        test.testTableSpace();\n        long end = System.currentTimeMillis();\n        System.out.println(\"take \" + (end - start) + \" ms. avg \"+(end-start+0.0)/total);\n    }\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/util/PartitionForSingle.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.util;\n\n/**\n * 数据分区工具单独版本，请使用singleton的模式调用。\n * \n * @author mycat\n */\npublic final class PartitionForSingle {\n\n    // 分区长度:数据段分布定义，其中取模的数一定要是2^n， 因为这里使用x % 2^n == x & (2^n - 1)等式，来优化性能。\n    private static final int PARTITION_LENGTH = 1024;\n\n    private static final int DEFAULT_HASH_LENGTH = 8;\n\n    // %转换为&操作的换算数值\n    private static final long AND_VALUE = PARTITION_LENGTH - 1;\n\n    // 分区线段\n    private final int[] segment = new int[PARTITION_LENGTH];\n\n    /**\n     * @param count\n     *            表示定义的分区数\n     * @param length\n     *            表示对应每个分区的取值长度\n     *            <p>\n     *            注意：其中count,length两个数组的长度必须是一致的。\n     *            </p>\n     */\n    public PartitionForSingle(int[] count, int[] length) {\n        if (count == null || length == null || (count.length != length.length)) {\n            throw new RuntimeException(\"error,check your scope & scopeLength definition.\");\n        }\n        int segmentLength = 0;\n        for (int i = 0; i < count.length; i++) {\n            segmentLength += count[i];\n        }\n        int[] scopeSegment = new int[segmentLength + 1];\n\n        int index = 0;\n        for (int i = 0; i < count.length; i++) {\n            for (int j = 0; j < count[i]; j++) {\n                scopeSegment[++index] = scopeSegment[index - 1] + length[i];\n            }\n        }\n        if (scopeSegment[scopeSegment.length - 1] != PARTITION_LENGTH) {\n            throw new RuntimeException(\"error,check your partitionScope definition.\");\n        }\n\n        // 数据映射操作\n        for (int i = 1; i < scopeSegment.length; i++) {\n            for (int j = scopeSegment[i - 1]; j < scopeSegment[i]; j++) {\n                segment[j] = (i - 1);\n            }\n        }\n    }\n\n    public int partition(long h) {\n        return segment[(int) (h & AND_VALUE)];\n    }\n\n    public int partition(String key) {\n        return segment[(int) (hash(key) & AND_VALUE)];\n    }\n\n    private static long hash(String s) {\n        long h = 0;\n        int len = s.length();\n        for (int i = 0; (i < DEFAULT_HASH_LENGTH && i < len); i++) {\n            h = (h << 5) - h + s.charAt(i);\n        }\n        return h;\n    }\n\n    // for test\n    public static void main(String[] args) {\n        // 拆分为16份，每份长度均为：64。\n        // Scope scope = new Scope(new int[] { 16 }, new int[] { 64 });\n\n        // // 拆分为23份，前8份长度为：8，后15份长度为：64。\n        // Scope scope = new Scope(new int[] { 8, 15 }, new int[] { 8, 64 });\n\n        // // 拆分为128份，每份长度均为：8。\n        // Scope scope = new Scope(new int[] { 128 }, new int[] { 8 });\n\n        PartitionForSingle p = new PartitionForSingle(new int[] { 8, 15 }, new int[] { 8, 64 });\n\n        String memberId = \"xianmao.hexm\";\n\n        int value = 0;\n        long st = System.currentTimeMillis();\n        for (int i = 0; i < 10000000; i++) {\n            value = p.partition(memberId);\n        }\n        long et = System.currentTimeMillis();\n\n        System.out.println(\"value:\" + value + \",take time:\" + (et - st) + \" ms.\");\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/util/PartitionUtilTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.route.util;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport io.mycat.route.util.PartitionUtil;\n\n/**\n * @author mycat()\n */\npublic class PartitionUtilTest {\n\n    @Test\n    public void testPartition() {\n        // 本例的分区策略：希望将数据水平分成3份，前两份各占25%，第三份占50%。（故本例非均匀分区）\n        // |<---------------------1024------------------------>|\n        // |<----256--->|<----256--->|<----------512---------->|\n        // | partition0 | partition1 | partition2 |\n        // | 共2份,故count[0]=2 | 共1份，故count[1]=1 |\n        int[] count = new int[] { 2, 1 };\n        int[] length = new int[] { 256, 512 };\n        PartitionUtil pu = new PartitionUtil(count, length);\n\n        // 下面代码演示分别以offerId字段或memberId字段根据上述分区策略拆分的分配结果\n        int DEFAULT_STR_HEAD_LEN = 8; // cobar默认会配置为此值\n        long offerId = 12345;\n        String memberId = \"qiushuo\";\n\n        // 若根据offerId分配，partNo1将等于0，即按照上述分区策略，offerId为12345时将会被分配到partition0中\n        int partNo1 = pu.partition(offerId);\n\n        // 若根据memberId分配，partNo2将等于2，即按照上述分区策略，memberId为qiushuo时将会被分到partition2中\n        int partNo2 = pu.partition(memberId, 0, DEFAULT_STR_HEAD_LEN);\n\n        Assert.assertEquals(0, partNo1);\n        Assert.assertEquals(2, partNo2);\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/route/util/RouterUtilTest.java",
    "content": "package io.mycat.route.util;\n\nimport io.mycat.util.StringUtil;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author Hash Zhang\n * @version 1.0.0\n * @date 2016/7/19\n */\npublic class RouterUtilTest {\n\n\tpublic static void main(String[] args) {\n        String sql = \"insert into hotnews(title,name) values('test1',\\\"name\\\"),('(test)',\\\"(test)\\\"),('\\\\\\\"',\\\"\\\\'\\\"),(\\\")\\\",\\\"\\\\\\\"\\\\')\\\");\";\n        List<String> values = RouterUtil.handleBatchInsert(sql, sql.toUpperCase().indexOf(\"VALUES\"));\n\t\tSystem.out.println(values);\n\t}\t\n\n    @Test\n    public void testBatchInsert()  {\n        String sql = \"insert into hotnews(title,name) values('test1',\\\"name\\\"),('(test)',\\\"(test)\\\"),('\\\\\\\"',\\\"\\\\'\\\"),(\\\")\\\",\\\"\\\\\\\"\\\\')\\\");\";\n        List<String> values = RouterUtil.handleBatchInsert(sql, sql.toUpperCase().indexOf(\"VALUES\"));\n        Assert.assertTrue(values.get(0).equals(\"insert into hotnews(title,name) values('test1',\\\"name\\\")\"));\n        Assert.assertTrue(values.get(1).equals(\"insert into hotnews(title,name) values('(test)',\\\"(test)\\\")\"));\n        Assert.assertTrue(values.get(2).equals(\"insert into hotnews(title,name) values('\\\\\\\"',\\\"\\\\'\\\")\"));\n        Assert.assertTrue(values.get(3).equals(\"insert into hotnews(title,name) values(\\\")\\\",\\\"\\\\\\\"\\\\')\\\")\"));\n    }\n    \n    @Test\n    public void testParseSqlValueArrayAndSuffixStr()  {\n        String sql = \"insert into hotnews(title,name) values('test1',\\\"name\\\"),('(test)',\\\"(test)\\\"),('\\\\\\\"',\\\"\\\\'\\\"),(\\\")\\\",\\\"\\\\\\\"\\\\')\\\"),(left(upper('test'), 2),\\\"left(upper('test'), 2)\\\") on duplicate key update name = values(name)\";\n        Object[] valueArrayAndSuffixStr = RouterUtil.parseSqlValueArrayAndSuffixStr(sql, sql.toUpperCase().indexOf(\"VALUES\"));\n        Assert.assertTrue(valueArrayAndSuffixStr.length == 2);\n        List<List<String>> valueArray = (List<List<String>>) valueArrayAndSuffixStr[0];\n        String suffixStr = (String) valueArrayAndSuffixStr[1];\n        Assert.assertTrue(valueArray.size() == 5);\n        Assert.assertTrue(valueArray.get(0).equals(new ArrayList<>(Arrays.asList(\"'test1'\", \"\\\"name\\\"\"))));\n        Assert.assertTrue(valueArray.get(1).equals(new ArrayList<>(Arrays.asList(\"'(test)'\", \"\\\"(test)\\\"\"))));\n        Assert.assertTrue(valueArray.get(2).equals(new ArrayList<>(Arrays.asList(\"'\\\\\\\"'\", \"\\\"\\\\'\\\"\"))));\n        Assert.assertTrue(valueArray.get(3).equals(new ArrayList<>(Arrays.asList(\"\\\")\\\"\", \"\\\"\\\\\\\"\\\\')\\\"\"))));\n        Assert.assertTrue(valueArray.get(4).equals(new ArrayList<>(Arrays.asList(\"left(upper('test'), 2)\", \"\\\"left(upper('test'), 2)\\\"\"))));\n        Assert.assertTrue(suffixStr.equals(\"on duplicate key update name = values(name)\"));\n    }\n    \n    @Test\n    public void testRemoveSchema()  {\n        String sql = \"update test set name='abcdtestx.aa'   where id=1 and testx=123\";\n\n      String afterAql=  RouterUtil.removeSchema(sql,\"testx\");\n        Assert.assertEquals(sql,afterAql);\n        System.out.println(afterAql);\n\n    }\n    @Test\n    public void testRemoveSchemaSelect()  {\n        String sql = \"select id as 'aa' from  test where name='abcdtestx.aa'   and id=1 and testx=123\";\n\n        String afterAql=  RouterUtil.removeSchema(sql,\"testx\");\n        Assert.assertEquals(sql,afterAql);\n\n    }\n\n    @Test\n    public void testRemoveSchemaSelect2()  {\n        String sql = \"select id as 'aa' from  testx.test where name='abcd  testx.aa'   and id=1 and testx=123\";\n\n        String afterAql=  RouterUtil.removeSchema(sql,\"testx\");\n        Assert.assertNotSame(sql.indexOf(\"testx.\"),afterAql.indexOf(\"testx.\"));\n\n    }\n\n    @Test\n    public void testRemoveSchema2(){\n        String sql = \"update testx.test set name='abcd \\\\' testx.aa' where id=1\";\n        String sqltrue = \"update test set name='abcd \\\\' testx.aa' where id=1\";\n        String sqlnew = RouterUtil.removeSchema(sql, \"testx\");\n        Assert.assertEquals(\"处理错误：\", sqltrue, sqlnew);\n    }\n\n    @Test\n    public void testRemoveSchema3(){\n        String sql = \"update testx.test set testx.name='abcd testx.aa' where testx.id=1\";\n        String sqltrue = \"update test set name='abcd testx.aa' where id=1\";\n        String sqlnew = RouterUtil.removeSchema(sql, \"testx\");\n        Assert.assertEquals(\"处理错误：\", sqltrue, sqlnew);\n    }\n\n    @Test\n    public void testRemoveSchema4(){\n        String sql = \"update testx.test set testx.name='abcd testx.aa' and testx.name2='abcd testx.aa' where testx.id=1\";\n        String sqltrue = \"update test set name='abcd testx.aa' and name2='abcd testx.aa' where id=1\";\n        String sqlnew = RouterUtil.removeSchema(sql, \"testx\");\n        Assert.assertEquals(\"处理错误：\", sqltrue, sqlnew);\n    }\n    /**\n     * @modification 修改支持createTable语句中包含“IF NOT EXISTS”的情况,这里测试下\n     * @date 2016/12/8\n     * @modifiedBy Hash Zhang\n     */\n    @Test\n    public void testGetCreateTableStmtTableName(){\n        String sql1 = StringUtil.makeString(\"create table if not exists producer(\\n\",\n                \"\\tid int(11) primary key,\\n\",\n                \"\\tname varchar(32)\\n\",\n                \");\").toUpperCase();\n        String sql2 = StringUtil.makeString(\"create table good(\\n\",\n                \"\\tid int(11) primary key,\\n\",\n                \"\\tcontent varchar(32),\\n\",\n                \"\\tproducer_id int(11) key\\n\",\n                \");\").toUpperCase();\n        Assert.assertTrue(\"producer\".equalsIgnoreCase(RouterUtil.getTableName(sql1, RouterUtil.getCreateTablePos(sql1, 0))));\n        Assert.assertTrue(\"good\".equalsIgnoreCase(RouterUtil.getTableName(sql2, RouterUtil.getCreateTablePos(sql2, 0))));\n    }\n\n    /**\n     * @modification 针对修改RouterUtil的去除schema的方法支持` 进行测试\n     * @date 2016/12/29\n     * @modifiedBy Hash Zhang\n     */\n    @Test\n    public void testRemoveSchemaWithHypha(){\n        String sql1 = StringUtil.makeString(\"select `testdb`.`orders`.`id`, `testdb`.`orders`.`customer_id`, `testdb`.`orders`.`goods_id` from `testdb`.`orders` where testdb.`orders`.`id` = 1;\").toUpperCase();\n        String sql2 = StringUtil.makeString(\"select `testdb`.`orders`.`id`, testdb.`orders`.`customer_id`, `testdb`.`orders`.`goods_id` from testdb.`orders` where `testdb`.`orders`.`id` = 1;\").toUpperCase();\n        String sql3 = StringUtil.makeString(\"select testdb.`orders`.`id`, `testdb`.`orders`.`customer_id`, testdb.`orders`.`goods_id` from `testdb`.`orders` where testdb.`orders`.`id` = 1;\").toUpperCase();\n        String sql4 = StringUtil.makeString(\"select testdb.`orders`.`id`, testdb.`orders`.`customer_id`, testdb.`orders`.`goods_id` from testdb.`orders` where testdb.`orders`.`id` = 1;\").toUpperCase();\n        String result = \"SELECT `ORDERS`.`ID`, `ORDERS`.`CUSTOMER_ID`, `ORDERS`.`GOODS_ID` FROM `ORDERS` WHERE `ORDERS`.`ID` = 1;\";\n        Assert.assertTrue(result.equals(RouterUtil.removeSchema(sql1,\"testdb\")));\n        Assert.assertTrue(result.equals(RouterUtil.removeSchema(sql2,\"testdb\")));\n        Assert.assertTrue(result.equals(RouterUtil.removeSchema(sql3,\"testdb\")));\n        Assert.assertTrue(result.equals(RouterUtil.removeSchema(sql4,\"testdb\")));\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/sequence/DistributedSequenceHandlerTest.java",
    "content": "package io.mycat.sequence;\n\nimport io.mycat.config.MycatConfig;\nimport io.mycat.route.sequence.handler.DistributedSequenceHandler;\nimport junit.framework.Assert;\nimport org.apache.curator.test.TestingServer;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 基于ZK与本地配置的分布式ID生成器\n * 无悲观锁，吞吐量更高\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 00:12:05 2016/5/3\n */\npublic class DistributedSequenceHandlerTest {\n    TestingServer testingServer = null;\n    DistributedSequenceHandler distributedSequenceHandler[];\n\n    @Before\n    public void initialize() throws Exception {\n        distributedSequenceHandler = new DistributedSequenceHandler[16];\n        MycatConfig mycatConfig = new MycatConfig();\n        testingServer = new TestingServer();\n        testingServer.start();\n        for (int i = 0; i < 16; i++) {\n            distributedSequenceHandler[i] = new DistributedSequenceHandler(mycatConfig.getSystem());\n            distributedSequenceHandler[i].initializeZK(testingServer.getConnectString());\n            distributedSequenceHandler[i].nextId(\"\");\n        }\n    }\n\n    /**\n     * 测试获取的唯一InstanceId\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testUniqueInstanceID() throws Exception {\n        Set<Long> idSet = new HashSet<>();\n        for (int i = 0; i < 16; i++) {\n            idSet.add(distributedSequenceHandler[i].getInstanceId());\n        }\n        Assert.assertEquals(idSet.size(), 16);\n    }\n\n    /**\n     * 测试获取的唯一id\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testUniqueID() throws Exception {\n        final ConcurrentHashMap<Long, String> idSet = new ConcurrentHashMap<>();\n        Thread thread[] = new Thread[10];\n        long start = System.currentTimeMillis();\n        //多少线程，注意线程数不能超过最大线程数（1<<threadBits）\n        for (int i = 0; i < 10; i++) {\n            thread[i] = new Thread() {\n                @Override\n                public void run() {\n                    for (int j = 0; j < 100; j++) {\n                        for (int k = 0; k < 16; k++) {\n                            idSet.put(distributedSequenceHandler[k].nextId(\"\"), \"\");\n                        }\n                    }\n\n                }\n            };\n            thread[i].start();\n        }\n        for (int i = 0; i < 10; i++) {\n            thread[i].join();\n        }\n        long end = System.currentTimeMillis();\n        System.out.println(\"Time elapsed:\" + (double) (end - start) / 1000.0 + \"s\");\n        System.out.println(\"ID/s:\" + (((double) idSet.size()) / ((double) (end - start) / 1000.0)));\n        Assert.assertEquals(idSet.size(), 16000);\n    }\n\n    /**\n     * 测试ZK容灾\n     *\n     * @throws Exception\n     */\n    @Test\n    public void testFailOver() {\n        Set<Long> idSet = new HashSet<>();\n        try {\n            int leader = failLeader(17);\n            System.out.println(\"***断掉一个leader节点后（curator会抛对应的异常断链异常，不用在意）***：\");\n            for (int i = 0; i < 16; i++) {\n                if (i == leader) {\n                    System.out.println(\"Node [\" + i + \"] used to be leader\");\n                    continue;\n                }\n                distributedSequenceHandler[i].nextId(\"\");\n                System.out.println(\"Node [\" + i + \"]is leader:\" + distributedSequenceHandler[i].getLeaderSelector().hasLeadership() );\n                System.out.println(\" InstanceID:\" + distributedSequenceHandler[i].getInstanceId());\n                idSet.add(distributedSequenceHandler[i].getInstanceId());\n            }\n            Assert.assertEquals(idSet.size(), 15);\n            idSet = new HashSet<>();\n            int leader2 = failLeader(leader);\n            System.out.println(\"***断掉两个leader节点后（curator会抛对应的异常断链异常，不用在意）***：\");\n            for (int i = 0; i < 16; i++) {\n                if (i == leader || i == leader2) {\n                    System.out.println(\"Node [\"+i + \" used to be leader\");\n                    continue;\n                }\n                distributedSequenceHandler[i].nextId(\"\");\n                System.out.println(\"Node [\"+i + \"]is leader:\" + distributedSequenceHandler[i].getLeaderSelector().hasLeadership());\n                System.out.println(\" InstanceID:\" + distributedSequenceHandler[i].getInstanceId());\n                idSet.add(distributedSequenceHandler[i].getInstanceId());\n            }\n            Assert.assertEquals(idSet.size(), 14);\n\n            idSet = new HashSet<>();\n            MycatConfig mycatConfig = new MycatConfig();\n            distributedSequenceHandler[leader] = new DistributedSequenceHandler(mycatConfig.getSystem());\n            distributedSequenceHandler[leader].initializeZK(testingServer.getConnectString());\n            distributedSequenceHandler[leader].nextId(\"\");\n            distributedSequenceHandler[leader2] = new DistributedSequenceHandler(mycatConfig.getSystem());\n            distributedSequenceHandler[leader2].initializeZK(testingServer.getConnectString());\n            distributedSequenceHandler[leader2].nextId(\"\");\n            System.out.println(\"新加入两个节点后\");\n            for (int i = 0; i < 16; i++) {\n                System.out.println(\"Node [\"+i + \"]is leader:\" + distributedSequenceHandler[i].getLeaderSelector().hasLeadership() );\n                System.out.println(\" InstanceID:\" + distributedSequenceHandler[i].getInstanceId());\n                idSet.add(distributedSequenceHandler[i].getInstanceId());\n            }\n        } catch (Exception e) {\n\n        } finally {\n            Assert.assertEquals(idSet.size(), 16);\n        }\n\n    }\n\n    private int failLeader(int p) {\n        int leader = 0, follower = 0;\n        for (int i = 0; i < 16; i++) {\n            if (i == p) {\n                continue;\n            }\n            if (distributedSequenceHandler[i].getLeaderSelector().hasLeadership()) {\n                leader = i;\n            } else {\n                follower = i;\n            }\n            System.out.println(\"Node [\"+i + \"]is leader:\" + distributedSequenceHandler[i].getLeaderSelector().hasLeadership() );\n            System.out.println(\" InstanceID:\" + distributedSequenceHandler[i].getInstanceId());\n        }\n        try {\n            distributedSequenceHandler[leader].close();\n        } catch (IOException e) {\n        }\n\n        while (true) {\n            follower++;\n            if (follower >= 16) {\n                follower = 0;\n            }\n            if (follower == leader || follower == p) {\n                continue;\n            }\n            if (distributedSequenceHandler[follower].getLeaderSelector().hasLeadership()) {\n                break;\n            }\n        }\n        return leader;\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/sequence/IncrSequenceZKHandlerTest.java",
    "content": "package io.mycat.sequence;\n\nimport io.mycat.route.sequence.handler.IncrSequenceZKHandler;\nimport io.mycat.route.util.PropertiesUtil;\nimport junit.framework.Assert;\nimport org.apache.curator.test.TestingServer;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.Properties;\nimport java.util.concurrent.ConcurrentSkipListSet;\n\n/**\n * zookeeper 实现递增序列号\n * 默认测试模拟60个进程，每个进程内20个线程。每个线程调用50次参数为GLOBAL的nextid\n * 默认GLOBAL.MINID=1\n * 默认GLOBAL.MAXID=10\n * 表示当前线程内id用光时，每次会取GLOBAL.MINID-GLOBAL.MAXID9个ID\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 23:35 2016/5/6\n */\npublic class IncrSequenceZKHandlerTest {\n    private final static int MAX_CONNECTION = 5;\n    private final static int threadCount = 5;\n    private final static int LOOP = 5;\n    TestingServer testingServer = null;\n    IncrSequenceZKHandler incrSequenceZKHandler[];\n    ConcurrentSkipListSet<Long> results;\n\n    @Before\n    public void initialize() throws Exception {\n        testingServer = new TestingServer();\n        testingServer.start();\n        incrSequenceZKHandler = new IncrSequenceZKHandler[MAX_CONNECTION];\n        results = new ConcurrentSkipListSet();\n    }\n\n    @Test\n    public void testCorrectnessAndEfficiency() throws InterruptedException {\n        final Thread threads[] = new Thread[MAX_CONNECTION];\n        for (int i = 0; i < MAX_CONNECTION; i++) {\n            final int a = i;\n            threads[i] = new Thread() {\n                @Override\n                public void run() {\n                    incrSequenceZKHandler[a] = new IncrSequenceZKHandler();\n                    Properties props = PropertiesUtil.loadProps(\"sequence_conf.properties\");\n                    try {\n                        incrSequenceZKHandler[a].initializeZK(props, testingServer.getConnectString());\n                    } catch (Exception e) {\n                        e.printStackTrace();\n                    }\n                    Thread threads[] = new Thread[threadCount];\n                    for (int j = 0; j < threadCount; j++) {\n                        threads[j] = new Thread() {\n                            @Override\n                            public void run() {\n                                for (int k = 0; k < LOOP; k++) {\n                                    long key = incrSequenceZKHandler[a].nextId(\"GLOBAL\");\n                                    results.add(key);\n                                }\n                            }\n                        };\n                        threads[j].start();\n                    }\n                    for (int j = 0; j < threadCount; j++) {\n                        try {\n                            threads[j].join();\n                        } catch (InterruptedException e) {\n                            e.printStackTrace();\n                        }\n                    }\n                }\n            };\n\n        }\n        long start = System.currentTimeMillis();\n        for (int i = 0; i < MAX_CONNECTION; i++) {\n            threads[i].start();\n        }\n        for (int i = 0; i < MAX_CONNECTION; i++) {\n            threads[i].join();\n        }\n        long end = System.currentTimeMillis();\n        Assert.assertEquals(MAX_CONNECTION * LOOP * threadCount, results.size());\n//        Assert.assertTrue(results.pollLast().equals(MAX_CONNECTION * LOOP * threadCount + 1L));\n//        Assert.assertTrue(results.pollFirst().equals(2L));\n        System.out.println(\"Time elapsed:\" + ((double) (end - start + 1) / 1000.0) + \"s\\n TPS:\" + ((double) (MAX_CONNECTION * LOOP * threadCount) / (double) (end - start + 1) * 1000.0) + \"/s\");\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/sequence/SequenceHandlerTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.sequence;\n\nimport junit.framework.Assert;\n\nimport org.junit.Test;\n\nimport io.mycat.route.sequence.handler.IncrSequencePropHandler;\nimport io.mycat.route.sequence.handler.SequenceHandler;\n\n/**\n * 全局序列号单元测试\n * \n * @author <a href=\"http://www.micmiu.com\">Michael</a>\n * @time Create on 2013-12-30 上午12:07:51\n * @version 1.0\n */\npublic class SequenceHandlerTest {\n\n\t//@Test\n\tpublic void testPropSequence() {\n\t\tSequenceHandler hander = IncrSequencePropHandler.getInstance();\n\t\tAssert.assertEquals(hander.nextId(\"DEF\") - hander.nextId(\"DEF\"), -1);\n\t}\n\n\t/**\n\t * @param args\n\t */\n\tpublic static void main(String[] args) {\n\t\tSequenceHandler hander = IncrSequencePropHandler.getInstance();\n\t\tSystem.out.println(hander.nextId(\"DEF\"));\n\t}\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/sequence/SequenceTest.java",
    "content": "package io.mycat.sequence;\n\nimport io.mycat.MycatServer;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.UUID;\n\n/**\n * 全局序列号测试\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 00:12:05 2016/5/6\n */\npublic class SequenceTest {\n    private Set<String> sequenceSet;\n    private long startTime;\n    private long endTime;\n\n    @Before\n    public void initialize() {\n        sequenceSet = new TreeSet<>();\n        startTime = System.nanoTime();\n    }\n\n//    @Test\n//    public void testIncrement(){\n//        System.out.print(\"Increment \");\n//        for (int i = 0; i < 1000000; i++) {\n//            sequenceSet.add(i+\"\");\n//        }\n//    }\n//\n    @Test\n    public void testUUID(){\n        System.out.print(\"UUID \");\n        for (int i = 0; i < 100; i++) {\n            sequenceSet.add(UUID.randomUUID().toString());\n        }\n    }\n\n    @Test\n    public void testRandom(){\n        TreeSet<String> treeSet= new TreeSet<>();\n        System.out.println(Long.toBinaryString(Long.valueOf(System.currentTimeMillis()+\"\")).length());\n    }\n\n    @Test\n    public void testRandom2(){\n        System.out.print(\"UUID \");\n        for (int i = 0; i < 100; i++) {\n            sequenceSet.add(\"aaassscccddd\"+i);\n        }\n    }\n\n    @Test\n    public void testXAXID(){\n        String xid = MycatServer.getInstance().getXATXIDGLOBAL();\n        System.out.println(xid);\n    }\n\n\n    @After\n    public void end() {\n        endTime = System.nanoTime();\n        System.out.println(\"Time elapsed: \" + (endTime - startTime)/(1000000L) + \"ms\");\n    }\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/server/handler/ServerHandlerTest.java",
    "content": "package io.mycat.server.handler;\n\npublic class ServerHandlerTest {\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/server/interceptor/impl/GlobalTableUtilTest.java",
    "content": "package io.mycat.server.interceptor.impl;\n\nimport org.junit.Test;\n\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\n\nimport junit.framework.Assert;\n\npublic class GlobalTableUtilTest {\n\t\n\tprivate static final String originSql1 = \"CREATE TABLE retl_mark\"\n\t\t\t+ \"(\"\t\n\t\t\t+ \"\tID BIGINT AUTO_INCREMENT,\"\n\t\t\t+ \"\tCHANNEL_ID INT(11),\"\n\t\t\t+ \"\tCHANNEL_INFO varchar(128),\"\n\t\t\t+ \"\tCONSTRAINT RETL_MARK_ID PRIMARY KEY (ID)\"\n\t\t\t+ \") ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\";\n\t\n\tprivate static final String originSql2 = \"CREATE TABLE retl_mark\"\n\t\t\t+ \"(\"\t\n\t\t\t+ \"\tID BIGINT AUTO_INCREMENT,\"\n\t\t\t+ \"\tCHANNEL_ID INT(11),\"\n\t\t\t+ \"\tCHANNEL_INFO varchar(128),\"\n\t\t\t+ \" _MYCAT_OP_TIME int,\"\n\t\t\t+ \"\tCONSTRAINT RETL_MARK_ID PRIMARY KEY (ID)\"\n\t\t\t+ \") ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\";\n\t\n\t@Test\n\tpublic void addColumnIfCreate() {\n\t\tString sql = parseSql(originSql1);\n\t\tSystem.out.println(sql);\n\t\tboolean contains = sql.contains(\"_mycat_op_time \");\n\t\tAssert.assertTrue(contains);\n\t\tsql = parseSql(originSql2);\n\t\tSystem.out.println(sql);\n\t\tAssert.assertFalse(sql.contains(\"_mycat_op_time int COMMENT '全局表保存修改时间戳的字段名'\"));\n\t}\n\n\tpublic String parseSql(String sql) {\n\t\tMySqlStatementParser parser = new MySqlStatementParser(sql);\n\t\tSQLStatement statement = parser.parseStatement();\n\t\treturn GlobalTableUtil.addColumnIfCreate(sql, statement);\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/sqlexecute/BaseSQLExeTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.sqlexecute;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\n\nimport junit.framework.Assert;\n\n/**\n * base sql test cases,some data should in database first\n * \n * @author wuzhih\n * \n */\npublic class BaseSQLExeTest {\n\n\tprivate static boolean driverLoaded = false;\n\n\tpublic static Connection getCon(String url, String user, String passwd)\n\t\t\tthrows SQLException {\n\t\tConnection theCon = DriverManager.getConnection(url, user, passwd);\n\t\treturn theCon;\n\t}\n\n\tprivate static void testMultiNodeNormalSQL(Connection theCon)\n\t\t\tthrows SQLException {\n\t\ttheCon.setAutoCommit(true);\n\t\tSystem.out.println(\"testMultiNodeNormalSQL begin\");\n\t\tString[] sqls = {\n\t\t\t\t\"select * from travelrecord where id=1\",\n\t\t\t\t\"select * from travelrecord  order by fee limit 200,100\",\n\t\t\t\t\"select * from travelrecord limit 100\",\n\t\t\t\t\"select sum(fee) total_fee, days,count(id),max(fee),min(fee) from  travelrecord  group by days  order by days desc limit 99 \",\n\t\t\t\t\"update travelrecord set user_id=user_id where id =1\",\n\t\t\t\t\"delete from travelrecord where id =1 \",\n\t\t\t\t\"insert into travelrecord (id,user_id,traveldate,fee,days) values(1,'wang','2014-01-05',510.5,3)\" };\n\t\tStatement stmt = theCon.createStatement();\n\t\tfor (String sql : sqls) {\n\t\t\tstmt.execute(sql);\n\t\t}\n\t\ttheCon.setAutoCommit(false);\n\t\tfor (String sql : sqls) {\n\t\t\tstmt.execute(sql);\n\t\t}\n\t\ttheCon.commit();\n\t\tSystem.out.println(\"testMultiNodeNormalSQL end\");\n\t}\n\n\tprivate static void testMultiNodeLargeResultset(Connection theCon)\n\t\t\tthrows SQLException {\n\t\ttheCon.setAutoCommit(true);\n\t\tSystem.out.println(\"testMultiNodeLargeResultset begin\");\n\t\tString sql = \"select * from travelrecord  limit 100000\";\n\n\t\tfor (int i = 0; i < 100; i++) {\n\t\t\tStatement stmt = theCon.createStatement();\n\t\t\tResultSet rs = stmt.executeQuery(sql);\n\t\t\tint count = 0;\n\t\t\twhile (rs.next()) {\n\t\t\t\tcount++;\n\t\t\t}\n\t\t\trs.close();\n\t\t\tstmt.close();\n\t\t\tSystem.out.println(\"total result \" + count);\n\t\t}\n\n\t\tSystem.out.println(\"testMultiNodeLargeResultset end\");\n\t}\n\n\tprivate static void testSingleNodeNormalSQL(Connection theCon)\n\t\t\tthrows SQLException {\n\t\ttheCon.setAutoCommit(true);\n\t\tSystem.out.println(\"testSingleNodeNormalSQL begin\");\n\t\tString[] sqls = {\n\t\t\t\t\"select * from company limit 100\",\n\t\t\t\t\"select name,count(id),max(id),min(id) from company group by name order by name desc limit 100 \",\n\t\t\t\t\"update company set name=name where id =1\",\n\t\t\t\t\"delete from company where id =-1 \",\n\t\t\t\t\"delete from company where id =1 \",\n\t\t\t\t\"insert into company(id,name) values(1,'hp')\" };\n\t\tStatement stmt = theCon.createStatement();\n\t\tfor (String sql : sqls) {\n\t\t\tSystem.out.println(\"execute \" + sql);\n\t\t\tstmt.execute(sql);\n\t\t}\n\t\ttheCon.setAutoCommit(false);\n\t\tfor (String sql : sqls) {\n\t\t\tstmt.execute(sql);\n\t\t}\n\t\ttheCon.commit();\n\t\tSystem.out.println(\"testSingleNodeNormalSQL end\");\n\t}\n\n\tprivate static void testBadSQL(Connection theCon) throws SQLException {\n\t\tSystem.out.println(\"testBadSQL begin\");\n\t\ttheCon.setAutoCommit(true);\n\t\tString[] sqls = {\n\t\t\t\t\"select sum(fee) total_fee, days,count(id),max(fee),min(fee) from  travelrecord  group by id  order by id desc limit 99\",\n\t\t\t\t\"select a ,id,name from company limit 1\",\n\t\t\t\t\"update company set name=name where id =-1\",\n\t\t\t\t\"insert into company(id,name) values(1,'hp')\",\n\t\t\t\t\"insert into company(id,name,badname) values(1,'hp')\",\n\t\t\t\t\"insert into travelrecord (id,user_id,traveldate,fee,days) values(1,’wang’,’2014-01-05’,510.5,3)\",\n\t\t\t\t\"insert into travelrecord (id,user_id,traveldate,fee,days,badcolumn) values(1,’wang’,’2014-01-05’,510.5,3)\",\n\t\t\t\t\"select sum(fee) total_fee, days,count(id),max(fee),min(fee) from  travelrecord  group by count(id)  order by count(id) desc limit 99 \"};\n\t\tfor (String sql : sqls) {\n\t\t\ttry {\n\t\t\t\tSystem.out.println(\"execute \"+sql);\n\t\t\t\ttheCon.createStatement().executeQuery(sql);\n\t\t\t\t\n\t\t\t} catch (Exception e) {\n\t\t\t\t// e.printStackTrace();\n\t\t\t\tAssert.assertEquals(true, e != null);\n\t\t\t}\n\t\t}\n\n\t\tSystem.out.println(\"testBadSQL passed\");\n\t}\n\n\tprivate static void testTransaction(Connection theCon) throws SQLException {\n\t\tSystem.out.println(\"testTransaction begin\");\n\t\ttheCon.setAutoCommit(false);\n\t\tString sql = \"select id,name from company limit 1\";\n\t\tString oldName = null;\n\t\tString upSQL = null;\n\t\tResultSet rs = theCon.createStatement().executeQuery(sql);\n\t\tif (rs.next()) {\n\t\t\tlong id = rs.getLong(1);\n\t\t\toldName = rs.getString(2);\n\t\t\tupSQL = \"update company set name='updatedname' where id=\" + id;\n\t\t\t// System.out.println(sql);\n\t\t}\n\t\tint count = theCon.createStatement().executeUpdate(upSQL);\n\t\tAssert.assertEquals(true, count > 0);\n\t\ttheCon.rollback();\n\t\trs = theCon.createStatement().executeQuery(sql);\n\t\tString newName = null;\n\t\tif (rs.next()) {\n\t\t\tnewName = rs.getString(2);\n\t\t}\n\t\tAssert.assertEquals(true, oldName.equals(newName));\n\t\tSystem.out.println(\"testTransaction passed\");\n\t}\n\n\tpublic static void closeCon(Connection theCon) {\n\t\ttry {\n\t\t\ttheCon.close();\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tpublic static Connection getCon(String[] args) throws Exception {\n\t\tif (driverLoaded == false) {\n\t\t\tClass.forName(\"com.mysql.jdbc.Driver\");\n\t\t\tdriverLoaded = true;\n\n\t\t}\n\t\tif (args.length < 3) {\n\t\t\tSystem.out\n\t\t\t\t\t.println(\"input param,format: [jdbcurl] [user] [password]   \");\n\t\t\treturn null;\n\t\t}\n\t\tString url = args[0];\n\t\tString user = args[1];\n\t\tString password = args[2];\n\t\treturn getCon(url, user, password);\n\n\t}\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tConnection theCon = null;\n\t\ttry {\n\t\t\ttheCon = getCon(args);\n\t\t\ttestBadSQL(theCon);\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tcloseCon(theCon);\n\t\t}\n\n\t\ttry {\n\t\t\ttheCon = getCon(args);\n\t\t\ttestMultiNodeLargeResultset(theCon);\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tcloseCon(theCon);\n\t\t}\n\t\ttry {\n\t\t\ttheCon = getCon(args);\n\t\t\ttestSingleNodeNormalSQL(theCon);\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tcloseCon(theCon);\n\t\t}\n\t\ttry {\n\t\t\ttheCon = getCon(args);\n\t\t\ttestTransaction(theCon);\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tcloseCon(theCon);\n\t\t}\n\t\ttheCon = null;\n\t\ttry {\n\t\t\ttheCon = getCon(args);\n\t\t\ttestMultiNodeNormalSQL(theCon);\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tcloseCon(theCon);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/sqlexecute/MultiThreadSelectTest.java",
    "content": "package io.mycat.sqlexecute;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MultiThreadSelectTest {\n\tprivate static void testSequnce(Connection theCon) throws SQLException {\n\t\tboolean autCommit = System.currentTimeMillis() % 2 == 1;\n\t\ttheCon.setAutoCommit(autCommit);\n\n\t\tString sql = \"select * from company \";\n\t\tStatement stmt = theCon.createStatement();\n\t\tint charChoise = (int) (System.currentTimeMillis() % 3);\n\t\tif (charChoise == 0) {\n\t\t\tstmt.executeQuery(\"SET NAMES UTF8;\");\n\t\t} else if (charChoise == 1) {\n\t\t\tstmt.executeQuery(\"SET NAMES latin1;\");\n\t\t}\n\t\tif (charChoise == 2) {\n\t\t\tstmt.executeQuery(\"SET NAMES gb2312;\");\n\t\t}\n\t\tResultSet rs = stmt.executeQuery(sql);\n\t\tif (rs.next()) {\n\t\t\tSystem.out.println(Thread.currentThread().getName() + \" get seq \" + rs.getLong(1));\n\t\t} else {\n\t\t\tSystem.out.println(Thread.currentThread().getName() + \" can't get  seq \");\n\t\t}\n\t\tif (autCommit == false) {\n\t\t\ttheCon.commit();\n\t\t}\n\t\tstmt.close();\n\n\t}\n\n\tprivate static Connection getCon(String url, String user, String passwd) throws SQLException {\n\t\tConnection theCon = DriverManager.getConnection(url, user, passwd);\n\t\treturn theCon;\n\t}\n\n\tpublic static void main(String[] args) {\n\t\ttry {\n\t\t\tClass.forName(\"com.mysql.jdbc.Driver\");\n\t\t} catch (ClassNotFoundException e1) {\n\t\t\te1.printStackTrace();\n\t\t}\n\n\t\tfinal String url = \"jdbc:mysql://localhost:8066/TESTDB\";\n\t\tfinal String user = \"test\";\n\t\tfinal String password = \"test\";\n\t\tList<Thread> threads = new ArrayList<Thread>(100);\n\t\tfor (int i = 0; i < 50; i++) {\n\n\t\t\tthreads.add(new Thread() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tConnection con;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcon = getCon(url, user, password);\n\t\t\t\t\t\tfor (int j = 0; j < 10000; j++) {\n\t\t\t\t\t\t\ttestSequnce(con);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (SQLException e) {\n\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t});\n\n\t\t}\n\t\tfor (Thread thred : threads) {\n\t\t\tthred.start();\n\n\t\t}\n\t\tboolean hasRunning = true;\n\t\twhile (hasRunning) {\n\t\t\thasRunning = false;\n\t\t\tfor (Thread thred : threads) {\n\t\t\t\tif (thred.isAlive()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tThread.sleep(1000);\n\t\t\t\t\t\thasRunning = true;\n\t\t\t\t\t} catch (InterruptedException e) {\n\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/sqlexecute/MultiThreadSequnceTest.java",
    "content": "package io.mycat.sqlexecute;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MultiThreadSequnceTest {\n\tprivate static void testSequnce(Connection theCon) throws SQLException {\n\t\ttry {\n\t\t\ttheCon.setAutoCommit(false);\n\t\t\tString sql = \"select next value for MYCATSEQ_GLOBAL \";\n\t\t\tStatement stmt = theCon.createStatement();\n\t\t\tResultSet rs = stmt.executeQuery(sql);\n\t\t\tif (rs.next()) {\n\t\t\t\tSystem.out.println(Thread.currentThread().getName()\n\t\t\t\t\t\t+ \" get seq \" + rs.getLong(1));\n\t\t\t} else {\n\t\t\t\tSystem.out.println(Thread.currentThread().getName()\n\t\t\t\t\t\t+ \" can't get  seq \");\n\t\t\t}\n\n\t\t\ttheCon.commit();\n\t\t\tstmt.close();\n\t\t} finally {\n\t\t\ttheCon.close();\n\t\t}\n\t}\n\n\tprivate static Connection getCon(String url, String user, String passwd)\n\t\t\tthrows SQLException {\n\t\tConnection theCon = DriverManager.getConnection(url, user, passwd);\n\t\treturn theCon;\n\t}\n\n\tpublic static void main(String[] args) {\n\t\ttry {\n\t\t\tClass.forName(\"com.mysql.jdbc.Driver\");\n\t\t} catch (ClassNotFoundException e1) {\n\t\t\te1.printStackTrace();\n\t\t}\n\n\t\tfinal String url = \"jdbc:mysql://localhost:8066/TESTDB\";\n\t\tfinal String user = \"test\";\n\t\tfinal String password = \"test\";\n\t\tList<Thread> threads = new ArrayList<Thread>(100);\n\t\tfor (int i = 0; i < 100; i++) {\n\n\t\t\tthreads.add(new Thread() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tConnection con;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcon = getCon(url, user, password);\n\t\t\t\t\t\ttestSequnce(con);\n\t\t\t\t\t} catch (SQLException e) {\n\t\t\t\t\t\t\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t});\n\n\t\t}\n\t\tfor (Thread thred : threads) {\n\t\t\tthred.start();\n\n\t\t}\n\t\tboolean hasRunning = true;\n\t\twhile (hasRunning) {\n\t\t\thasRunning = false;\n\t\t\tfor (Thread thred : threads) {\n\t\t\t\tif (thred.isAlive()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tThread.sleep(1000);\n\t\t\t\t\t\thasRunning = true;\n\t\t\t\t\t} catch (InterruptedException e) {\n\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/sqlexecute/MycatMulitJdbcVersionTest.java",
    "content": "package io.mycat.sqlexecute;\n\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.sql.Connection;\nimport java.sql.Driver;\nimport java.sql.DriverManager;\nimport java.sql.DriverPropertyInfo;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.SQLFeatureNotSupportedException;\nimport java.sql.Statement;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.logging.Logger;\n\n/**\n * \n * 测试mycat对不同版本的mysql jdbc的兼容性 \n * \n * \n * <p>\n * 关联issue: @see https://github.com/MyCATApache/Mycat-Server/issues/1203\n * \n * <p>\n * <b>Note:</b> </br>\n * 1. 请将这个类放到新建的project独立运行, mycat pom.xml里面使用的mysql驱动会影响测试结果 </br>\n * 2. 确保project新建lib子目录并且在lib子目录里面放置了各类版本的mysql jdbc驱动\n * 3. 程序会动态加载不同版本的jdbc驱动, 请不要将任何mysql jdbc驱动加入classpath, 否则也可能影响测试结果\n * \n * @author CrazyPig\n * @since 2016-11-13\n *\n */\npublic class MycatMulitJdbcVersionTest {\n\t\n\tprivate static final String JDBC_URL = \"jdbc:mysql://localhost:8066/TESTDB\";\n\tprivate static final String USER = \"root\";\n\tprivate static final String PASSWORD = \"123456\";\n\tprivate static final Map<String, String> jdbcVersionMap = new HashMap<String, String>();\n\tprivate static final Map<String, Driver> tmpDriverMap = new HashMap<String, Driver>();\n\t\n\t// 动态加载jdbc驱动\n\tprivate static void dynamicLoadJdbc(String mysqlJdbcFile) throws Exception {\n\t\tURL u = new URL(\"jar:file:lib/\" + mysqlJdbcFile + \"!/\");\n\t\tString classname = jdbcVersionMap.get(mysqlJdbcFile);\n\t\tURLClassLoader ucl = new URLClassLoader(new URL[] { u });\n\t\tDriver d = (Driver)Class.forName(classname, true, ucl).newInstance();\n\t\tDriverShim driver = new DriverShim(d);\n\t\tDriverManager.registerDriver(driver);\n\t\ttmpDriverMap.put(mysqlJdbcFile, driver);\n\t}\n\t\n\t// 每一次测试完卸载对应版本的jdbc驱动\n\tprivate static void dynamicUnLoadJdbc(String mysqlJdbcFile) throws SQLException {\n\t\tDriverManager.deregisterDriver(tmpDriverMap.get(mysqlJdbcFile));\n\t}\n\t\n\t// 进行一次测试\n\tprivate static void testOneVersion(String mysqlJdbcFile) {\n\t\t\n\t\tSystem.out.println(\"start test mysql jdbc version : \" + mysqlJdbcFile);\n\t\t\n\t\ttry {\n\t\t\tdynamicLoadJdbc(mysqlJdbcFile);\n\t\t} catch (Exception e1) {\n\t\t\te1.printStackTrace();\n\t\t}\n\t\t\n\t\tConnection conn = null;\n\t\ttry {\n\t\t\tconn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);\n\t\t\tStatement stmt = conn.createStatement();\n\t\t\tResultSet rs = stmt.executeQuery(\"select user()\");\n\t\t\tSystem.out.println(\"select user() output : \");\n\t\t\twhile(rs.next()) {\n\t\t\t\tSystem.out.println(rs.getObject(1));\n\t\t\t}\n\t\t\trs = stmt.executeQuery(\"show tables\");\n\t\t\tSystem.out.println(\"show tables output : \");\n\t\t\twhile(rs.next()) {\n\t\t\t\tSystem.out.println(rs.getObject(1));\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tif(conn != null) {\n\t\t\t\ttry {\n\t\t\t\t\tconn.close();\n\t\t\t\t} catch (SQLException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\ttry {\n\t\t\tdynamicUnLoadJdbc(mysqlJdbcFile);\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t\tSystem.out.println(\"end !!!\");\n\t\tSystem.out.println();\n\t}\n\t\n\tpublic static void main(String[] args) {\n\t\t\n\t\t// 多版本mysql jdbc驱动兼容性测试\n\t\t\n\t\t// NOTE: 注意将对应的jar放到lib子目录, 不需要加入classpath!!!\n\t\tjdbcVersionMap.put(\"mysql-connector-java-6.0.3.jar\", \"com.mysql.cj.jdbc.Driver\");\n\t\tjdbcVersionMap.put(\"mysql-connector-java-5.1.6.jar\", \"com.mysql.jdbc.Driver\");\n\t\tjdbcVersionMap.put(\"mysql-connector-java-5.1.31.jar\", \"com.mysql.jdbc.Driver\");\n\t\tjdbcVersionMap.put(\"mysql-connector-java-5.1.35.jar\", \"com.mysql.jdbc.Driver\");\n\t\tjdbcVersionMap.put(\"mysql-connector-java-5.1.39.jar\", \"com.mysql.jdbc.Driver\");\n\t\t\n\t\t// 更多的jdbc驱动...\n\t\t\n\t\tfor(String mysqlJdbcFile : jdbcVersionMap.keySet()) {\n\t\t\ttestOneVersion(mysqlJdbcFile);\n\t\t}\n\t\t\n\t}\n\n}\n\nclass DriverShim implements Driver {\n    private Driver driver;\n    DriverShim(Driver d) { this.driver = d; }\n    public boolean acceptsURL(String u) throws SQLException {\n        return this.driver.acceptsURL(u);\n    }\n    public Connection connect(String u, Properties p) throws SQLException {\n        return this.driver.connect(u, p);\n    }\n\t@Override\n\tpublic DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {\n\t\treturn this.driver.getPropertyInfo(url, info);\n\t}\n\t@Override\n\tpublic int getMajorVersion() {\n\t\treturn this.driver.getMajorVersion();\n\t}\n\t@Override\n\tpublic int getMinorVersion() {\n\t\treturn this.driver.getMinorVersion();\n\t}\n\t@Override\n\tpublic boolean jdbcCompliant() {\n\t\treturn this.driver.jdbcCompliant();\n\t}\n\t@Override\n\tpublic Logger getParentLogger() throws SQLFeatureNotSupportedException {\n\t\treturn this.driver.getParentLogger();\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/sqlexecute/RollbackTest.java",
    "content": "package io.mycat.sqlexecute;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\n\npublic class RollbackTest {\n\tprivate static Connection getCon(String url, String user, String passwd)\n\t\t\tthrows SQLException {\n\t\tConnection theCon = DriverManager.getConnection(url, user, passwd);\n\t\treturn theCon;\n\t}\n\tpublic static void main(String[] args) {\n\t\t\n\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/sqlexecute/ServerPrepareTest.java",
    "content": "package io.mycat.sqlexecute;\n\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.sql.*;\n\nimport org.junit.Assert;\n\n/**\n * \n * @author CrazyPig\n *\n */\npublic class ServerPrepareTest {\n\n    // JDBC driver name and database URL\n    static final String JDBC_DRIVER = \"com.mysql.jdbc.Driver\";\n    static final String DB_URL = \"jdbc:mysql://localhost:8066/TESTDB?useServerPrepStmts=true\";\n\n    //  Database credentials\n    static final String USER = \"root\";\n    static final String PASS = \"mysql\";\n    \n    static {\n    \ttry {\n\t\t\tClass.forName(\"com.mysql.jdbc.Driver\");\n\t\t} catch (ClassNotFoundException e) {\n\t\t\te.printStackTrace();\n\t\t}\n    }\n    \n    /**\n     *  create table hotnews (\n     *  \tid int primary key auto_increment,\n     *  \ttitle varchar(200),\n     *  \tcontent text,\n     *  \timage0 blob,\n     *  \timage1 blob,\n     *  \timage2 mediumblob,\n     *  \timage3 longblob\n     *  ) engine = innodb default character set = 'utf8';\n     */\n    \n    /**\n     * 测试发送COM_STMT_SEND_LONG_DATA命令\n     * @throws IOException \n     */\n    public static void testComStmtSendLondData() throws IOException {\n    \tConnection conn = null;\n    \tPreparedStatement pstmt = null;\n    \tClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n    \t// 获取待存储图片输入流\n    \tInputStream image0In = classLoader.getResourceAsStream(\"blob/image0.jpg\");\n    \tInputStream image1In = classLoader.getResourceAsStream(\"blob/image1.png\");\n    \tInputStream image2In = classLoader.getResourceAsStream(\"blob/image2.png\");\n    \tInputStream image3In = classLoader.getResourceAsStream(\"blob/image3.png\");\n    \t\n    \t// 保存图片字节数据,待后面取回数据进行校验\n    \tbyte[] image0Bytes = getBytes(image0In);\n    \tbyte[] image1Bytes = getBytes(image1In);\n    \tbyte[] image2Bytes = getBytes(image2In);\n    \tbyte[] image3Bytes = getBytes(image3In);\n    \t\n    \ttry {\n\t\t\tconn = DriverManager.getConnection(DB_URL,USER,PASS);\n\t\t\tpstmt = conn.prepareStatement(\"insert into hotnews(id, title, content, image0, image1, image2, image3) values(?,?,?,?,?,?,?)\");\n\t\t\tpstmt.setInt(1, 1314);\n\t\t\tpstmt.setString(2, \"hotnew\");\n\t\t\t// text字段设置\n\t\t\tpstmt.setBinaryStream(3, new ByteArrayInputStream(\"this is a content of hotnew\".getBytes(\"UTF-8\")));\n\t\t\t// blob字段构造\n\t\t\tBlob image0Blob = conn.createBlob();\n\t\t\tBlob image1Blob = conn.createBlob();\n\t\t\tBlob image2Blob = conn.createBlob();\n\t\t\tBlob image3Blob = conn.createBlob();\n\t\t\timage0Blob.setBytes(1, image0Bytes);\n\t\t\timage1Blob.setBytes(1, image1Bytes);\n\t\t\timage2Blob.setBytes(1, image2Bytes);\n\t\t\timage3Blob.setBytes(1, image3Bytes);\n\t\t\t// blob字段设置\n\t\t\tpstmt.setBlob(4, image0Blob);\n\t\t\tpstmt.setBlob(5, image1Blob);\n\t\t\tpstmt.setBlob(6, image2Blob);\n\t\t\tpstmt.setBlob(7, image3Blob);\n\t\t\t// 执行\n\t\t\tpstmt.execute();\n\t\t\t\n\t\t\t// 从表里面拿出刚插入的数据, 对blob字段进行校验\n\t\t\tpstmt = conn.prepareStatement(\"select image0, image1, image2, image3 from hotnews where id = ?\");\n\t\t\tpstmt.setInt(1, 1314);\n\t\t\tResultSet rs = pstmt.executeQuery();\n\t\t\tif(rs.next()) {\n\t\t\t\tInputStream _image0In = rs.getBlob(1).getBinaryStream();\n\t\t\t\tInputStream _image1In = rs.getBlob(2).getBinaryStream();\n\t\t\t\tInputStream _image2In = rs.getBlob(3).getBinaryStream();\n\t\t\t\tInputStream _image3In = rs.getBlob(4).getBinaryStream();\n\t\t\t\t// 断言从数据库取出来的数据,与之前发送的数据是一致的(字节数组内容比较)\n\t\t\t\tAssert.assertArrayEquals(image0Bytes, getBytes(_image0In));\n\t\t\t\tAssert.assertArrayEquals(image1Bytes, getBytes(_image1In));\n\t\t\t\tAssert.assertArrayEquals(image2Bytes, getBytes(_image2In));\n\t\t\t\tAssert.assertArrayEquals(image3Bytes, getBytes(_image3In));\n\t\t\t}\n\t\t\t\n\t\t\tpstmt.close();\n\t\t\t\n    \t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tif(conn != null) {\n\t\t\t\ttry {\n\t\t\t\t\tconn.close();\n\t\t\t\t} catch (SQLException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n    }\n    \n    private static byte[] getBytes(InputStream in) throws IOException {\n    \tbyte[] bytes = new byte[0];\n    \tbyte[] buffer = new byte[1024];\n    \tByteArrayOutputStream bout = new ByteArrayOutputStream();\n    \tint len = 0;\n    \twhile((len = in.read(buffer)) > -1) {\n    \t\tbout.write(buffer, 0, len);\n    \t}\n    \tbytes = bout.toByteArray();\n    \treturn bytes;\n    }\n    \n    /**\n     * 测试发送COM_STMT_RESET命令\n     */\n    public static void testComStmtRest() {\n    \tConnection conn = null;\n    \tPreparedStatement pstmt = null;\n    \ttry {\n\t\t\tconn = DriverManager.getConnection(DB_URL,USER,PASS);\n\t\t\tpstmt = conn.prepareStatement(\"insert into hotnews(id, title, content) values(?,?,?)\");\n\t\t\tpstmt.setInt(1, 1314);\n\t\t\tpstmt.setString(2, \"hotnew\");\n\t\t\tpstmt.setBinaryStream(3, new ByteArrayInputStream(\"this is a content of hotnew\".getBytes(\"UTF-8\")));\n\t\t\tpstmt.execute();\n\t\t\tpstmt.clearParameters();\n\t\t\tpstmt.setInt(1, 1315);\n\t\t\tpstmt.setString(2, \"hotnew\");\n\t\t\tpstmt.setBinaryStream(3, new ByteArrayInputStream(\"this is a new content of hotnew\".getBytes(\"UTF-8\")));\n\t\t\tpstmt.execute();\n\t\t\tpstmt.close();\n    \t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tif(conn != null) {\n\t\t\t\ttry {\n\t\t\t\t\tconn.close();\n\t\t\t\t} catch (SQLException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n    }\n    \n    public static void simpleTest() {\n    \tConnection conn = null;\n        PreparedStatement stmt = null;\n        try{\n\n            System.out.println(\"Connecting to database...\");\n            conn = DriverManager.getConnection(DB_URL,USER,PASS);\n\n            System.out.println(\"Creating statement...\");\n            String sql = \"SELECT *  FROM test  where id<?\";\n\n            stmt=conn.prepareStatement(sql)   ;\n            stmt.setInt(1,8);\n            ResultSet rs = stmt.executeQuery();\n            // Extract data from result set\n            ResultSetMetaData rsmd = rs.getMetaData();\n            \n            int colCount = rsmd.getColumnCount();\n            for(int i = 1; i <= colCount; i++) {\n            \tSystem.out.print(rsmd.getColumnName(i) + \"\\t\");\n            }\n            System.out.println();\n            while(rs.next()){\n            \t//Display values\n            \tfor(int i = 1; i <= colCount; i++) {\n            \t\tSystem.out.print(rs.getObject(i) + \"\\t\");\n            \t}\n            \tSystem.out.println();\n            }\n            // Clean-up environment\n            rs.close();\n            stmt.close();\n            conn.close();\n        }catch(SQLException se){\n            //Handle errors for JDBC\n            se.printStackTrace();\n        }catch(Exception e){\n            //Handle errors for Class.forName\n            e.printStackTrace();\n        }finally{\n            //finally block used to close resources\n            try{\n                if(stmt!=null)\n                    stmt.close();\n            }catch(SQLException se2){\n            }// nothing we can do\n            try{\n                if(conn!=null)\n                    conn.close();\n            }catch(SQLException se){\n                se.printStackTrace();\n            }//end finally try\n        }//end try\n        System.out.println(\"Goodbye!\");\n    }\n\n    public static void main(String[] args) {\n    \t\n    \ttry {\n\t\t\ttestComStmtSendLondData();\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n    \t\n    }//end main\n\n\n    static void print( String name , ResultSet res )\n            throws SQLException {\n        System.out.println( name);\n        ResultSetMetaData meta=res.getMetaData();\n        //System.out.println( \"\\t\"+res.getRow()+\"条记录\");\n        String  str=\"\";\n        for(int i=1;i<=meta.getColumnCount();i++){\n            str+=meta.getColumnName(i)+\"   \";\n            //System.out.println( meta.getColumnName(i)+\"   \");\n        }\n        System.out.println(\"\\t\"+str);\n        str=\"\";\n        while ( res.next() ){\n            for(int i=1;i<=meta.getColumnCount();i++){\n                str+= res.getString(i)+\"   \";\n            }\n            System.out.println(\"\\t\"+str);\n            str=\"\";\n        }\n    }\n}\n\n"
  },
  {
    "path": "src/test/java/io/mycat/sqlexecute/StandBatchInsertTest.java",
    "content": "package io.mycat.sqlexecute;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.Statement;\n\npublic class StandBatchInsertTest {\n\n\tpublic static void testJDBCBatchInsert(Connection theCon)\n\t\t\tthrows SQLException {\n\t\ttheCon.setAutoCommit(false);\n\t\tStatement stmt = theCon.createStatement();\n\t\tint batchSize = 10;\n\t\tfor (int i = 0; i < batchSize; i++) {\n\t\t\tString sql = \"insert into travelrecord (id,user_id,traveldate,fee,days) values(\"\n\t\t\t\t\t+ i + \",'wang','2014-01-05',510.5,3)\";\n\t\t\tstmt.addBatch(sql);\n\t\t}\n\t\tstmt.executeBatch();\n\t\ttheCon.commit();\n      System.out.println(\"succees\");\n\t}\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tConnection theCon = null;\n\t\ttry {\n\t\t\ttheCon = BaseSQLExeTest.getCon(args);\n\t\t\ttestJDBCBatchInsert(theCon);\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tBaseSQLExeTest.closeCon(theCon);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/sqlexecute/TestJdbc.java",
    "content": "package io.mycat.sqlexecute;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\npublic class TestJdbc {\n\tpublic static void main(String[] args) throws SQLException {\n\t\ttry {\n\t\t\t// 加载MySql的驱动类\n\t\t\tClass.forName(\"com.mysql.jdbc.Driver\");\n\t\t} catch (ClassNotFoundException e) {\n\t\t\tSystem.out.println(\"找不到驱动程序类 ，加载驱动失败！\");\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\t// 连接MySql数据库，用户名和密码都是root\n\t\t// String url = \"jdbc:mysql://192.168.56.101:3309/mycat\";\n\t\t// String username = \"root\";\n\t\t// String password = \"root123\";\n\t\tString url = \"jdbc:mysql://127.0.0.1:8066/TESTDB\";\n\t\tString username = \"test\";\n\t\tString password = \"test\";\n\t\tConnection con = null;\n\t\ttry {\n\t\t\tcon = DriverManager.getConnection(url, username, password);\n\t\t} catch (SQLException se) {\n\t\t\tSystem.out.println(\"数据库连接失败！\");\n\t\t\tse.printStackTrace();\n\t\t}\n\t\tPreparedStatement stmt = null;\n\t\tResultSet rs = null;\n\t\ttry {\n\t\t\t// String sql =\n\t\t\t// \"insert into customer (id,name,company_id,sharding_id) values (10001,'test',1,10000)\";\n\t\t\t// PreparedStatement pstmt = con.prepareStatement(sql) ;\n\t\t\t// ResultSet rs = stmt.executeQuery(\"SELECT * FROM ...\") ;\n\t\t\t// int rows = stmt.executeUpdate(\"INSERT INTO ...\") ;\n\t\t\t// boolean flag = stmt.execute(String sql)\n\t\t\t// while(rs.next()){\n\t\t\t// String name = rs.getString(\"name\") ;\n\t\t\t// String pass = rs.getString(1) ; // 此方法比较高效\n\t\t\t// }\n\n\t\t\tcon.setAutoCommit(false);\n//\t\t\tstmt = con\n//\t\t\t\t\t.prepareStatement(\"insert into hotnews(id, title,created_time) values(?, ?,?)\");\n\t\t\tstmt = con\n\t\t\t.prepareStatement(\"update company set name=concat(name,'1')\");\n\t\t\t// con.setAutoCommit(false);\n\t\t\t// stmt =\n\t\t\t// con.prepareStatement(\"select * from tb_cm_cust limit 100000\");\n\t\t\tlong begin = System.currentTimeMillis();\n\t\t\t// con.createStatement().execute(\"truncate table hotnews\");\n\t\t\tSystem.out.println();\n\t\t\t// for (int loop = 0; loop < 10; loop++) {\n\t\t\t//stmt.setInt(1, 1);\n\t\t\t//stmt.setString(2, 1 + \"\" + new Date());\n\n\t\t\t// stmt.setInt(1, loop);\n\t\t\t// stmt.setString(2, loop + \"\" + new Date());\n\t\t\t//stmt.setDate(3, new java.sql.Date(System.currentTimeMillis()));\n\t\t\tstmt.addBatch();\n\t\t\t// if (loop % 5 == 0) {\n\t\t\t// System.out.println(loop);\n\t\t\t// }\n\t\t\t// }\n\t\t\t//\n\t\t\tstmt.executeBatch();\n//\t\t\tif (true) {\n//\t\t\t\tthrow new RuntimeException(\"test\");\n//\t\t\t}\n\t\t\tcon.commit();\n\t\t\t// stmt.executeQuery();\n\t\t\tSystem.out.println((System.currentTimeMillis() - begin) / 1000.0);\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\ttry {\n\t\t\t\tcon.rollback();\n\t\t\t} catch (Exception e2) {\n\t\t\t\te2.printStackTrace();\n\t\t\t}\n\t\t} finally {\n\n\t\t\tif (rs != null) {\n\t\t\t\trs.close();\n\t\t\t}\n\t\t\tif (stmt != null) {\n\t\t\t\tstmt.close();\n\t\t\t}\n\t\t\tif (con != null) {\n\t\t\t\tcon.close();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/sqlexecute/TestPrepareSql.java",
    "content": "package io.mycat.sqlexecute;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\n\npublic class TestPrepareSql {\n\n\tprivate static String url = \"jdbc:mysql://localhost:8066/test?useServerPrepStmts=true\"; // 使用服务端预处理\n\tprivate static String user = \"zhuam\";\n\tprivate static String password = \"zhuam\";\n\t\n\tstatic {\n\t\ttry {\n\t\t\t// 加载MySql的驱动类\n\t\t\tClass.forName(\"com.mysql.jdbc.Driver\");\n\t\t} catch (ClassNotFoundException e) {\n\t\t\tSystem.out.println(\"找不到驱动程序类 ，加载驱动失败！\");\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\t\n\tpublic static void main(String[] args) {\n\t\ttestServerPrepareInsertWithBingParam();\n\t}\n\t\n\t/**\n\t * 测试服务端预处理批量插入,动态绑定插入参数\n\t */\n\tpublic static void testServerPrepareInsertWithBingParam() {\n\t\tConnection conn = null;\n\t\ttry {\n\t\t\tconn = DriverManager.getConnection(url, user, password);\n\t\t\tconn.setAutoCommit(false);\n\t\t\tString sql = \"insert into v1test(id,name1) values(?,?)\";\n\t\t\tPreparedStatement pstmt = conn.prepareStatement(sql);\n\t\t\tint startId = 100;\n\t\t\tint batchSize = 10;\n\t\t\tint count = 0;\n\t\t\twhile(count < batchSize) {\n\t\t\t\tpstmt.setInt(1, (int)(System.currentTimeMillis() / 1000L) + startId);\n\t\t\t\tpstmt.setString(2, \"wowo\" + startId);\n\t\t\t\tstartId++;\n\t\t\t\tcount++;\n\t\t\t\tpstmt.addBatch();\n\t\t\t}\n\t\t\tpstmt.executeBatch();\n\t\t\tconn.setAutoCommit(true);\n\t\t\tpstmt.close();\n\t\t} catch (SQLException e) {\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tif(conn != null) {\n\t\t\t\ttry {\n\t\t\t\t\tconn.close();\n\t\t\t\t} catch (SQLException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/statistic/SQLStatisticsMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.statistic;\n\nimport io.mycat.statistic.SQLRecord;\nimport io.mycat.statistic.SQLRecorder;\n\n/**\n * @author mycat\n */\npublic class SQLStatisticsMain {\n\n\n\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/statistic/TestConcurrentSafety.java",
    "content": "package io.mycat.statistic;\n\nimport io.mycat.server.parser.ServerParse;\nimport io.mycat.statistic.stat.*;\nimport org.junit.Assert;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * 测试SQLstat相关元素并发安全性\n *\n *\n *\n *\n *\n *\n * 此单元测试会造成服务器上build运行时间过长一直通不过，最多build了6天还没结束，所以先忽略\n *\n *\n *\n *  后续修复好了再打开\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n *\n * @author Hash Zhang\n * @version 1.0\n * @time 08:54 2016/5/16\n */\npublic class TestConcurrentSafety {\n    private static final int THREAD_COUNT = 2;\n    private static final int LOOP_COUNT = 1000;\n\n    String sql = \"SELECT `fnum`, `forg`, `fdst`, `airline`, `ftype` , `ports_of_call`, \" +\n            \"`scheduled_deptime`, `scheduled_arrtime`, `actual_deptime`, `actual_arrtime`, \" +\n            \"`flight_status_code` FROM dynamic \" +\n            \"WHERE `fnum` = 'CA1'  AND `forg` = 'PEK'  AND `fdst` = 'SHA' \" +\n            \"AND `scheduled_deptime` BETWEEN 1212121 AND 232323233 \" +\n            \"AND `fservice` = 'J' AND `fcategory` = 1 \" +\n            \"AND `share_execute_flag` = 1 ORDER BY scheduled_deptime\";\n\n    String sql2 = \"SELECT `fnum`, `forg`, `fdst`, `airline`, `ftype` , `ports_of_call`, \" +\n            \"`scheduled_deptime`, `scheduled_arrtime`, `actual_deptime`, `actual_arrtime`, \" +\n            \"`flight_status_code` FROM dynamic \" +\n            \"WHERE `fnum` = 'CA2'  AND `forg` = 'PEK'  AND `fdst` = 'SHA' \" +\n            \"AND `scheduled_deptime` BETWEEN 1212121 AND 232323233 \" +\n            \"AND `fservice` = 'J' AND `fcategory` = 1 \" +\n            \"AND `share_execute_flag` = 1 ORDER BY scheduled_deptime\";\n\n    String sql3 = \"SELECT `fnum`, `forg`, `fdst`, `airline`, `ftype` , `ports_of_call`, \" +\n            \"`scheduled_deptime`, `scheduled_arrtime`, `actual_deptime`, `actual_arrtime`, \" +\n            \"`flight_status_code` FROM dynamic \" +\n            \"WHERE `fnum` = 'CA3'  AND `forg` = 'PEK'  AND `fdst` = 'SHA' \" +\n            \"AND `scheduled_deptime` BETWEEN 1212121 AND 232323233 \" +\n            \"AND `fservice` = 'J' AND `fcategory` = 1 \" +\n            \"AND `share_execute_flag` = 1 ORDER BY scheduled_deptime\";\n\n    String sql4 = \"SELECT `fnum`, `forg`, `fdst`, `airline`, `ftype` , `ports_of_call`, \" +\n            \"`scheduled_deptime`, `scheduled_arrtime`, `actual_deptime`, `actual_arrtime`, \" +\n            \"`flight_status_code` FROM dynamic \" +\n            \"WHERE `fnum` = 'CA3'  AND `forg` = 'PEK'\";\n\n\n    @Test  @Ignore\n    public void testQueryConditionAnalyzer() throws InterruptedException {\n\n\n        final QueryResult qr = new QueryResult(\"zhuam\", \"root\", ServerParse.SELECT, sql, 0, 0, 0, 0, 0,0,       \"127.0.0.1\");\n        final QueryResult qr2 = new QueryResult(\"zhuam\", \"root\", ServerParse.SELECT, sql2, 0, 0, 0, 0, 0,0,       \"127.0.0.1\");\n        final QueryResult qr3 = new QueryResult(\"zhuam\", \"root\", ServerParse.SELECT, sql3, 0, 0, 0, 0, 0,0,       \"127.0.0.1\");\n\n        final QueryConditionAnalyzer analyzer = QueryConditionAnalyzer.getInstance();\n        analyzer.setCf(\"dynamic&fnum\");\n\n        Thread thread[] = new Thread[THREAD_COUNT];\n        Thread thread2[] = new Thread[THREAD_COUNT];\n        Thread thread3[] = new Thread[THREAD_COUNT];\n\n        for (int i = 0; i < THREAD_COUNT; i++) {\n            thread[i] = new Thread() {\n                @Override\n                public void run() {\n                    for (int j = 0; j < LOOP_COUNT; j++) {\n                        analyzer.onQueryResult(qr);\n                    }\n                }\n            };\n\n            thread2[i] = new Thread() {\n                @Override\n                public void run() {\n                    for (int j = 0; j < LOOP_COUNT; j++) {\n                        analyzer.onQueryResult(qr2);\n                    }\n                }\n            };\n\n            thread3[i] = new Thread() {\n                @Override\n                public void run() {\n                    for (int j = 0; j < LOOP_COUNT; j++) {\n                        analyzer.onQueryResult(qr3);\n                    }\n                }\n            };\n        }\n\n        for (int i = 0; i < THREAD_COUNT; i++) {\n            thread[i].start();\n            thread2[i].start();\n            thread3[i].start();\n        }\n\n        for (int i = 0; i < THREAD_COUNT; i++) {\n            thread[i].join();\n            thread2[i].join();\n            thread3[i].join();\n        }\n\n        List<Map.Entry<Object, AtomicLong>> list = analyzer.getValues();\n        Assert.assertTrue((list.get(0).getValue().get() == (long) THREAD_COUNT * LOOP_COUNT));\n        Assert.assertTrue((list.get(1).getValue().get() == (long) THREAD_COUNT * LOOP_COUNT));\n        Assert.assertTrue((list.get(2).getValue().get() == (long) THREAD_COUNT * LOOP_COUNT));\n    }\n\n    @Test       @Ignore\n    public void testUserSqlHighStat() throws InterruptedException {\n        final UserSqlHighStat userSqlHighStat = new UserSqlHighStat();\n\n        Thread thread[] = new Thread[THREAD_COUNT];\n        Thread thread2[] = new Thread[THREAD_COUNT];\n        Thread thread3[] = new Thread[THREAD_COUNT];\n\n        for (int i = 0; i < THREAD_COUNT; i++) {\n            thread[i] = new Thread() {\n                @Override\n                public void run() {\n                    for (int j = 0; j < LOOP_COUNT; j++) {\n                        userSqlHighStat.addSql(sql, 10L, 1L, 11L,      \"127.0.0.1\");\n                    }\n                }\n            };\n\n            thread2[i] = new Thread() {\n                @Override\n                public void run() {\n                    for (int j = 0; j < LOOP_COUNT; j++) {\n                        userSqlHighStat.addSql(sql2, 10L, 1L, 11L,      \"127.0.0.1\");\n                    }\n                }\n            };\n\n            thread3[i] = new Thread() {\n                @Override\n                public void run() {\n                    for (int j = 0; j < LOOP_COUNT; j++) {\n                        userSqlHighStat.addSql(sql4, 10L, 1L, 11L,      \"127.0.0.1\");\n                    }\n                }\n            };\n        }\n\n        for (int i = 0; i < THREAD_COUNT; i++) {\n            thread[i].start();\n            thread2[i].start();\n            thread3[i].start();\n        }\n\n        for (int i = 0; i < THREAD_COUNT; i++) {\n            thread[i].join();\n            thread2[i].join();\n            thread3[i].join();\n        }\n\n        List<SqlFrequency> sqlFrequency = userSqlHighStat.getSqlFrequency(true);\n        Assert.assertTrue(sqlFrequency.size() == 2);\n        Assert.assertTrue(sqlFrequency.get(0).getCount() == 2 * THREAD_COUNT *LOOP_COUNT);\n        Assert.assertTrue(sqlFrequency.get(1).getCount() == THREAD_COUNT *LOOP_COUNT);\n    }\n\n\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/util/ArrayPerformanceMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author mycat\n */\npublic class ArrayPerformanceMain {\n\n    public void tArray() {\n        byte[] a = new byte[] { 1, 2, 3, 4, 5, 6, 7 };\n        System.currentTimeMillis();\n        long t1 = System.currentTimeMillis();\n        for (int x = 0; x < 1000000; x++) {\n            byte[][] ab = new byte[10][];\n            for (int i = 0; i < ab.length; i++) {\n                ab[i] = a;\n            }\n        }\n        long t2 = System.currentTimeMillis();\n        System.out.println(\"array take time:\" + (t2 - t1) + \" ms.\");\n    }\n\n    public void tList() {\n        byte[] a = new byte[] { 1, 2, 3, 4, 5, 6, 7 };\n        System.currentTimeMillis();\n        long t1 = System.currentTimeMillis();\n        for (int x = 0; x < 1000000; x++) {\n            List<byte[]> ab = new ArrayList<byte[]>(10);\n            for (int i = 0; i < ab.size(); i++) {\n                ab.add(a);\n            }\n        }\n        long t2 = System.currentTimeMillis();\n        System.out.println(\"list take time:\" + (t2 - t1) + \" ms.\");\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/util/BitTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport org.junit.Test;\n\n/**\n * @author mycat\n */\npublic class BitTest {\n    @Test\n    public void testNoop() {\n    }\n\n    public static void main(String[] args) {\n        System.out.println(0xffff0001 & 0xffff);// 低16位\n        System.out.println(0x0002ffff >>> 16);// 高16位\n    }\n}"
  },
  {
    "path": "src/test/java/io/mycat/util/ConcurrentHashMapMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * @author mycat\n */\npublic class ConcurrentHashMapMain {\n\n    private final ConcurrentMap<String, String> cm;\n\n    public ConcurrentHashMapMain() {\n        cm = new ConcurrentHashMap<String, String>();\n        cm.put(\"abcdefg\", \"abcdefghijk\");\n    }\n\n    public void tGet() {\n        for (int i = 0; i < 1000000; i++) {\n            cm.get(\"abcdefg\");\n        }\n    }\n\n    public void tGetNone() {\n        for (int i = 0; i < 1000000; i++) {\n            cm.get(\"abcdefghijk\");\n        }\n    }\n\n    public void tEmpty() {\n        for (int i = 0; i < 1000000; i++) {\n            cm.isEmpty();\n        }\n    }\n\n    public void tRemove() {\n        for (int i = 0; i < 1000000; i++) {\n            cm.remove(\"abcdefg\");\n        }\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/util/HashMapMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author mycat\n */\npublic class HashMapMain {\n\n    public void t() {\n        String[] keys = new String[] { \"a\", \"b\", \"c\", \"d\", \"e\" };\n        long t = System.currentTimeMillis();\n        int count = 1000000;\n        Map<String, String> m = new HashMap<String, String>();\n        t = System.currentTimeMillis();\n        for (int i = 0; i < count; i++) {\n            for (String key : keys) {\n                m.put(key, \"String.value\");\n            }\n            for (String key : keys) {\n                m.remove(key);\n            }\n        }\n        System.out.println((System.currentTimeMillis() - t) * 1000 * 1000 / (count * keys.length * 2) + \" ns\");\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/util/HexFormatUtilMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.mycat.util.HexFormatUtil;\n\n/**\n * @author mycat\n */\npublic class HexFormatUtilMain {\n\n    public static void main(String[] args) {\n        List<String> srcList = new ArrayList<String>();\n        srcList.add(\"53 45 4C 45 43 54 20 4C 41 53 54 5F 49 4E 53 45 52 54 5F 49 44 28 29\");\n        srcList.add(\"4C 41 53 54 5F 49 4E 53 45 52 54 5F 49 44 28 29\");\n        srcList.add(\"64 65 66\");\n        srcList.add(\"73 65 6C 65 63 74 20 2A 20 66 72 6F 6D 20 62 72 6D 6D 73 5F 75 73 65 72 20 6C 69 6D 69 74 20 31\");\n        srcList.add(\"62 72 6D 6D 73 31\");\n        srcList.add(\"62 72 6D 6D 73 5F 75 73 65 72\");\n        srcList.add(\"69 64\");\n        srcList.add(\"49 4E 53 45 52 54 20 49 4E 54 4F 20 62 72 6D 6D 73 5F 75 73 65 72 20 56 41 4C 55 45 53 20 28 6E 75 6C 6C 2C 27 68 65 78 69 61 6E 6D 61 6F 27 2C 30 2C 30 2C 30 2C 30 2C 27 32 30 30 39 2D 30 33 2D 30 35 27 2C 27 31 32 31 2E 33 34 2E 31 37 38 2E 33 35 27 2C 27 32 30 30 39 2D 30 33 2D 30 35 20 31 34 3A 33 38 3A 33 35 27 2C 27 32 30 30 39 2D 30 33 2D 30 35 20 31 34 3A 33 38 3A 33 35 27 2C 27 32 30 30 39 2D 30 33 2D 30 35 20 31 34 3A 33 38 3A 33 35 27 29\");\n        srcList.add(\"73 65 6C 65 63 74 20 69 64 20 66 72 6F 6D 20 6F 66 66 65 72 20 6C 69 6D 69 74 20 3F\");\n        srcList.add(\"73 65 6C 65 63 74 20 69 64 20 66 72 6F 6D 20 6F 66 66 65 72 20 6C 69 6D 69 74 20 31\");\n        srcList.add(\"64 65 66\");\n        srcList.add(\"3F\");\n        srcList.add(\"6F 66 66 65 72 31\");\n        srcList.add(\"6F 66 66 65 72\");\n        srcList.add(\"32 39 30 34 33\");\n        for (int i = 0; i < srcList.size(); i++) {\n            System.out.println(HexFormatUtil.fromHex(srcList.get(i), \"UTF-8\"));\n        }\n        System.out.println(HexFormatUtil.fromHex8B(\"73 71 00 00 00 00 00 00\"));\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/util/HexFormatUtilTest.java",
    "content": "package io.mycat.util;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * \n * @author CrazyPig\n * @since 2016-09-09\n *\n */\npublic class HexFormatUtilTest {\n\t\n\t@Test\n\tpublic void testBytesToString() {\n\t\tbyte[] bytes = new byte[]{\n\t\t\t1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20\n\t\t};\n\t\tString hexString = HexFormatUtil.bytesToHexString(bytes);\n\t\tString expected = \"0102030405060708090A0B0C0D0E0F1011121314\";\n\t\tAssert.assertEquals(expected, hexString);\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/io/mycat/util/LockPerfMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * @author mycat\n */\npublic class LockPerfMain {\n\n    public void tReentrantLock() {\n        System.currentTimeMillis();\n        ReentrantLock lock = new ReentrantLock();\n\n        long t1 = System.currentTimeMillis();\n        for (int i = 0; i < 10000000; i++) {\n            if (lock.tryLock()) {\n                try {\n                    // ...\n                } finally {\n                    lock.unlock();\n                }\n            }\n        }\n        long t2 = System.currentTimeMillis();\n\n        System.out.println(\"take time:\" + (t2 - t1) + \" ms.\");\n    }\n\n    public void tAtomicBoolean() {\n        System.currentTimeMillis();\n        AtomicBoolean atomic = new AtomicBoolean();\n\n        long t1 = System.currentTimeMillis();\n        for (int i = 0; i < 10000000; i++) {\n            if (atomic.compareAndSet(false, true)) {\n                try {\n                    // ...\n                } finally {\n                    atomic.set(false);\n                }\n            }\n        }\n        long t2 = System.currentTimeMillis();\n\n        System.out.println(\"take time:\" + (t2 - t1) + \" ms.\");\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/util/MapPerfMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport org.junit.Assert;\n\n/**\n * @author mycat\n */\npublic class MapPerfMain {\n\n    public void t1() {\n        Map<String, Date> m = new HashMap<String, Date>();\n        for (int i = 0; i < 100000; i++) {\n            m.put(UUID.randomUUID().toString(), new Date());\n        }\n        remove1(m);\n        Assert.assertEquals(0, m.size());\n    }\n\n    public void t2() {\n        Map<String, Date> m = new HashMap<String, Date>();\n        for (int i = 0; i < 100000; i++) {\n            m.put(UUID.randomUUID().toString(), new Date());\n        }\n        remove2(m);\n        Assert.assertEquals(0, m.size());\n    }\n\n    void remove1(Map<String, Date> m) {\n        Iterator<Map.Entry<String, Date>> it = m.entrySet().iterator();\n        while (it.hasNext()) {\n            it.next().getValue();\n            it.remove();\n        }\n    }\n\n    void remove2(Map<String, Date> m) {\n        Iterator<Map.Entry<String, Date>> it = m.entrySet().iterator();\n        while (it.hasNext()) {\n            it.next().getValue();\n        }\n        m.clear();\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/util/SchemaUtilTest.java",
    "content": "package io.mycat.util;\nimport static org.junit.Assert.assertArrayEquals;\n\nimport org.junit.Test;\n\nimport io.mycat.server.util.SchemaUtil;\n/**\n * @author stones-he\n */\npublic class SchemaUtilTest {\n    @Test\n    public void parseShowTableTest() {\n        String stmt = \"SHOW FULL TABLES FROM schema001 WHERE Tables_in_schema001 LIKE 'tab_company'; \";\n        String[] fields = SchemaUtil.parseShowTable(stmt);\n        assertArrayEquals(new String[]{\"1\",\n                                       \"FULL\",\n                                       \"FROM\",\n                                       \"schema001\",\n                                       \"WHERE\",\n                                       \"Tables_in_schema001\",\n                                       \"LIKE\",\n                                       \"'tab_company'\",\n                                       \"tab_company\"}, fields);\n    }\n    @Test\n    public void parseShowTableTest1() {\n        String stmt = \"SHOW FULL TABLES LIKE 'tab_company' \";\n        String[] fields = SchemaUtil.parseShowTable(stmt);\n        assertArrayEquals(new String[]{\"1\", \"FULL\", null, null, null, null, \"LIKE\", \"'tab_company'\", \"tab_company\"},\n                          fields);\n    }\n    @Test\n    public void parseShowTableTest2() {\n        String stmt = \"SHOW FULL TABLES IN mysql LIKE 'time%'; \";\n        String[] fields = SchemaUtil.parseShowTable(stmt);\n        assertArrayEquals(new String[]{\"1\", \"FULL\", \"IN\", \"mysql\", null, null, \"LIKE\", \"'time%'\", \"time%\"}, fields);\n        stmt = \"SHOW FULL TABLES IN mysql LIKE '%time%'; \";\n        fields = SchemaUtil.parseShowTable(stmt);\n        assertArrayEquals(new String[]{\"1\", \"FULL\", \"IN\", \"mysql\", null, null, \"LIKE\", \"'%time%'\", \"%time%\"}, fields);\n        stmt = \"SHOW FULL TABLES IN mysql LIKE '%time'; \";\n        fields = SchemaUtil.parseShowTable(stmt);\n        assertArrayEquals(new String[]{\"1\", \"FULL\", \"IN\", \"mysql\", null, null, \"LIKE\", \"'%time'\", \"%time\"}, fields);\n    }\n    @Test\n    public void parseShowTableTest3() {\n        String stmt = \"SHOW TABLES WHERE table_type = 'BASE TABLE';\";\n        String[] fields = SchemaUtil.parseShowTable(stmt);\n        assertArrayEquals(new String[]{\"1\", null, null, null, \"WHERE\", \"table_type\", \"=\", \"'BASE TABLE'\", \"BASE TABLE\"}, fields);\n    }\n    @Test\n    public void parseShowTableTest4() {\n        String stmt = \"SHOW TABLES WHERE table_type = 'BASE TABLE';\";\n        String[] fields = SchemaUtil.parseShowTable(stmt);\n        assertArrayEquals(new String[]{\"1\", null, null, null, \"WHERE\", \"table_type\", \"=\", \"'BASE TABLE'\", \"BASE TABLE\"}, fields);\n\t\t// 多个空格也能解析\n\t\tstmt = \"SHOW FULL    TABLES WHERE table_type = 'BASE TABLE';\";\n        fields = SchemaUtil.parseShowTable(stmt);\n        assertArrayEquals(new String[]{\"1\", \"FULL\", null, null, \"WHERE\", \"table_type\", \"=\", \"'BASE TABLE'\", \"BASE TABLE\"}, fields);\n        //\n        stmt = \"SHOW FULL TABLES in schema001 WHERE table_type = 'BASE TABLE';\";\n        fields = SchemaUtil.parseShowTable(stmt);\n        assertArrayEquals(new String[]{\"1\", \"FULL\", \"in\", \"schema001\", \"WHERE\", \"table_type\", \"=\", \"'BASE TABLE'\", \"BASE TABLE\"}, fields);\n        //\n        stmt = \"SHOW FULL TABLES WHERE table_type LIKE '%BASE TABLE';\";\n        fields = SchemaUtil.parseShowTable(stmt);\n        assertArrayEquals(new String[]{\"1\", \"FULL\", null, null, \"WHERE\", \"table_type\", \"LIKE\", \"'%BASE TABLE'\", \"%BASE TABLE\"}, fields);\n         //\n         stmt = \"SHOW FULL TABLES WHERE tables_in_0001 LIKE '%BASE TABLE';\";\n         fields = SchemaUtil.parseShowTable(stmt);\n         assertArrayEquals(new String[]{\"1\", \"FULL\", null, null, \"WHERE\", \"tables_in_0001\", \"LIKE\", \"'%BASE TABLE'\", \"%BASE TABLE\"}, fields);\n         \n    }\n    //SHOW FULL TABLES in miccore WHERE table_type = 'BASE TABLE';\n}"
  },
  {
    "path": "src/test/java/io/mycat/util/SmallSetTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.util.Collection;\nimport java.util.Iterator;\n\nimport io.mycat.util.SmallSet;\nimport junit.framework.Assert;\nimport junit.framework.TestCase;\n\n/**\n * @author mycat\n */\npublic class SmallSetTest extends TestCase {\n\n    public void assertListEquals(Collection<? extends Object> col, Object... objects) {\n        if (objects == null) {\n            Assert.assertTrue(col.isEmpty());\n        }\n        Assert.assertEquals(objects.length, col.size());\n        int i = 0;\n        for (Object o : col) {\n            Assert.assertEquals(objects[i++], o);\n        }\n    }\n\n    public void testSet() throws Exception {\n        SmallSet<Object> sut = new SmallSet<Object>();\n        sut.add(1);\n        Assert.assertEquals(1, sut.size());\n        Iterator<Object> iter = sut.iterator();\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(1, iter.next());\n        Assert.assertFalse(iter.hasNext());\n        assertListEquals(sut, 1);\n        try {\n            iter.next();\n            Assert.assertTrue(false);\n        } catch (Exception e) {\n        }\n\n        sut = new SmallSet<Object>();\n        sut.add(1);\n        Assert.assertEquals(1, sut.size());\n        iter = sut.iterator();\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(1, iter.next());\n        Assert.assertFalse(iter.hasNext());\n        assertListEquals(sut, 1);\n        iter.remove();\n        Assert.assertEquals(0, sut.size());\n        Assert.assertFalse(iter.hasNext());\n        iter = sut.iterator();\n        Assert.assertFalse(iter.hasNext());\n\n        sut = new SmallSet<Object>();\n        sut.add(1);\n        sut.add(2);\n        Assert.assertEquals(2, sut.size());\n        iter = sut.iterator();\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(1, iter.next());\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(2, iter.next());\n        Assert.assertFalse(iter.hasNext());\n        assertListEquals(sut, 1, 2);\n        iter.remove();\n        assertListEquals(sut, 1);\n        Assert.assertEquals(1, sut.size());\n        Assert.assertFalse(iter.hasNext());\n\n        sut = new SmallSet<Object>();\n        sut.add(1);\n        sut.add(2);\n        assertListEquals(sut, 1, 2);\n        Assert.assertEquals(2, sut.size());\n        iter = sut.iterator();\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(1, iter.next());\n        Assert.assertTrue(iter.hasNext());\n        iter.remove();\n        assertListEquals(sut, 2);\n        Assert.assertEquals(1, sut.size());\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(2, iter.next());\n        Assert.assertFalse(iter.hasNext());\n\n        sut = new SmallSet<Object>();\n        sut.add(1);\n        sut.add(2);\n        assertListEquals(sut, 1, 2);\n        Assert.assertEquals(2, sut.size());\n        iter = sut.iterator();\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(1, iter.next());\n        Assert.assertTrue(iter.hasNext());\n        iter.remove();\n        assertListEquals(sut, 2);\n        Assert.assertEquals(1, sut.size());\n        iter = sut.iterator();\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(2, iter.next());\n        Assert.assertFalse(iter.hasNext());\n\n        sut = new SmallSet<Object>();\n        sut.add(1);\n        sut.add(2);\n        assertListEquals(sut, 1, 2);\n        Assert.assertEquals(2, sut.size());\n        iter = sut.iterator();\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(1, iter.next());\n        Assert.assertTrue(iter.hasNext());\n        iter.remove();\n        assertListEquals(sut, 2);\n        Assert.assertEquals(1, sut.size());\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(2, iter.next());\n        iter.remove();\n        assertListEquals(sut);\n        iter = sut.iterator();\n        Assert.assertFalse(iter.hasNext());\n\n        sut = new SmallSet<Object>();\n        sut.add(1);\n        sut.add(2);\n        sut.add(3);\n        assertListEquals(sut, 1, 2, 3);\n        iter = sut.iterator();\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(1, iter.next());\n        assertListEquals(sut, 1, 2, 3);\n        iter.remove();\n        assertListEquals(sut, 2, 3);\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(2, iter.next());\n        iter.remove();\n        assertListEquals(sut, 3);\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(3, iter.next());\n        Assert.assertFalse(iter.hasNext());\n        iter.remove();\n        assertListEquals(sut);\n        Assert.assertFalse(iter.hasNext());\n        iter = sut.iterator();\n        Assert.assertFalse(iter.hasNext());\n\n        sut = new SmallSet<Object>();\n        sut.add(1);\n        sut.add(2);\n        sut.add(3);\n        assertListEquals(sut, 1, 2, 3);\n        iter = sut.iterator();\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(1, iter.next());\n        assertListEquals(sut, 1, 2, 3);\n        iter.remove();\n        assertListEquals(sut, 2, 3);\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(2, iter.next());\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(3, iter.next());\n        Assert.assertFalse(iter.hasNext());\n        iter.remove();\n        assertListEquals(sut, 2);\n        Assert.assertFalse(iter.hasNext());\n        iter = sut.iterator();\n        Assert.assertTrue(iter.hasNext());\n        Assert.assertEquals(2, iter.next());\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/util/SplitUtilTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport org.junit.Assert;\n\nimport org.junit.Test;\n\nimport io.mycat.util.SplitUtil;\n\n/**\n * @author mycat\n */\npublic class SplitUtilTest {\n\n    @Test\n    public void test() {\n        String str = \"mysql$1-3,mysql7,mysql9\";\n        String[] destStr = SplitUtil.split(str, ',', '$', '-');\n        Assert.assertEquals(5, destStr.length);\n        Assert.assertEquals(\"mysql1\", destStr[0]);\n        Assert.assertEquals(\"mysql2\", destStr[1]);\n        Assert.assertEquals(\"mysql3\", destStr[2]);\n        Assert.assertEquals(\"mysql7\", destStr[3]);\n        Assert.assertEquals(\"mysql9\", destStr[4]);\n    }\n\n    @Test\n    public void test1() {\n        String src = \"offer$0-3\";\n        String[] dest = SplitUtil.split(src, '$', true);\n        Assert.assertEquals(2, dest.length);\n        Assert.assertEquals(\"offer\", dest[0]);\n        Assert.assertEquals(\"0-3\", dest[1]);\n    }\n\n    @Test\n    public void test2() {\n        String src = \"OFFER_group\";\n        String[] dest = SplitUtil.split2(src, '$', '-');\n        Assert.assertEquals(1, dest.length);\n        Assert.assertEquals(\"OFFER_group\", dest[0]);\n    }\n\n    @Test\n    public void test3() {\n        String src = \"OFFER_group$2\";\n        String[] dest = SplitUtil.split2(src, '$', '-');\n        Assert.assertEquals(1, dest.length);\n        Assert.assertEquals(\"OFFER_group[2]\", dest[0]);\n    }\n\n    @Test\n    public void test4() {\n        String src = \"offer$0-3\";\n        String[] dest = SplitUtil.split2(src, '$', '-');\n        Assert.assertEquals(4, dest.length);\n        Assert.assertEquals(\"offer[0]\", dest[0]);\n        Assert.assertEquals(\"offer[1]\", dest[1]);\n        Assert.assertEquals(\"offer[2]\", dest[2]);\n        Assert.assertEquals(\"offer[3]\", dest[3]);\n    }\n\n    @Test\n    public void test5() {\n        Assert.assertNull(SplitUtil.split(null, '\\u0000'));\n        Assert.assertArrayEquals(new String[] {}, SplitUtil.split(\"\", '/'));\n        Assert.assertArrayEquals(new String[] {}, SplitUtil.split(\"/\", '/'));\n        Assert.assertArrayEquals(new String[] {\"/\"}, SplitUtil.split(\"/\", '\\\"'));\n        Assert.assertArrayEquals(new String[] {\"/\"}, SplitUtil.split(\"/\\\"\", '\\\"'));\n    }\n\n    @Test\n    public void test6() {\n        Assert.assertNull(SplitUtil.split(null, \"1\", 1));\n        Assert.assertArrayEquals(new String[] {}, SplitUtil.split(\"\", \"foo\", 1));\n        Assert.assertArrayEquals(new String[] {}, SplitUtil.split(\" \", null, 1));\n        Assert.assertArrayEquals(new String[] {\"foo bar\"}, SplitUtil.split(\"foo bar\", null, 1));\n        Assert.assertArrayEquals(new String[] {\"foo\", \"bar\"}, SplitUtil.split(\"foo bar\", null, 2));\n        Assert.assertArrayEquals(new String[] {}, SplitUtil.split(\"1\", \"1\", 1));\n        Assert.assertArrayEquals(new String[] {\"foo1bar\"}, SplitUtil.split(\"foo1bar\", \"1\", 1));\n        Assert.assertArrayEquals(new String[] {\"foo\", \"bar\"}, SplitUtil.split(\"foo1bar\", \"1\", 2));\n        Assert.assertArrayEquals(new String[] {\"foo11bar\"}, SplitUtil.split(\"foo11bar\", \"11\", 1));\n        Assert.assertArrayEquals(new String[] {\"foo\", \"bar\"}, SplitUtil.split(\"foo11bar\", \"11\", 2));\n    }\n\n    @Test\n    public void test7() {\n        Assert.assertNull(SplitUtil.split(null, '0', '0', '0', '0'));\n        Assert.assertArrayEquals(new String[] {}, SplitUtil.split(\"\", '0', '0', '0', '0'));\n        Assert.assertArrayEquals(new String[] {\"0-1-2\"}, SplitUtil.split(\"0-1-2\", '3', ' ', '0', '0'));\n        Assert.assertArrayEquals(new String[] {\"011\"}, SplitUtil.split(\"0-1-2\", '-', ' ', '1', '0'));\n        Assert.assertArrayEquals(new String[] {\"0111\"}, SplitUtil.split(\"0-1-2\", '-', ' ', '1', '1'));\n    }\n\n    @Test\n    public void test8() {\n        Assert.assertArrayEquals(new String[] {\"foo\"}, SplitUtil.splitByByteSize(\"foo\", 1));\n        Assert.assertArrayEquals(new String[] {\"f\"}, SplitUtil.splitByByteSize(\"f\", 2));\n        Assert.assertArrayEquals(new String[] {\"fo\", \"o\"}, SplitUtil.splitByByteSize(\"foo\", 2));\n        Assert.assertArrayEquals(new String[] {\"fo\", \"ob\", \"ar\"}, SplitUtil.splitByByteSize(\"foobar\", 2));\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/util/StringHashPerfMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class StringHashPerfMain {\n\n    public static void main(String[] args) {\n        String s = \"abcdejdsalfp\";\n        int end = s.length();\n        for (int i = 0; i < 10; i++) {\n            StringUtil.hash(s, 0, end);\n        }\n        long loop = 10000 * 10000;\n        long t1 = System.currentTimeMillis();\n        t1 = System.currentTimeMillis();\n        for (long i = 0; i < loop; ++i) {\n            StringUtil.hash(s, 0, end);\n        }\n        long t2 = System.currentTimeMillis();\n        System.out.println((((t2 - t1) * 1000 * 1000) / loop) + \" ns.\");\n    }\n\n}"
  },
  {
    "path": "src/test/java/io/mycat/util/StringUtilTest.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\n\nimport junit.framework.Assert;\n\nimport org.junit.Test;\n\nimport io.mycat.util.StringUtil;\n\n/**\n * @author mycat\n */\npublic class StringUtilTest {\n\n    @Test\n    public void test() {\n        String oriSql = \"insert into ssd  (id) values (s)\";\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"ssd\", tableName);\n    }\n\n    @Test\n    public void test1() {\n    \tString oriSql = \"insert into    ssd(id) values (s)\";\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"ssd\", tableName);\n    }\n\n    @Test\n    public void test2() {\n    \tString oriSql = \"  insert  into    ssd(id) values (s)\";\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"ssd\", tableName);\n    }\n\n    @Test\n    public void test3() {\n    \tString oriSql = \"  insert  into    isd(id) values (s)\";\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"isd\", tableName);\n    }\n\n    @Test\n    public void test4() {\n    \tString oriSql = \"INSERT INTO test_activity_input  (id,vip_no\";\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"test_activity_input\", tableName);\n    }\n    \n    @Test\n    public void test5() {\n    \tString oriSql = \" /* ApplicationName=DBeaver 3.3.1 - Main connection */ insert into employee(id,name,sharding_id) values(4, 'myhome', 10011)\";\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"employee\", tableName);\n    }\n    \n    @Test\n    public void test6() {\n    \tString oriSql = \" /* insert int a (id, name) value(1, 'ben') */ insert into employee(id,name,sharding_id) values(4, 'myhome', 10011)\";\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"employee\", tableName);\n    }\n    \n    @Test\n    public void test7() {\n    \tString oriSql = \" /**/ insert into employee(id,name,sharding_id) values(4, 'myhome', 10011)\";\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"employee\", tableName);\n    }\n    \n    @Test\n    public void test8() {\n    \tString oriSql = \" /*  */ insert into employee(id,name,sharding_id) values(4, 'myhome', 10011) /**/\";\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"employee\", tableName);\n    }\n    \n    @Test\n    public void test9() {\n    \tString oriSql = \" /* hint1 insert */ /**/ /* hint3 insert */ insert into employee(id,name,sharding_id) values(4, 'myhome', 10011) /**/\";\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"employee\", tableName);\n    }\n    \n    @Test\n    public void test10() {\n    \tString oriSql = \" /* hint1 insert */ /* // */ /* hint3 insert */ insert into employee(id,name,sharding_id) values(4, 'myhome', 10011) /**/\";\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"employee\", tableName);\n    }\n    \n    @Test\n    public void test11() {\n    \tString oriSql = \" /* hint1 insert */ /* // */ /* hint3 insert */ insert /*  */ into employee(id,name,sharding_id) values(4, 'myhome', 10011) /**/\";\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"employee\", tableName);\n    }\n    @Test\n    public void test12() {\n    \tStringWriter sw=new StringWriter();\n    \tPrintWriter pw=new PrintWriter(sw);\n    \tpw.println(\"insert into\");\n    \tpw.println(\" employee(id,name,sharding_id) values(4, 'myhome', 10011)\");\n    \tpw.flush();\n    \tString oriSql = sw.toString();\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"employee\", tableName);\n    }\n    @Test\n    public void test13() {\n    \tStringWriter sw=new StringWriter();\n    \tPrintWriter pw=new PrintWriter(sw);\n    \tpw.println(\"insert into\");\n    \tpw.println(\"employee(id,name,sharding_id) values(4, 'myhome', 10011)\");\n    \tpw.flush();\n    \tString oriSql = sw.toString();\n        String tableName = StringUtil.getTableName(oriSql);\n        Assert.assertEquals(\"employee\", tableName);\n    }\n}"
  },
  {
    "path": "src/test/java/io/mycat/util/SyncPerfMain.java",
    "content": "/*\n * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n *\n * This code is free software;Designed and Developed mainly by many Chinese \n * opensource volunteers. you can redistribute it and/or modify it under the \n * terms of the GNU General Public License version 2 only, as published by the\n * Free Software Foundation.\n *\n * This code is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n * version 2 for more details (a copy is included in the LICENSE file that\n * accompanied this code).\n *\n * You should have received a copy of the GNU General Public License version\n * 2 along with this work; if not, write to the Free Software Foundation,\n * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n * \n * Any questions about this component can be directed to it's project Web address \n * https://code.google.com/p/opencloudb/.\n *\n */\npackage io.mycat.util;\n\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * @author mycat\n */\npublic class SyncPerfMain {\n\n    long i = 0L;\n\n    private final Object lockA = new Object();\n    private final ReentrantLock lockB = new ReentrantLock();\n\n    final void tLockA() {\n        final Object lock = this.lockA;\n        synchronized (lock) {\n            i++;\n        }\n    }\n\n    final void tLockB() {\n        final ReentrantLock lock = this.lockB;\n        lock.lock();\n        try {\n            i++;\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public static void main(String[] args) {\n        int count = 10000000;\n        SyncPerfMain test = new SyncPerfMain();\n\n        System.currentTimeMillis();\n        long t1 = System.currentTimeMillis();\n        for (int i = 0; i < count; i++) {\n            test.tLockA();\n            // test.testLockB();\n        }\n        long t2 = System.currentTimeMillis();\n        System.out.println(\"take:\" + (t2 - t1) + \" ms.\");\n    }\n\n}"
  },
  {
    "path": "src/test/resources/autopartition-long-dupl.txt",
    "content": "# range start-end ,data node index\n0-1000=0\n1001-2000=1\n2001-3000=0\n3001-4000=1\n"
  },
  {
    "path": "src/test/resources/autopartition-long.txt",
    "content": "# range start-end ,data node index\n0-200M=0\n200M1-400M=1\n400M1-600M=2\n#600M1-800M=3\n#800M1-1000M=4\n"
  },
  {
    "path": "src/test/resources/autopartition-long2.txt",
    "content": "# range start-end ,data node index\n0-200M=0\n200M1-400M=1\n#400M1-600M=2\n#600M1-800M=3\n#800M1-1000M=4\n"
  },
  {
    "path": "src/test/resources/config/rule.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n -  \n - Licensed under the Apache License, Version 2.0 (the \"License\");\n - you may not use this file except in compliance with the License.\n - You may obtain a copy of the License at\n -  \n -      http://www.apache.org/licenses/LICENSE-2.0\n -  \n - Unless required by applicable law or agreed to in writing, software\n - distributed under the License is distributed on an \"AS IS\" BASIS,\n - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n - See the License for the specific language governing permissions and\n - limitations under the License.\n-->\n<!DOCTYPE mycat:rule SYSTEM \"rule.dtd\">\n<mycat:rule xmlns:mycat=\"http://io.mycat/\">\n\n  <!-- 路由规则定义，定义什么表，什么字段，采用什么路由算法 -->\n  <tableRule name=\"rule1\">\n    <rule>\n      <columns>id</columns>\n      <algorithm>func1</algorithm>\n    </rule>\n  </tableRule>\n\n  <!-- 路由函数定义 -->\n  <function name=\"func1\"\n    class=\"io.mycat.route.function.PartitionByLong\">\n    <property name=\"partitionCount\">2</property>\n    <property name=\"partitionLength\">512</property>\n  </function>\n\n</mycat:rule>\n"
  },
  {
    "path": "src/test/resources/config/schema.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n -  \n - Licensed under the Apache License, Version 2.0 (the \"License\");\n - you may not use this file except in compliance with the License.\n - You may obtain a copy of the License at\n -  \n -      http://www.apache.org/licenses/LICENSE-2.0\n -  \n - Unless required by applicable law or agreed to in writing, software\n - distributed under the License is distributed on an \"AS IS\" BASIS,\n - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n - See the License for the specific language governing permissions and\n - limitations under the License.\n-->\n<!DOCTYPE mycat:schema SYSTEM \"schema.dtd\">\n<mycat:schema xmlns:mycat=\"http://io.mycat/\">\n\n  <!-- schema定义 -->\n  <schema name=\"dbtest\">\n    <table name=\"tb2\" dataNode=\"dnTest2,dnTest3\" rule=\"rule1\" />\n  </schema>\n  \n  <schema name=\"dbtest1\">    \n   \t<!-- 动态日期表设置, 格式： yyyymm,年,月,往后月数 -->\n\t<!-- 动态日期表设置, 格式： yyyymmdd,年,月,日,wanghou天数 -->\n\t<table name=\"dynamic_\" nameSuffix=\"yyyymmdd,2015,08,01,15\" dataNode=\"dnTest2,dnTest3\" />\n\t<table name=\"dynamic_\" nameSuffix=\"yyyymm,2015,01,15\" dataNode=\"dnTest2,dnTest3\" />\n  </schema>\n  \n  <schema name=\"dbtest2\" checkSQLschema=\"false\" sqlMaxLimit=\"100\" dataNode=\"dnTest4\"></schema>\n\n\n  <!-- 数据节点定义，数据节点由数据源和其他一些参数组织而成。 -->\n  <dataNode name=\"dnTest1\" dataHost=\"localhost1\" database=\"db1\" /> \n  <dataNode name=\"dnTest2\" dataHost=\"localhost1\" database=\"db2\" />\n  <dataNode name=\"dnTest3\" dataHost=\"localhost1\" database=\"db3\" />\n  <dataNode name=\"dnTest4\" dataHost=\"localhost2\" database=\"db4\" />\n  \n  <dataHost name=\"localhost1\" maxCon=\"500\" minCon=\"10\" balance=\"0\"\n\t\tdbType=\"mysql\" dbDriver=\"native\" >\n\t\t<heartbeat>select user()</heartbeat>\n\t\t<!-- can have multi write hosts -->\n\t\t<writeHost host=\"hostM1\" url=\"localhost:3306\" user=\"root\"\n\t\t\tpassword=\"123456\">\n\t\t\t<!-- can have multi read hosts -->\n\t\t\t<!-- <readHost host=\"hostS1\" url=\"localhost:3307\" user=\"root\" password=\"123456\" \n\t\t\t\t/> -->\n\t\t</writeHost>\n\t</dataHost>\n\n\n\t<!-- 读权重设置 -->\n\t<dataHost name=\"localhost2\" maxCon=\"1000\" minCon=\"10\" balance=\"0\"\n\t\twriteType=\"0\" dbType=\"mysql\" dbDriver=\"native\" switchType=\"1\"  slaveThreshold=\"100\" tempReadHostAvailable=\"1\">\n\t\t<heartbeat>select user()</heartbeat>\t\t\n\t\t<writeHost host=\"writeS\" url=\"192.168.1.10:3306\" user=\"root\" password=\"123456\" >\n\t\t\t<!-- can have multi read hosts -->\n\t\t\t<readHost host=\"readS1\" url=\"192.168.1.11:3306\" user=\"root\" password=\"123456\" weight=\"1\" />\n\t\t\t<readHost host=\"readS2\" url=\"192.168.1.12:3306\" user=\"root\" password=\"123456\" weight=\"2\"/>\n\t\t\t<readHost host=\"readS3\" url=\"192.168.1.13:3306\" user=\"root\" password=\"123456\" weight=\"3\"/>\n\t\t</writeHost>\t\t\n\t</dataHost>\n \n</mycat:schema>\n"
  },
  {
    "path": "src/test/resources/ehcache.xml",
    "content": "<ehcache xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:noNamespaceSchemaLocation=\"ehcache.xsd\" maxEntriesLocalHeap=\"100000000\"\n\tmaxBytesLocalDisk=\"50G\">\n\t<defaultCache maxElementsInMemory=\"1000000\" eternal=\"false\"\n\t\toverflowToDisk=\"false\" diskSpoolBufferSizeMB=\"30\" maxElementsOnDisk=\"10000000\"\n\t\tdiskPersistent=\"false\" diskExpiryThreadIntervalSeconds=\"120\"\n\t\tmemoryStoreEvictionPolicy=\"LRU\" />\n</ehcache>"
  },
  {
    "path": "src/test/resources/log4j2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"INFO\">\n    <Appenders>\n        <Console name=\"Console\" target=\"SYSTEM_OUT\">\n            <PatternLayout pattern=\"%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%t] (%l) - %m%n\"/>\n        </Console>\n\n        <RollingFile name=\"RollingFile\" fileName=\"logs/mycat.log\"\n                     filePattern=\"logs/$${date:yyyy-MM}/mycat-%d{MM-dd}-%i.log.gz\">\n            <PatternLayout>\n                <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%t] (%l) - %m%n</Pattern>\n            </PatternLayout>\n            <Policies>\n                <OnStartupTriggeringPolicy/>\n                <SizeBasedTriggeringPolicy size=\"250 MB\"/>\n                <TimeBasedTriggeringPolicy/>\n            </Policies>\n        </RollingFile>\n    </Appenders>\n    <Loggers>\n        <AsyncLogger name=\"io.mycat\" level=\"info\" includeLocation=\"true\" additivity=\"false\">\n            <AppenderRef ref=\"Console\"/>\n            <AppenderRef ref=\"RollingFile\"/>\n        </AsyncLogger>\n        <asyncRoot level=\"warn\" includeLocation=\"true\">\n\n            <AppenderRef ref=\"Console\" />\n            <AppenderRef ref=\"RollingFile\"/>\n\n        </asyncRoot>\n    </Loggers>\n</Configuration>\n"
  },
  {
    "path": "src/test/resources/partition-pattern.txt",
    "content": "# id partition range start-end ,data node index\n###### first host configuration\n1-32=0\n33-64=1\n65-96=2\n97-128=3\n######## second host configuration\n129-160=4\n161-192=5\n193-224=6\n225-256=7\n0-0=7"
  },
  {
    "path": "src/test/resources/partition-range-mod.txt",
    "content": "# range start-end ,data node group size\n0-200M=5\n200M1-400M=1\n400M1-600M=4\n600M1-800M=4\n800M1-1000M=6\n"
  },
  {
    "path": "src/test/resources/partition_prefix_pattern.txt",
    "content": "# range start-end ,data node index\n# ASCII编码：主要划分出10个数字，小字母26，一共36个字母进行分片\n# 48-57=0-9阿拉伯数字\n# 64、65-90=@、A-Z \n# 97-122=a-z\n###### first host configuration\n1-4=0\n5-8=1\n9-12=2\n13-16=3\n###### second host configuration\n17-20=4\n21-24=5\n25-28=6\n29-32=7\n0-0=7\n"
  },
  {
    "path": "src/test/resources/route/rule.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- - - Licensed under the Apache License, Version 2.0 (the \"License\"); \n\t- you may not use this file except in compliance with the License. - You \n\tmay obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 \n\t- - Unless required by applicable law or agreed to in writing, software - \n\tdistributed under the License is distributed on an \"AS IS\" BASIS, - WITHOUT \n\tWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the \n\tLicense for the specific language governing permissions and - limitations \n\tunder the License. -->\n<!DOCTYPE mycat:rule SYSTEM \"rule.dtd\">\n<mycat:rule xmlns:mycat=\"http://io.mycat/\">\n\n\t<tableRule name=\"offerRule\">\n\t\t<rule>\n\t\t\t<columns>member_id</columns>\n\t\t\t<algorithm>func</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t\n\t<tableRule name=\"dateRule\">\n\t\t<rule>\n\t\t\t<columns>col_date</columns>\n\t\t\t<algorithm>by-date</algorithm>\n\t\t</rule>\n\t</tableRule>\n\n\t<tableRule name=\"offerDetailRule\">\n\t\t<rule>\n\t\t\t<columns>offer_id</columns>\n\t\t\t<algorithm>func2</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"sharding-by-intfile\">\n\t\t<rule>\n\t\t\t<columns>sharding_id</columns>\n\t\t\t<algorithm>func1</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"auto-sharding-long\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>rang-long</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t<tableRule name=\"auto-sharding-long2\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>rang-long2</algorithm>\n\t\t</rule>\n\t</tableRule>\n\n    <tableRule name=\"auto-sharding-rang-mod\">\n        <rule>\n            <columns>id</columns>\n            <algorithm>rang-mod</algorithm>\n        </rule>\n    </tableRule>\n    \n    <tableRule name=\"partitionByMod\">\n    \t<rule>\n    \t\t<columns>id</columns>\n    \t\t<algorithm>partitionByMod</algorithm>\n    \t</rule>\n    </tableRule>\n    \n    <tableRule name=\"rangeDateHash\">\n        <rule>\n            <columns>col_date</columns>\n            <algorithm>range-date-hash</algorithm>\n        </rule>\n    </tableRule>\n\t<tableRule name=\"crc32slot\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>crc32slot</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t\n\t<tableRule name=\"mod-long\">\n\t\t<rule>\n\t\t\t<columns>id</columns>\n\t\t\t<algorithm>mod-long</algorithm>\n\t\t</rule>\n\t</tableRule>\n\t\n\t<function name=\"partitionByMod\" class=\"io.mycat.route.function.PartitionByMod\">\n\t\t<property name=\"count\">2</property>\n\t</function>\n\t<function name=\"func1\"\n\t\tclass=\"io.mycat.route.function.PartitionByFileMap\">\n\t\t<property name=\"defaultNode\">0</property>\n\t\t<property name=\"mapFile\">sharding.txt</property>\n\t</function>\n\t<function name=\"func\"\n\t\tclass=\"io.mycat.route.function.PartitionByString\">\n\t\t<property name=\"partitionCount\">128</property>\n\t\t<property name=\"partitionLength\">8</property>\n\t\t<property name=\"hashSlice\">:8</property>\n\t</function>\n\t<function name=\"func2\" class=\"io.mycat.route.function.PartitionByLong\">\n\t\t<property name=\"partitionCount\">128</property>\n\t\t<property name=\"partitionLength\">8</property>\n\t</function>\n\t<function name=\"rang-long\"\n\t\tclass=\"io.mycat.route.function.AutoPartitionByLong\">\n\t\t<property name=\"mapFile\">autopartition-long.txt</property>\n\t</function>\n\t<function name=\"rang-long2\"\n\t\tclass=\"io.mycat.route.function.AutoPartitionByLong\">\n\t\t<property name=\"mapFile\">autopartition-long2.txt</property>\n\t</function>\n\t<function name=\"by-date\"\n\t\tclass=\"io.mycat.route.function.PartitionByDate\">                            \n\t\t<property name=\"sBeginDate\">2014-01-01</property>\n\t\t<property name=\"sPartionDay\">10</property>\n\t\t<property name=\"dateFormat\">yyyy-MM-dd</property>\n\t</function>\n\n    <function name=\"rang-mod\"\n              class=\"io.mycat.route.function.PartitionByRangeMod\">\n        <property name=\"mapFile\">partition-range-mod.txt</property>\n    </function>\n\n    <function name=\"range-date-hash\"\n              class=\"io.mycat.route.function.PartitionByRangeDateHash\">\n        <property name=\"sBeginDate\">2014-01-01 00:00:00</property>\n        <property name=\"sPartionDay\">3</property>\n        <property name=\"dateFormat\">yyyy-MM-dd HH:mm:ss</property>\n        <property name=\"groupPartionSize\">6</property>\n    </function>\n\n\t<function name=\"crc32slot\"\n\t\t\t  class=\"io.mycat.route.function.PartitionByCRC32PreSlot\">\n\t</function>\n\t\n\t<function name=\"mod-long\" class=\"io.mycat.route.function.PartitionByMod\">\n\t\t\t<!-- how many data nodes -->\n\t\t\t<property name=\"count\">2</property>\n\t</function>\n</mycat:rule>\n"
  },
  {
    "path": "src/test/resources/route/schema.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- - - Licensed under the Apache License, Version 2.0 (the \"License\"); \n\t- you may not use this file except in compliance with the License. - You \n\tmay obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 \n\t- - Unless required by applicable law or agreed to in writing, software - \n\tdistributed under the License is distributed on an \"AS IS\" BASIS, - WITHOUT \n\tWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the \n\tLicense for the specific language governing permissions and - limitations \n\tunder the License. -->\n<!DOCTYPE mycat:schema SYSTEM \"schema.dtd\">\n<mycat:schema xmlns:mycat=\"http://io.mycat/\">\n\n\t<schema name=\"cndb\" checkSQLschema=\"true\" >\n\t\t<table name=\"offer\" dataNode=\"offer_dn$0-127\" rule=\"offerRule\" />\n\t\t<table name=\"offer_detail\" dataNode=\"detail_dn0,detail_dn$1-127\"\n\t\t\trule=\"offerDetailRule\" ruleRequired=\"false\" />\n\t\t<table name=\"offer_date\" dataNode=\"detail_dn0,detail_dn$1-127\"\n\t\t\trule=\"dateRule\" ruleRequired=\"false\" />\n\n\t\t<table name=\"independent\" dataNode=\"independent_dn$0-126,independent_dn127\" />\n\t</schema>\n\t<schema name=\"dubbo\" dataNode=\"dubbo_dn\" />\n    <schema name=\"dubbo2\" dataNode=\"dn1\" >\n        <table name=\"company\" type=\"global\" dataNode=\"dn1,dn2,dn3\" />\n        <table name=\"goods\" type=\"global\" dataNode=\"dn1,dn2,dn3\" />\n            </schema>\n\t<schema name=\"ignoreSchemaTest\" dataNode=\"cndb_dn\" checkSQLschema=\"true\"/>\n\t<schema name=\"config\">\n\t\t<table name=\"offer\" dataNode=\"offer_dn$0-127\" rule=\"offerRule\"\n\t\t\truleRequired=\"true\" />\n\t</schema>\n\t<schema name=\"none_node_test\">\n\t\t<table name=\"offer\" dataNode=\"offer_dn$0-127\" rule=\"offerRule\"\n\t\t\truleRequired=\"true\" />\n\t</schema>\n\t<schema name=\"solo1\" dataNode=\"solo1\" />\n\n\t<schema name=\"TESTDB\"  sqlMaxLimit=\"100\">\n\t\t<table name=\"travelrecord\" dataNode=\"dn1,dn2,dn3\" rule=\"auto-sharding-long\" />\n\t\t<!-- global table is auto cloned to all defined data nodes ,so can join \n\t\t\twith any table whose sharding node is in the same data node -->\n\t\t<table name=\"company\" type=\"global\" dataNode=\"dn1,dn2,dn3\" />\n\t\t<table name=\"goods\" type=\"global\" dataNode=\"dn1,dn2,dn3\" />\n\t\t<table name=\"notpartionTable\"  dataNode=\"dn1\" />\n\t\t<table name=\"area\" primaryKey=\"ID\" type=\"global\" dataNode=\"dn1,dn2,dn3\" />\n\t\t<table name=\"employee\" primaryKey=\"id\" dataNode=\"dn1,dn2\"\n\t\t\trule=\"sharding-by-intfile\" />\n\t\t<table name=\"customer\" dataNode=\"dn1,dn2\" rule=\"auto-sharding-long2\">\n\t\t\t<childTable name=\"orders\" joinKey=\"customer_id\" parentKey=\"id\">\n\t\t\t\t<childTable name=\"order_items\" joinKey=\"order_id\"\n\t\t\t\t\tparentKey=\"id\" />\n\t\t\t</childTable>\n\t\t\t<childTable name=\"customer_addr\" joinKey=\"customer_id\"\n\t\t\t\tparentKey=\"id\" />\n\t\t</table>\n\t\t<table name=\"globalsn\" primaryKey=\"ID\" type=\"global\" dataNode=\"dn1\" />\n        <table name=\"offer\" primaryKey=\"id\" dataNode=\"offer_dn$1-20\"\n               rule=\"auto-sharding-rang-mod\" />\n\n        <table name=\"offer1\" primaryKey=\"id\" dataNode=\"offer_dn$1-36\"\n               rule=\"rangeDateHash\" />\n\t</schema>\n    <schema name=\"schema_table_test\"  dataNode=\"dn2\"  checkSQLschema=\"true\">\n        <table name=\"offer\" dataNode=\"dn1,dn2\" rule=\"auto-sharding-long2\" />\n\t </schema>\n\t \n    <schema name=\"mysqldb\"  sqlMaxLimit=\"100\">\n        <table name=\"offer\" dataNode=\"dn1,dn2\" rule=\"auto-sharding-long2\" />\n\t\t<table name=\"autoslot\" dataNode=\"dn1,dn2\" rule=\"crc32slot\" />\n        <table name=\"offer1\" dataNode=\"dn1\" />\n    </schema>\n    <schema name=\"oracledb\"  sqlMaxLimit=\"100\">\n        <table name=\"offer\" dataNode=\"d_oracle1,d_oracle2\" rule=\"auto-sharding-long2\" />\n        <table name=\"offer1\" dataNode=\"d_oracle1\" />\n    </schema>\n\n\t<schema name=\"db2db\"  sqlMaxLimit=\"100\">\n\t\t<table name=\"offer\" dataNode=\"db2_1,db2_2\" rule=\"auto-sharding-long2\" />\n\t\t<table name=\"offer1\" dataNode=\"db2_1\" />\n\t</schema>\n\n\n\t<schema name=\"sqlserverdb\"  sqlMaxLimit=\"100\">\n\t\t<table name=\"offer\" dataNode=\"sqlserver_1,sqlserver_2\" rule=\"auto-sharding-long2\" />\n\t\t<table name=\"offer1\" dataNode=\"sqlserver_1\" />\n\t</schema>\n\n\n\t<schema name=\"pgdb\"  sqlMaxLimit=\"100\">\n\t\t<table name=\"offer\" dataNode=\"pg_1,pg_2\" rule=\"auto-sharding-long2\" />\n\t\t<table name=\"offer1\" dataNode=\"pg_1\" />\n\t</schema>\n\t\n\t<schema name=\"subQueries\" sqlMaxLimit=\"100\">\n\t\t<table name=\"table_0\" dataNode=\"dn1,dn2\" type=\"global\"/><!-- 全局表    -->\n\t\t<table name=\"table_1\" dataNode=\"dn1\"/>          <!-- 非分片表 -->\n\t\t<table name=\"subtest1\" dataNode=\"dn1,dn2\" rule=\"mod-long\" />\n\t\t<table name=\"subtest2\" dataNode=\"dn1,dn2,dn3\" rule=\"auto-sharding-long\" />\n\t\t<table name=\"subtest3\" dataNode=\"dn2,dn3\" rule=\"mod-long\" />\n\t\t<table name=\"subtest4\" dataNode=\"dn1,dn2\" rule=\"mod-long\" />\n\t</schema>\n\n\t<dataNode name=\"dn1\" dataHost=\"localhost1\" database=\"db1\" />\n\t<dataNode name=\"dn2\" dataHost=\"localhost1\" database=\"db2\" />\n\t<dataNode name=\"dn3\" dataHost=\"localhost1\" database=\"db3\" />\n\t<dataNode name=\"cndb_dn\" dataHost=\"localhost1\" database=\"db4\" />\n\t<dataNode name=\"offer_dn$0-127\" dataHost=\"localhost1\" database=\"db1$0-127\" />\n\t<dataNode name=\"detail_dn$0-127\" dataHost=\"localhost1\" database=\"db2$0-127\" />\n    <dataNode name=\"test_wild1$1-3\" dataHost=\"localhost$1-3\" database=\"db1\" />\n    <dataNode name=\"test_wild$1-6\" dataHost=\"localhost$1-3\" database=\"db1$1-2\" />\n\t<dataNode name=\"independent_dn$0-127\" dataHost=\"localhost1\"\n\t\tdatabase=\"db7_$0-127\" />\n\t<dataNode name=\"dubbo_dn\" dataHost=\"localhost1\" database=\"db8\" />\n\t<dataNode name=\"solo1\" dataHost=\"localhost1\" database=\"db9\" />\n\n    <dataNode name=\"d_oracle1\" dataHost=\"oracle1\" database=\"base\" />\n    <dataNode name=\"d_oracle2\" dataHost=\"oracle2\" database=\"bwg\" />\n\n\t<dataNode name=\"db2_1\" dataHost=\"db21\" database=\"base\" />\n\t<dataNode name=\"db2_2\" dataHost=\"db22\" database=\"test\" />\n\n\t<dataNode name=\"sqlserver_1\" dataHost=\"ms1\" database=\"base\" />\n\t<dataNode name=\"sqlserver_2\" dataHost=\"ms2\" database=\"test\" />\n\n\t<dataNode name=\"pg_1\" dataHost=\"pg1\" database=\"base\" />\n\t<dataNode name=\"pg_2\" dataHost=\"pg2\" database=\"test\" />\n\n\t<dataHost name=\"localhost1\" maxCon=\"500\" minCon=\"10\" balance=\"0\"\n\t\tdbType=\"mysql\" dbDriver=\"native\">\n\t\t<heartbeat>select user()</heartbeat>\n\t\t<!-- can have multi write hosts -->\n\t\t<writeHost host=\"hostM1\" url=\"localhost:3306\" user=\"root\"\n\t\t\tpassword=\"123456\">\n\t\t\t<!-- can have multi read hosts -->\n\t\t\t<!-- <readHost host=\"hostS1\" url=\"localhost:3307\" user=\"root\" password=\"123456\" \n\t\t\t\t/> -->\n\t\t</writeHost>\n\t</dataHost>\n    <dataHost name=\"localhost2\" maxCon=\"500\" minCon=\"10\" balance=\"0\"\n              dbType=\"mysql\" dbDriver=\"native\">\n        <heartbeat>select user()</heartbeat>\n        <!-- can have multi write hosts -->\n        <writeHost host=\"hostM2\" url=\"localhost:3306\" user=\"root\"\n                   password=\"123456\">\n            <!-- can have multi read hosts -->\n            <!-- <readHost host=\"hostS1\" url=\"localhost:3307\" user=\"root\" password=\"123456\"\n                /> -->\n        </writeHost>\n    </dataHost>\n    <dataHost name=\"localhost3\" maxCon=\"500\" minCon=\"10\" balance=\"0\"\n              dbType=\"mysql\" dbDriver=\"native\">\n        <heartbeat>select user()</heartbeat>\n        <!-- can have multi write hosts -->\n        <writeHost host=\"hostM3\" url=\"localhost:3306\" user=\"root\"\n                   password=\"123456\">\n            <!-- can have multi read hosts -->\n            <!-- <readHost host=\"hostS1\" url=\"localhost:3307\" user=\"root\" password=\"123456\"\n                /> -->\n        </writeHost>\n    </dataHost>\n\n\n    <dataHost name=\"oracle1\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" writeType=\"0\" dbType=\"oracle\" dbDriver=\"jdbc\">\n        <heartbeat>select 1 from dual</heartbeat>\n        <connectionInitSql>alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'</connectionInitSql>\n        <writeHost host=\"ohostM1\" url=\"jdbc:oracle:thin:@127.0.0.1:1521:nange\" user=\"base\" password=\"123456\" >\n        </writeHost>\n    </dataHost>\n\n    <dataHost name=\"oracle2\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" writeType=\"0\" dbType=\"oracle\" dbDriver=\"jdbc\">\n        <heartbeat>select 1 from dual</heartbeat>\n        <connectionInitSql>alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'</connectionInitSql>\n        <writeHost host=\"ohostM2\" url=\"jdbc:oracle:thin:@127.0.0.1:1521:nange\" user=\"bwg\" password=\"123456\" >\n        </writeHost>\n    </dataHost>\n\n\n\t<dataHost name=\"db21\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" writeType=\"0\" dbType=\"db2\" dbDriver=\"jdbc\">\n\t\t<heartbeat>select 1 from SYSIBM.SYSDUMMY1</heartbeat>\n\t\t<writeHost host=\"dhostM1\" url=\"jdbc:db2://127.0.0.1:50000/base\" user=\"db2inst1\" password=\"123\" >\n\t\t</writeHost>\n\t</dataHost>\n\n\n\t<dataHost name=\"db22\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" writeType=\"0\" dbType=\"db2\" dbDriver=\"jdbc\">\n\t\t<heartbeat>select 1 from SYSIBM.SYSDUMMY1</heartbeat>\n\t\t<writeHost host=\"dhostM2\" url=\"jdbc:db2://127.0.0.1:50000/test\" user=\"db2inst1\" password=\"123\" >\n\t\t</writeHost>\n\t</dataHost>\n\n\n\n\t<dataHost name=\"ms1\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" writeType=\"0\" dbType=\"sqlserver\" dbDriver=\"jdbc\">\n\t\t<heartbeat>select 1 </heartbeat>\n\t\t<writeHost host=\"mhostM1\" url=\"jdbc:sqlserver://127.0.0.1:1433;DatabaseName=base\" user=\"base\" password=\"123\" >\n\t\t</writeHost>\n\t</dataHost>\n\n\t<dataHost name=\"ms2\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" writeType=\"0\" dbType=\"sqlserver\" dbDriver=\"jdbc\">\n\t\t<heartbeat>select 1 </heartbeat>\n\t\t<writeHost host=\"mhostM2\" url=\"jdbc:sqlserver://127.0.0.1:1433;DatabaseName=test\" user=\"base\" password=\"123\" >\n\t\t</writeHost>\n\t</dataHost>\n\t<dataHost name=\"pg1\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" writeType=\"0\" dbType=\"postgresql\" dbDriver=\"jdbc\">\n\t\t<heartbeat>select 1</heartbeat>\n\t\t<writeHost host=\"phostM1\" url=\"jdbc:postgresql://127.0.0.1:5432/base\" user=\"test\" password=\"123\" >\n\t\t</writeHost>\n\t</dataHost>\n\n\t<dataHost name=\"pg2\" maxCon=\"1000\" minCon=\"1\" balance=\"0\" writeType=\"0\" dbType=\"postgresql\" dbDriver=\"jdbc\">\n\t\t<heartbeat>select 1 </heartbeat>\n\t\t<writeHost host=\"phostM2\" url=\"jdbc:postgresql://127.0.0.1:5432/test\" user=\"test\" password=\"123\" >\n\t\t</writeHost>\n\t</dataHost>\n\n</mycat:schema>\n"
  },
  {
    "path": "src/test/resources/rule.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n -  \n - Licensed under the Apache License, Version 2.0 (the \"License\");\n - you may not use this file except in compliance with the License.\n - You may obtain a copy of the License at\n -  \n -      http://www.apache.org/licenses/LICENSE-2.0\n -  \n - Unless required by applicable law or agreed to in writing, software\n - distributed under the License is distributed on an \"AS IS\" BASIS,\n - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n - See the License for the specific language governing permissions and\n - limitations under the License.\n-->\n<!DOCTYPE mycat:rule SYSTEM \"rule.dtd\">\n<mycat:rule xmlns:mycat=\"http://io.mycat/\">\n\n  <!-- 路由规则定义，定义什么表，什么字段，采用什么路由算法 -->\n  <tableRule name=\"rule1\">\n    <rule>\n      <columns>id</columns>\n      <algorithm>func1</algorithm>\n    </rule>\n  </tableRule>\n\n  <!-- 路由函数定义 -->\n  <function name=\"func1\"\n    class=\"io.mycat.route.function.PartitionByLong\">\n    <property name=\"partitionCount\">2</property>\n    <property name=\"partitionLength\">512</property>\n  </function>\n\n</mycat:rule>\n"
  },
  {
    "path": "src/test/resources/schema.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n -  \n - Licensed under the Apache License, Version 2.0 (the \"License\");\n - you may not use this file except in compliance with the License.\n - You may obtain a copy of the License at\n -  \n -      http://www.apache.org/licenses/LICENSE-2.0\n -  \n - Unless required by applicable law or agreed to in writing, software\n - distributed under the License is distributed on an \"AS IS\" BASIS,\n - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n - See the License for the specific language governing permissions and\n - limitations under the License.\n-->\n<!DOCTYPE mycat:schema SYSTEM \"schema.dtd\">\n<mycat:schema xmlns:mycat=\"http://io.mycat/\">\n\n  <!-- schema定义 -->\n  <schema name=\"dbtest\">\n    <table name=\"tb2\" dataNode=\"dnTest2,dnTest3\" rule=\"rule1\" />\n  </schema>\n\n  <!-- 数据节点定义，数据节点由数据源和其他一些参数组织而成。 -->\n  <dataNode name=\"dnTest1\" dataHost=\"localhost1\" database=\"db1\" /> \n  <dataNode name=\"dnTest2\" dataHost=\"localhost1\" database=\"db2\" />\n  <dataNode name=\"dnTest3\" dataHost=\"localhost1\" database=\"db3\" />\n<dataHost name=\"localhost1\" maxCon=\"500\" minCon=\"10\" balance=\"0\"\n\t\tdbType=\"mysql\" dbDriver=\"native\" >\n\t\t<heartbeat>select user()</heartbeat>\n\t\t<!-- can have multi write hosts -->\n\t\t<writeHost host=\"hostM1\" url=\"localhost:3306\" user=\"root\"\n\t\t\tpassword=\"123456\">\n\t\t\t<!-- can have multi read hosts -->\n\t\t\t<!-- <readHost host=\"hostS1\" url=\"localhost:3307\" user=\"root\" password=\"123456\" \n\t\t\t\t/> -->\n\t\t</writeHost>\n\t</dataHost>\n\n \n</mycat:schema>\n"
  },
  {
    "path": "src/test/resources/sequence_conf.properties",
    "content": "GLOBAL.HISIDS=\nGLOBAL.MINID=1\nGLOBAL.MAXID=10\nGLOBAL.CURID=1\n\nMY1.HISIDS=\nMY1.MINID=1001\nMY1.MAXID=2000\nMY1.CURID=1000\n\n\n"
  },
  {
    "path": "src/test/resources/server.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n\n -  \n - Licensed under the Apache License, Version 2.0 (the \"License\");\n - you may not use this file except in compliance with the License.\n - You may obtain a copy of the License at\n -  \n -      http://www.apache.org/licenses/LICENSE-2.0\n -  \n - Unless required by applicable law or agreed to in writing, software\n - distributed under the License is distributed on an \"AS IS\" BASIS,\n - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n - See the License for the specific language governing permissions and\n - limitations under the License.\n-->\n<!DOCTYPE mycat:server SYSTEM \"server.dtd\">\n<mycat:server xmlns:mycat=\"http://io.mycat/\">\n  \n  <!-- 系统参数定义，服务端口、管理端口，处理器个数、线程池等。 -->\n  <!--\n  <system>\n    <property name=\"serverPort\">8066</property>\n    <property name=\"managerPort\">9066</property>\n    <property name=\"initExecutor\">16</property>\n    <property name=\"timerExecutor\">4</property>\n    <property name=\"managerExecutor\">4</property>\n    <property name=\"processors\">4</property>\n    <property name=\"processorHandler\">8</property>\n    <property name=\"processorExecutor\">8</property>\n    <property name=\"clusterHeartbeatUser\">_HEARTBEAT_USER_</property>\n    <property name=\"clusterHeartbeatPass\">_HEARTBEAT_PASS_</property>\n  </system>\n  -->\n\n  <!-- 用户访问定义，用户名、密码、schema等信息。 -->\n  <user name=\"test\">\n    <property name=\"password\">test</property>\n    <property name=\"schemas\">dbtest</property>    \n    <property name=\"benchmark\">11111</property>   \n    <!-- benchmark 基准, 当前端的整体connection数达到基准值是, 对来自该账户的请求开始拒绝连接，0或不设表示不限制 -->\n  </user>\n  <!--\n  <user name=\"root\">\n    <property name=\"password\"></property>\n  </user>\n  -->\n\n  <!-- 集群列表定义，指定集群节点的主机和权重，用于集群间的心跳和客户端负载均衡。 -->\n  <cluster>\n    <node name=\"daas1\">\n      <property name=\"host\">127.0.0.1</property>\n      <property name=\"weight\">1</property>\n    </node>\n  </cluster>\n   \n  <!-- 隔离区定义，可以限定某个主机上只允许某个用户登录。 -->\n  <!--\n  <quarantine>\n    <host name=\"1.2.3.4\">\n      <property name=\"user\">test</property>\n    </host>\n  </quarantine>\n  -->\n\n</mycat:server>\n"
  },
  {
    "path": "src/test/resources/sharding.txt",
    "content": "10000=0\n10010=1"
  },
  {
    "path": "src/test/resources/zk-create-test.yaml",
    "content": "zkURL : 127.0.0.1:2181\nmycat-cluster:\n  mycat-cluster-1:\n    blockSQLs:\n      sql1 :\n        name : sql1\n      sql2 :\n        name : sql2\n      sql3 :\n        name : sql3\n    user :\n      test :\n        name : test\n        password : admin\n        readOnly : true\n        schemas  :\n          - testdb\n          - test\n      mycat :\n        name: mycat\n        password: admin\n        readOnly : false\n        schemas:\n          - testdb\n    rule :\n      sharding-by-enum :\n        name : sharding-by-enum\n        functionName : io.mycat.route.function.PartitionByFileMap\n        column : create_time\n        defaultnode : 0\n        type : 0\n        config :\n          10000 : 0\n          10010 : 1\n      sharding-by-hour :\n        name : sharding-by-hour\n        functionName : io.mycat.route.function.LatestMonthPartion\n        column : createTime\n        splitOneDay : 24\n\n      auto-sharding-long :\n       name : auto-sharding-long\n       column : id\n       functionName : io.mycat.route.function.AutoPartitionByLong\n       defaultNode : 0\n       config :\n             0-2000000 : 0\n             2000001-4000000 : 1\n             4000001-8000000 : 2\n\n      sharding-by-mod :\n       name : sharding-by-mod\n       column : id\n       functionName : io.mycat.route.function.PartitionByMod\n       count : 3\n\n      auto-sharding-rang-mod :\n       name : auto-sharding-rang-mod\n       column : id\n       functionName : io.mycat.route.function.PartitionByRangeMod\n       defaultNode : 21\n       config :\n             0-200M : 5\n             200M1-400M : 1\n             400M1-600M : 4\n             600M1-800M : 4\n             800M1-1000M : 6\n\n      auto-sharding-rang-mod :\n       name : sharding-by-RangeDateHash\n       column : create_time\n       functionName : io.mycat.route.function.PartitionByRangeDateHash\n       sBeginDate : \"2014-01-01 00:00:00\"\n       sPartionDay : 3\n       dateFormat : yyyy-MM-dd HH:mm:ss\n       groupPartionSize : 6\n\n    sequence:\n      sequence-3 :\n        current_value : 100000\n        increament : 100\n      sequence-2 :\n        workid: 1\n        centerid : 2\n      sequence-0 :\n        type : file\n      sequence-1 :\n        type : 1\n        config :\n          current_value : 100000\n          increament : 100\n        sequence-mapping :\n          T_NODE :  0\n\n    schema :\n      TESTDB :\n        name : TESTDB\n        checkSQLSchema : false\n        defaultMaxLimit : 100\n\n        travelrecord :\n          name :  travelrecord\n          datanode :  dn1,dn2,dn3\n          ruleName :  auto-sharding-long\n\n        company :\n          name :  company\n          datanode :  dn1,dn2,dn3\n          primaryKey : ID\n          type : 1  #全局表为 1\n\n        goods :\n          name :  goods\n          datanode :  dn1,dn2\n          primaryKey : ID\n          type : 1  #全局表为 1\n\n        hotnews :\n          name :  hotnews\n          datanode :  dn1,dn2,dn3\n          primaryKey : ID\n          ruleName : sharding-by-mod\n\n        employee :\n          name :  employee\n          datanode :  dn1,dn2\n          primaryKey : ID\n          ruleName : sharding-by-enum\n\n        customer :\n          name :  customer\n          datanode :  dn1,dn2\n          primaryKey : ID\n          ruleName : sharding-by-enum\n\n          orders :\n            name :  orders\n            primaryKey :  ID\n            joinKey : customer_id\n            parentKey : ID\n\n            order_items :\n              name :  order_items\n              joinKey : order_id\n              parentKey : ID\n\n          customer_addr :\n            name :  customer_addr\n            joinKey : customer_id\n            parentKey : ID\n\n        offer :\n          name :  offer\n          datanode :  offer_dn$1-20\n          primaryKey : id\n          ruleName : auto-sharding-rang-mod\n\n        offer1 :\n          name :  offer1\n          datanode :  offer_dn$1-36\n          primaryKey : id\n          ruleName : sharding-by-RangeDateHash\n\n    datanode :\n      dn1:\n        name : dn1\n        dataHost : localhost1\n        database : db1\n      dn2:\n        name : dn2\n        dataHost : localhost1\n        database : db2\n      dn3:\n        name : dn3\n        dataHost : localhost1\n        database : db3\n      offer_dn$0-127:\n        name : offer_dn$0-127\n        dataHost : localhost1\n        database : db1$0-127\n\n    datahost :\n      localhost1 :\n        name : localhost1\n        maxcon : 1000\n        mincon : 10\n        balance : 0\n        writetype : 0\n        dbtype : mysql\n        dbDriver : native\n        switchType : 1\n        slaveThreshold : 100\n        heartbeatSQL : select user()\n        mysqlGroup : mysql_rep_1\n\n#集群中所有的主机信息\nmycat-hosts:\n  fz_vm1:\n    hostname: fz_vm1\n    ip: 192.168.10.2\n    root: root\n    password: admin\n\nmycat-zones:\n  wh:\n    name: 武汉中心\n  fz: 福州中心\n\n#zone内mycat 实例配置,名字为mycat实例的,myid.\nmycat-nodes:\n  mycat_fz_01:\n    name: mycat_fz_01\n    hostname: fz_vm1\n    zone: fz\n    cluster: mycat-cluster-1\n    weigth: 1\n    leader: 1\n    state: red\n    systemParams:\n      defaultsqlparser : druidparser\n      serverport : 8066\n      sequncehandlertype : 1\n\nmycat-mysqls:\n  mysql_1:\n    ip: 192.168.8.2\n    port: 3306\n    user: mysql\n    password: mysql\n    hostId: host\n    zone: bj\n  mysql_2:\n    ip: 192.168.8.3\n    port: 3307\n    user: mysql\n    password: mysql\n    hostId: host\n    zone: bj\n  mysql_3:\n    ip: 192.168.8.4\n    port: 3308\n    user: mysql\n    password: mysql\n    hostId: host\n    zone: bj\n\nmycat-mysqlgroup :\n  mysql_rep_1:\n    name: mysql_rep_1\n    repType: 0\n    zone: bj\n    servers:\n      - mysql_1\n      - mysql_2\n      - mysql_3\n    cur-write-server: mysql_1\n    auto-write-switch: true\n    heartbeatSQL : select user()\n"
  },
  {
    "path": "test-output/Default suite/Default test.html",
    "content": "<html>\n<head>\n<title>TestNG:  Default test</title>\n<link href=\"../testng.css\" rel=\"stylesheet\" type=\"text/css\" />\n<link href=\"../my-testng.css\" rel=\"stylesheet\" type=\"text/css\" />\n\n<style type=\"text/css\">\n.log { display: none;} \n.stack-trace { display: none;} \n</style>\n<script type=\"text/javascript\">\n<!--\nfunction flip(e) {\n  current = e.style.display;\n  if (current == 'block') {\n    e.style.display = 'none';\n    return 0;\n  }\n  else {\n    e.style.display = 'block';\n    return 1;\n  }\n}\n\nfunction toggleBox(szDivId, elem, msg1, msg2)\n{\n  var res = -1;  if (document.getElementById) {\n    res = flip(document.getElementById(szDivId));\n  }\n  else if (document.all) {\n    // this is the way old msie versions work\n    res = flip(document.all[szDivId]);\n  }\n  if(elem) {\n    if(res == 0) elem.innerHTML = msg1; else elem.innerHTML = msg2;\n  }\n\n}\n\nfunction toggleAllBoxes() {\n  if (document.getElementsByTagName) {\n    d = document.getElementsByTagName('div');\n    for (i = 0; i < d.length; i++) {\n      if (d[i].className == 'log') {\n        flip(d[i]);\n      }\n    }\n  }\n}\n\n// -->\n</script>\n\n</head>\n<body>\n<h2 align='center'>Default test</h2><table border='1' align=\"center\">\n<tr>\n<td>Tests passed/Failed/Skipped:</td><td>0/0/0</td>\n</tr><tr>\n<td>Started on:</td><td>Mon Mar 17 10:49:43 CST 2014</td>\n</tr>\n<tr><td>Total time:</td><td>0 seconds (12 ms)</td>\n</tr><tr>\n<td>Included groups:</td><td></td>\n</tr><tr>\n<td>Excluded groups:</td><td></td>\n</tr>\n</table><p/>\n<small><i>(Hover the method name to see the test class name)</i></small><p/>\n</body>\n</html>"
  },
  {
    "path": "test-output/Default suite/Default test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Generated by org.testng.reporters.JUnitXMLReporter -->\n<testsuite hostname=\"WUZHIH1\" tests=\"0\" failures=\"0\" timestamp=\"17 Mar 2014 02:49:43 GMT\" time=\"0.012\" errors=\"0\">\n</testsuite>\n"
  },
  {
    "path": "test-output/emailable-report.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>TestNG Report</title><style type=\"text/css\">table {margin-bottom:10px;border-collapse:collapse;empty-cells:show}th,td {border:1px solid #009;padding:.25em .5em}th {vertical-align:bottom}td {vertical-align:top}table a {font-weight:bold}.stripe td {background-color: #E6EBF9}.num {text-align:right}.passedodd td {background-color: #3F3}.passedeven td {background-color: #0A0}.skippedodd td {background-color: #DDD}.skippedeven td {background-color: #CCC}.failedodd td,.attn {background-color: #F33}.failedeven td,.stripe .attn {background-color: #D00}.stacktrace {white-space:pre;font-family:monospace}.totop {font-size:85%;text-align:center;border-bottom:2px solid #000}</style></head><body><table><tr><th>Test</th><th># Passed</th><th># Skipped</th><th># Failed</th><th>Time (ms)</th><th>Included Groups</th><th>Excluded Groups</th></tr><tr><th colspan=\"7\">Default suite</th></tr><tr><td><a href=\"#t0\">Default test</a></td><td class=\"num\">0</td><td class=\"num\">0</td><td class=\"num\">0</td><td class=\"num\">12</td><td></td><td></td></tr></table><table><thead><tr><th>Class</th><th>Method</th><th>Start</th><th>Time (ms)</th></tr></thead><tbody><tr><th colspan=\"4\">Default suite</th></tr></tbody><tbody id=\"t0\"></tbody></table><h2>Default test</h2></body></html>"
  },
  {
    "path": "test-output/index.html",
    "content": "<!DOCTYPE html>\n\n<html>\n  <head>\n  <title>TestNG reports</title>\n\n    <link type=\"text/css\" href=\"testng-reports.css\" rel=\"stylesheet\" />  \n    <script type=\"text/javascript\" src=\"jquery-1.7.1.min.js\"></script>\n    <script type=\"text/javascript\" src=\"testng-reports.js\"></script>\n    <script type=\"text/javascript\" src=\"https://www.google.com/jsapi\"></script>\n    <script type='text/javascript'>\n      google.load('visualization', '1', {packages:['table']});\n      google.setOnLoadCallback(drawTable);\n      var suiteTableInitFunctions = new Array();\n      var suiteTableData = new Array();\n    </script>\n    <!--\n      <script type=\"text/javascript\" src=\"jquery-ui/js/jquery-ui-1.8.16.custom.min.js\"></script>\n     -->\n  </head>\n\n  <body>\n    <div class=\"top-banner-root\">\n      <span class=\"top-banner-title-font\">Test results</span>\n      <br/>\n      <span class=\"top-banner-font-1\">1 suite</span>\n    </div> <!-- top-banner-root -->\n    <div class=\"navigator-root\">\n      <div class=\"navigator-suite-header\">\n        <span>All suites</span>\n        <a href=\"#\" class=\"collapse-all-link\" title=\"Collapse/expand all the suites\">\n          <img class=\"collapse-all-icon\" src=\"collapseall.gif\">\n          </img> <!-- collapse-all-icon -->\n        </a> <!-- collapse-all-link -->\n      </div> <!-- navigator-suite-header -->\n      <div class=\"suite\">\n        <div class=\"rounded-window\">\n          <div class=\"suite-header light-rounded-window-top\">\n            <a href=\"#\" class=\"navigator-link\" panel-name=\"suite-Default_suite\">\n              <span class=\"suite-name border-passed\">Default suite</span>\n            </a> <!-- navigator-link -->\n          </div> <!-- suite-header light-rounded-window-top -->\n          <div class=\"navigator-suite-content\">\n            <div class=\"suite-section-title\">\n              <span>Info</span>\n            </div> <!-- suite-section-title -->\n            <div class=\"suite-section-content\">\n              <ul>\n                <li>\n                  <a href=\"#\" class=\"navigator-link \" panel-name=\"test-xml-Default_suite\">\n                    <span>C:\\Users\\wuzhih\\AppData\\Local\\Temp\\testng-eclipse--90647589\\testng-customsuite.xml</span>\n                  </a> <!-- navigator-link  -->\n                </li>\n                <li>\n                  <a href=\"#\" class=\"navigator-link \" panel-name=\"testlist-Default_suite\">\n                    <span class=\"test-stats\">1 test</span>\n                  </a> <!-- navigator-link  -->\n                </li>\n                <li>\n                  <a href=\"#\" class=\"navigator-link \" panel-name=\"group-Default_suite\">\n                    <span>0 groups</span>\n                  </a> <!-- navigator-link  -->\n                </li>\n                <li>\n                  <a href=\"#\" class=\"navigator-link \" panel-name=\"times-Default_suite\">\n                    <span>Times</span>\n                  </a> <!-- navigator-link  -->\n                </li>\n                <li>\n                  <a href=\"#\" class=\"navigator-link \" panel-name=\"reporter-Default_suite\">\n                    <span>Reporter output</span>\n                  </a> <!-- navigator-link  -->\n                </li>\n                <li>\n                  <a href=\"#\" class=\"navigator-link \" panel-name=\"ignored-methods-Default_suite\">\n                    <span>Ignored methods</span>\n                  </a> <!-- navigator-link  -->\n                </li>\n                <li>\n                  <a href=\"#\" class=\"navigator-link \" panel-name=\"chronological-Default_suite\">\n                    <span>Chronological view</span>\n                  </a> <!-- navigator-link  -->\n                </li>\n              </ul>\n            </div> <!-- suite-section-content -->\n            <div class=\"result-section\">\n              <div class=\"suite-section-title\">\n                <span>Results</span>\n              </div> <!-- suite-section-title -->\n              <div class=\"suite-section-content\">\n                <ul>\n                  <li>\n                    <span class=\"method-stats\">0 methods,   </span>\n                  </li>\n                </ul>\n              </div> <!-- suite-section-content -->\n            </div> <!-- result-section -->\n          </div> <!-- navigator-suite-content -->\n        </div> <!-- rounded-window -->\n      </div> <!-- suite -->\n    </div> <!-- navigator-root -->\n    <div class=\"wrapper\">\n      <div class=\"main-panel-root\">\n        <div panel-name=\"suite-Default_suite\" class=\"panel Default_suite\">\n        </div> <!-- panel Default_suite -->\n        <div panel-name=\"test-xml-Default_suite\" class=\"panel\">\n          <div class=\"main-panel-header rounded-window-top\">\n            <span class=\"header-content\">C:\\Users\\wuzhih\\AppData\\Local\\Temp\\testng-eclipse--90647589\\testng-customsuite.xml</span>\n          </div> <!-- main-panel-header rounded-window-top -->\n          <div class=\"main-panel-content rounded-window-bottom\">\n            <pre>\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\n&lt;!DOCTYPE suite SYSTEM &quot;http://testng.org/testng-1.0.dtd&quot;&gt;\n&lt;suite name=&quot;Default suite&quot;&gt;\n  &lt;test verbose=&quot;2&quot; name=&quot;Default test&quot;&gt;\n    &lt;classes&gt;\n      &lt;class name=&quot;io.mycat.route.function.PartitionByStringTest&quot;/&gt;\n    &lt;/classes&gt;\n  &lt;/test&gt; &lt;!-- Default test --&gt;\n&lt;/suite&gt; &lt;!-- Default suite --&gt;\n            </pre>\n          </div> <!-- main-panel-content rounded-window-bottom -->\n        </div> <!-- panel -->\n        <div panel-name=\"testlist-Default_suite\" class=\"panel\">\n          <div class=\"main-panel-header rounded-window-top\">\n            <span class=\"header-content\">Tests for Default suite</span>\n          </div> <!-- main-panel-header rounded-window-top -->\n          <div class=\"main-panel-content rounded-window-bottom\">\n            <ul>\n              <li>\n                <span class=\"test-name\">Default test (1 class)</span>\n              </li>\n            </ul>\n          </div> <!-- main-panel-content rounded-window-bottom -->\n        </div> <!-- panel -->\n        <div panel-name=\"group-Default_suite\" class=\"panel\">\n          <div class=\"main-panel-header rounded-window-top\">\n            <span class=\"header-content\">Groups for Default suite</span>\n          </div> <!-- main-panel-header rounded-window-top -->\n          <div class=\"main-panel-content rounded-window-bottom\">\n          </div> <!-- main-panel-content rounded-window-bottom -->\n        </div> <!-- panel -->\n        <div panel-name=\"times-Default_suite\" class=\"panel\">\n          <div class=\"main-panel-header rounded-window-top\">\n            <span class=\"header-content\">Times for Default suite</span>\n          </div> <!-- main-panel-header rounded-window-top -->\n          <div class=\"main-panel-content rounded-window-bottom\">\n            <div class=\"times-div\">\n              <script type=\"text/javascript\">\nsuiteTableInitFunctions.push('tableData_Default_suite');\nfunction tableData_Default_suite() {\nvar data = new google.visualization.DataTable();\ndata.addColumn('number', 'Number');\ndata.addColumn('string', 'Method');\ndata.addColumn('string', 'Class');\ndata.addColumn('number', 'Time (ms)');\ndata.addRows(0);\nwindow.suiteTableData['Default_suite']= { tableData: data, tableDiv: 'times-div-Default_suite'}\nreturn data;\n}\n              </script>\n              <div id=\"times-div-Default_suite\">\n              </div> <!-- times-div-Default_suite -->\n            </div> <!-- times-div -->\n          </div> <!-- main-panel-content rounded-window-bottom -->\n        </div> <!-- panel -->\n        <div panel-name=\"reporter-Default_suite\" class=\"panel\">\n          <div class=\"main-panel-header rounded-window-top\">\n            <span class=\"header-content\">Reporter output for Default suite</span>\n          </div> <!-- main-panel-header rounded-window-top -->\n          <div class=\"main-panel-content rounded-window-bottom\">\n          </div> <!-- main-panel-content rounded-window-bottom -->\n        </div> <!-- panel -->\n        <div panel-name=\"ignored-methods-Default_suite\" class=\"panel\">\n          <div class=\"main-panel-header rounded-window-top\">\n            <span class=\"header-content\">0 ignored methods</span>\n          </div> <!-- main-panel-header rounded-window-top -->\n          <div class=\"main-panel-content rounded-window-bottom\">\n          </div> <!-- main-panel-content rounded-window-bottom -->\n        </div> <!-- panel -->\n        <div panel-name=\"chronological-Default_suite\" class=\"panel\">\n          <div class=\"main-panel-header rounded-window-top\">\n            <span class=\"header-content\">Methods in chronological order</span>\n          </div> <!-- main-panel-header rounded-window-top -->\n          <div class=\"main-panel-content rounded-window-bottom\">\n          </div> <!-- main-panel-content rounded-window-bottom -->\n        </div> <!-- panel -->\n      </div> <!-- main-panel-root -->\n    </div> <!-- wrapper -->\n  </body>\n</html>\n"
  },
  {
    "path": "test-output/old/Default suite/Default test.properties",
    "content": "[SuiteResult context=Default test]"
  },
  {
    "path": "test-output/old/Default suite/classes.html",
    "content": "<table border='1'>\n<tr>\n<th>Class name</th>\n<th>Method name</th>\n<th>Groups</th>\n</tr></table>\n"
  },
  {
    "path": "test-output/old/Default suite/groups.html",
    "content": "<h2>Groups used for this test run</h2>"
  },
  {
    "path": "test-output/old/Default suite/index.html",
    "content": "<html><head><title>Results for Default suite</title></head>\n<frameset cols=\"26%,74%\">\n<frame src=\"toc.html\" name=\"navFrame\">\n<frame src=\"main.html\" name=\"mainFrame\">\n</frameset>\n</html>\n"
  },
  {
    "path": "test-output/old/Default suite/main.html",
    "content": "<html><head><title>Results for Default suite</title></head>\n<body>Select a result on the left-hand pane.</body></html>\n"
  },
  {
    "path": "test-output/old/Default suite/methods-alphabetical.html",
    "content": "<h2>Methods run, sorted chronologically</h2><h3>&gt;&gt; means before, &lt;&lt; means after</h3><p/><br/><em>Default suite</em><p/><small><i>(Hover the method name to see the test class name)</i></small><p/>\n</table>\n"
  },
  {
    "path": "test-output/old/Default suite/methods-not-run.html",
    "content": "<h2>Methods that were not run</h2><table>\n</table>"
  },
  {
    "path": "test-output/old/Default suite/methods.html",
    "content": "<h2>Methods run, sorted chronologically</h2><h3>&gt;&gt; means before, &lt;&lt; means after</h3><p/><br/><em>Default suite</em><p/><small><i>(Hover the method name to see the test class name)</i></small><p/>\n</table>\n"
  },
  {
    "path": "test-output/old/Default suite/reporter-output.html",
    "content": "<h2>Reporter output</h2><table></table>"
  },
  {
    "path": "test-output/old/Default suite/testng.xml.html",
    "content": "<html><head><title>testng.xml for Default suite</title></head><body><tt>&lt;?xml&nbsp;version=\"1.0\"&nbsp;encoding=\"UTF-8\"?&gt;\r<br/>&lt;!DOCTYPE&nbsp;suite&nbsp;SYSTEM&nbsp;\"http://testng.org/testng-1.0.dtd\"&gt;\r<br/>&lt;suite&nbsp;name=\"Default&nbsp;suite\"&gt;\r<br/>&nbsp;&nbsp;&lt;test&nbsp;verbose=\"2\"&nbsp;name=\"Default&nbsp;test\"&gt;\r<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;classes&gt;\r<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;class&nbsp;name=\"io.mycat.route.function.PartitionByStringTest\"/&gt;\r<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/classes&gt;\r<br/>&nbsp;&nbsp;&lt;/test&gt;&nbsp;&lt;!--&nbsp;Default&nbsp;test&nbsp;--&gt;\r<br/>&lt;/suite&gt;&nbsp;&lt;!--&nbsp;Default&nbsp;suite&nbsp;--&gt;\r<br/></tt></body></html>"
  },
  {
    "path": "test-output/old/Default suite/toc.html",
    "content": "<html>\n<head>\n<title>Results for Default suite</title>\n<link href=\"../testng.css\" rel=\"stylesheet\" type=\"text/css\" />\n<link href=\"../my-testng.css\" rel=\"stylesheet\" type=\"text/css\" />\n</head>\n<body>\n<h3><p align=\"center\">Results for<br/><em>Default suite</em></p></h3>\n<table border='1' width='100%'>\n<tr valign='top'>\n<td>1 test</td>\n<td><a target='mainFrame' href='classes.html'>0 class</a></td>\n<td>0 method:<br/>\n&nbsp;&nbsp;<a target='mainFrame' href='methods.html'>chronological</a><br/>\n&nbsp;&nbsp;<a target='mainFrame' href='methods-alphabetical.html'>alphabetical</a><br/>\n&nbsp;&nbsp;<a target='mainFrame' href='methods-not-run.html'>not run (0)</a></td>\n</tr>\n<tr>\n<td><a target='mainFrame' href='groups.html'>0 group</a></td>\n<td><a target='mainFrame' href='reporter-output.html'>reporter output</a></td>\n<td><a target='mainFrame' href='testng.xml.html'>testng.xml</a></td>\n</tr></table>\n<table width='100%' class='test-failed'>\n<tr><td>\n<table style='width: 100%'><tr><td valign='top'>Default test (0/0/0)</td><td valign='top' align='right'>\n  <a href='Default test.html' target='mainFrame'>Results</a>\n</td></tr></table>\n</td></tr><p/>\n</table>\n</body></html>"
  },
  {
    "path": "test-output/old/index.html",
    "content": "<html>\n<head><title>Test results</title><link href=\"./testng.css\" rel=\"stylesheet\" type=\"text/css\" />\n<link href=\"./my-testng.css\" rel=\"stylesheet\" type=\"text/css\" />\n</head><body>\n<h2><p align='center'>Test results</p></h2>\n<table border='1' width='100%' class='main-page'><tr><th>Suite</th><th>Passed</th><th>Failed</th><th>Skipped</th><th>testng.xml</th></tr>\n<tr align='center' class='invocation-failed'><td><em>Total</em></td><td><em>0</em></td><td><em>0</em></td><td><em>0</em></td><td>&nbsp;</td></tr>\n<tr align='center' class='invocation-failed'><td><a href='Default suite/index.html'>Default suite</a></td>\n<td>0</td><td>0</td><td>0</td><td><a href='Default suite/testng.xml.html'>Link</a></td></tr></table></body></html>\n"
  },
  {
    "path": "test-output/testng-reports.css",
    "content": "body {\n    margin: 0px 0px 5px 5px;\n}\n\nul {\n    margin: 0px;\n}\n\nli {\n    list-style-type: none;\n}\n\na {\n    text-decoration: none;\n}\n\na:hover {\n    text-decoration: underline;\n}\n\n.navigator-selected {\n    background: #ffa500;\n}\n\n.wrapper {\n    position: absolute;\n    top: 60px;\n    bottom: 0;\n    left: 400px;\n    right: 0;\n    overflow: auto;\n}\n\n.navigator-root {\n    position: absolute;\n    top: 60px;\n    bottom: 0;\n    left: 0;\n    width: 400px;\n    overflow-y: auto;\n}\n\n.suite {\n    margin: 0px 10px 10px 0px;\n    background-color: #fff8dc;\n}\n\n.suite-name {\n    padding-left: 10px;\n    font-size: 25px;\n    font-family: Times;\n}\n\n.main-panel-header {\n    padding: 5px;\n    background-color: #9FB4D9; //afeeee;\n    font-family: monospace;\n    font-size: 18px;\n}\n\n.main-panel-content {\n    padding: 5px;\n    margin-bottom: 10px;\n    background-color: #DEE8FC; //d0ffff;\n}\n\n.rounded-window {\n    border-radius: 10px;\n    border-style: solid;\n    border-width: 1px;\n}\n\n.rounded-window-top {\n    border-top-right-radius: 10px 10px;\n    border-top-left-radius: 10px 10px;\n    border-style: solid;\n    border-width: 1px;\n    overflow: auto;\n}\n\n.light-rounded-window-top {\n    border-top-right-radius: 10px 10px;\n    border-top-left-radius: 10px 10px;\n}\n\n.rounded-window-bottom {\n    border-style: solid;\n    border-width: 0px 1px 1px 1px;\n    border-bottom-right-radius: 10px 10px;\n    border-bottom-left-radius: 10px 10px;\n    overflow: auto;\n}\n\n.method-name {\n    font-size: 12px;\n    font-family: monospace;\n}\n\n.method-content {\n    border-style: solid;\n    border-width: 0px 0px 1px 0px;\n    margin-bottom: 10;\n    padding-bottom: 5px;\n    width: 80%;\n}\n\n.parameters {\n    font-size: 14px;\n    font-family: monospace;\n}\n\n.stack-trace {\n    white-space: pre;\n    font-family: monospace;\n    font-size: 12px;\n    font-weight: bold;\n    margin-top: 0px;\n    margin-left: 20px;\n}\n\n.testng-xml {\n    font-family: monospace;\n}\n\n.method-list-content {\n    margin-left: 10px;\n}\n\n.navigator-suite-content {\n    margin-left: 10px;\n    font: 12px 'Lucida Grande';\n}\n\n.suite-section-title {\n    margin-top: 10px;\n    width: 80%;\n    border-style: solid;\n    border-width: 1px 0px 0px 0px;\n    font-family: Times;\n    font-size: 18px;\n    font-weight: bold;\n}\n\n.suite-section-content {\n    list-style-image: url(bullet_point.png);\n}\n\n.top-banner-root {\n    position: absolute;\n    top: 0;\n    height: 45px;\n    left: 0;\n    right: 0;\n    padding: 5px;\n    margin: 0px 0px 5px 0px;\n    background-color: #0066ff;\n    font-family: Times;\n    color: #fff;\n    text-align: center;\n}\n\n.top-banner-title-font {\n    font-size: 25px;\n}\n\n.test-name {\n    font-family: 'Lucida Grande';\n    font-size: 16px;\n}\n\n.suite-icon {\n    padding: 5px;\n    float: right;\n    height: 20;\n}\n\n.test-group {\n    font: 20px 'Lucida Grande';\n    margin: 5px 5px 10px 5px;\n    border-width: 0px 0px 1px 0px;\n    border-style: solid;\n    padding: 5px;\n}\n\n.test-group-name {\n    font-weight: bold;\n}\n\n.method-in-group {\n    font-size: 16px;\n    margin-left: 80px;\n}\n\ntable.google-visualization-table-table {\n    width: 100%;\n}\n\n.reporter-method-name {\n    font-size: 14px;\n    font-family: monospace;\n}\n\n.reporter-method-output-div {\n    padding: 5px;\n    margin: 0px 0px 5px 20px;\n    font-size: 12px;\n    font-family: monospace;\n    border-width: 0px 0px 0px 1px;\n    border-style: solid;\n}\n\n.ignored-class-div {\n    font-size: 14px;\n    font-family: monospace;\n}\n\n.ignored-methods-div {\n    padding: 5px;\n    margin: 0px 0px 5px 20px;\n    font-size: 12px;\n    font-family: monospace;\n    border-width: 0px 0px 0px 1px;\n    border-style: solid;\n}\n\n.border-failed {\n    border-top-left-radius: 10px 10px;\n    border-bottom-left-radius: 10px 10px;\n    border-style: solid;\n    border-width: 0px 0px 0px 10px;\n    border-color: #f00;\n}\n\n.border-skipped {\n    border-top-left-radius: 10px 10px;\n    border-bottom-left-radius: 10px 10px;\n    border-style: solid;\n    border-width: 0px 0px 0px 10px;\n    border-color: #edc600;\n}\n\n.border-passed {\n    border-top-left-radius: 10px 10px;\n    border-bottom-left-radius: 10px 10px;\n    border-style: solid;\n    border-width: 0px 0px 0px 10px;\n    border-color: #19f52d;\n}\n\n.times-div {\n    text-align: center;\n    padding: 5px;\n}\n\n.suite-total-time {\n    font: 16px 'Lucida Grande';\n}\n\n.configuration-suite {\n    margin-left: 20px;\n}\n\n.configuration-test {\n    margin-left: 40px;\n}\n\n.configuration-class {\n    margin-left: 60px;\n}\n\n.configuration-method {\n    margin-left: 80px;\n}\n\n.test-method {\n    margin-left: 100px;\n}\n\n.chronological-class {\n    background-color: #0ccff;\n    border-style: solid;\n    border-width: 0px 0px 1px 1px;\n}\n\n.method-start {\n    float: right;\n}\n\n.chronological-class-name {\n    padding: 0px 0px 0px 5px;\n    color: #008;\n}\n\n.after, .before, .test-method {\n    font-family: monospace;\n    font-size: 14px;\n}\n\n.navigator-suite-header {\n    font-size: 22px;\n    margin: 0px 10px 5px 0px;\n    background-color: #deb887;\n    text-align: center;\n}\n\n.collapse-all-icon {\n    padding: 5px;\n    float: right;\n}\n"
  },
  {
    "path": "test-output/testng-reports.js",
    "content": "$(document).ready(function() {\n    $('a.navigator-link').click(function() {\n        // Extract the panel for this link\n        var panel = getPanelName($(this));\n\n        // Mark this link as currently selected\n        $('.navigator-link').parent().removeClass('navigator-selected');\n        $(this).parent().addClass('navigator-selected');\n\n        showPanel(panel);\n    });\n\n    installMethodHandlers('failed');\n    installMethodHandlers('skipped');\n    installMethodHandlers('passed', true); // hide passed methods by default\n\n    $('a.method').click(function() {\n        showMethod($(this));\n        return false;\n    });\n\n    // Hide all the panels and display the first one (do this last\n    // to make sure the click() will invoke the listeners)\n    $('.panel').hide();\n    $('.navigator-link').first().click();\n\n    // Collapse/expand the suites\n    $('a.collapse-all-link').click(function() {\n        var contents = $('.navigator-suite-content');\n        if (contents.css('display') == 'none') {\n            contents.show();\n        } else {\n            contents.hide();\n        }\n    });\n});\n\n// The handlers that take care of showing/hiding the methods\nfunction installMethodHandlers(name, hide) {\n    function getContent(t) {\n    return $('.method-list-content.' + name + \".\" + t.attr('panel-name'));\n    }\n\n    function getHideLink(t, name) {\n        var s = 'a.hide-methods.' + name + \".\" + t.attr('panel-name');\n        return $(s);\n    }\n\n    function getShowLink(t, name) {\n        return $('a.show-methods.' + name + \".\" + t.attr('panel-name'));\n    }\n\n    function getMethodPanelClassSel(element, name) {\n        var panelName = getPanelName(element);\n    var sel = '.' + panelName + \"-class-\" + name;\n        return $(sel);\n    }\n\n    $('a.hide-methods.' + name).click(function() {\n        var w = getContent($(this));\n        w.hide();\n        getHideLink($(this), name).hide();\n        getShowLink($(this), name).show();\n    getMethodPanelClassSel($(this), name).hide();\n    });\n\n    $('a.show-methods.' + name).click(function() {\n        var w = getContent($(this));\n        w.show();\n        getHideLink($(this), name).show();\n        getShowLink($(this), name).hide();\n    showPanel(getPanelName($(this)));\n    getMethodPanelClassSel($(this), name).show();\n    });\n\n    if (hide) {\n        $('a.hide-methods.' + name).click();\n    } else {\n        $('a.show-methods.' + name).click();\n    }\n}\n\nfunction getHashForMethod(element) {\n    return element.attr('hash-for-method');\n}\n\nfunction getPanelName(element) {\n    return element.attr('panel-name');\n}\n\nfunction showPanel(panelName) {\n    $('.panel').hide();\n    var panel = $('.panel[panel-name=\"' + panelName + '\"]');\n    panel.show();\n}\n\nfunction showMethod(element) {\n    var hashTag = getHashForMethod(element);\n    var panelName = getPanelName(element);\n    showPanel(panelName);\n    var current = document.location.href;\n    var base = current.substring(0, current.indexOf('#'))\n    document.location.href = base + '#' + hashTag;\n    var newPosition = $(document).scrollTop() - 65;\n    $(document).scrollTop(newPosition);\n}\n\nfunction drawTable() {\n    for (var i = 0; i < suiteTableInitFunctions.length; i++) {\n        window[suiteTableInitFunctions[i]]();\n    }\n\n    for (var k in window.suiteTableData) {\n        var v = window.suiteTableData[k];\n        var div = v.tableDiv;\n        var data = v.tableData\n        var table = new google.visualization.Table(document.getElementById(div));\n        table.draw(data, {\n            showRowNumber : false\n        });\n    }\n}\n"
  },
  {
    "path": "test-output/testng-results.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<testng-results skipped=\"0\" failed=\"0\" total=\"0\" passed=\"0\">\n  <reporter-output>\n  </reporter-output>\n  <suite name=\"Default suite\" duration-ms=\"12\" started-at=\"2014-03-17T02:49:43Z\" finished-at=\"2014-03-17T02:49:43Z\">\n    <groups>\n    </groups>\n    <test name=\"Default test\" duration-ms=\"12\" started-at=\"2014-03-17T02:49:43Z\" finished-at=\"2014-03-17T02:49:43Z\">\n    </test> <!-- Default test -->\n  </suite> <!-- Default suite -->\n</testng-results>\n"
  },
  {
    "path": "test-output/testng.css",
    "content": ".invocation-failed,  .test-failed  { background-color: #DD0000; }\n.invocation-percent, .test-percent { background-color: #006600; }\n.invocation-passed,  .test-passed  { background-color: #00AA00; }\n.invocation-skipped, .test-skipped { background-color: #CCCC00; }\n\n.main-page {\n  font-size: x-large;\n}\n\n"
  },
  {
    "path": "tmlogs/tmlog-1.log",
    "content": ""
  },
  {
    "path": "version.txt",
    "content": "BuildTime  2020-11-04 09:46:07\nGitVersion   277758d6dd4193529ebab066571512f8e8383d1d\nMavenVersion 1.6.7.6-release\nGitUrl https://github.com/MyCATApache/Mycat-Server.git\nMyCatSite http://www.mycat.org.cn\nQQGroup 106088787\n"
  },
  {
    "path": "version.txt.template",
    "content": "BuildTime  @buildtime@\nGitVersion   @buildnumber@\nMavenVersion @pomversion@\nGitUrl @giturl@\nMyCatSite @mycatsite@\nQQGroup @qqgroup@\n"
  }
]